diff --git a/automation/tests/aegisub.cpp b/automation/tests/aegisub.cpp
index 61330b4a10280858a8778c11f84e4ab51d8c8f2f..d348f36a50a7dc8607f01b3a22677a2962f273d5 100644
--- a/automation/tests/aegisub.cpp
+++ b/automation/tests/aegisub.cpp
@@ -28,69 +28,73 @@
 using namespace agi::lua;
 
 namespace {
-void check(lua_State *L, int status) {
-	if (status && !lua_isnil(L, -1)) {
-		fprintf(stderr, "%s\n", get_string_or_default(L, -1).c_str());
-		exit(status);
-	}
+void check(lua_State *L, int status)
+{
+    if (status && !lua_isnil(L, -1)) {
+        fprintf(stderr, "%s\n", get_string_or_default(L, -1).c_str());
+        exit(status);
+    }
 }
 
-int close_and_exit(lua_State *L) {
-	int status = lua_tointeger(L, 1);
-	lua_close(L);
-	exit(status);
+int close_and_exit(lua_State *L)
+{
+    int status = lua_tointeger(L, 1);
+    lua_close(L);
+    exit(status);
 }
 }
 
-int main(int argc, char **argv) {
-	if (argc < 2) {
-		fprintf(stderr, "usage: aegisub-lua <script> [args]\n");
-		return 1;
-	}
-
-	std::locale::global(boost::locale::generator().generate(""));
-	agi::dispatch::Init([](agi::dispatch::Thunk f) { });
-	agi::log::log = new agi::log::LogSink;
-
-	// Init lua state
-	lua_State *L = lua_open();
-	if (!L) {
-		fprintf(stderr, "Failed to create Lua state\n");
-		return 1;
-	}
-
-	preload_modules(L);
-	Install(L, {"include"});
-
-	// Patch os.exit to close the lua state first since busted calls it when
-	// it's done
-	lua_getglobal(L, "os");
-	lua_pushcfunction(L, close_and_exit);
-	lua_setfield(L, -2, "exit");
-	lua_pop(L, 1);
-
-	// Build arg table for scripts
-	lua_createtable(L, argc - 1, 0);
-	for (int i = 1; i < argc; ++i) {
-		lua_pushstring(L, argv[i]);
-		lua_rawseti(L, -2, i - 1);
-	}
-	lua_setglobal(L, "arg");
-
-	// Stack needs to be error handler -> function -> args
-	lua_pushcfunction(L, add_stack_trace);
-
-	try {
-		check(L, !LoadFile(L, argv[1]));
-	} catch (agi::Exception const& e) {
-		fprintf(stderr, "%s\n", e.GetMessage().c_str());
-	}
-
-	for (int i = 2; i < argc; ++i)
-		lua_pushstring(L, argv[i]);
-
-	int base = lua_gettop(L) - argc + 1;
-	check(L, lua_pcall(L, argc - 2, LUA_MULTRET, base));
-	lua_close(L);
+int main(int argc, char **argv)
+{
+    if (argc < 2) {
+        fprintf(stderr, "usage: aegisub-lua <script> [args]\n");
+        return 1;
+    }
+
+    std::locale::global(boost::locale::generator().generate(""));
+    agi::dispatch::Init([](agi::dispatch::Thunk f) { });
+    agi::log::log = new agi::log::LogSink;
+
+    // Init lua state
+    lua_State *L = lua_open();
+    if (!L) {
+        fprintf(stderr, "Failed to create Lua state\n");
+        return 1;
+    }
+
+    preload_modules(L);
+    Install(L, {"include"});
+
+    // Patch os.exit to close the lua state first since busted calls it when
+    // it's done
+    lua_getglobal(L, "os");
+    lua_pushcfunction(L, close_and_exit);
+    lua_setfield(L, -2, "exit");
+    lua_pop(L, 1);
+
+    // Build arg table for scripts
+    lua_createtable(L, argc - 1, 0);
+    for (int i = 1; i < argc; ++i) {
+        lua_pushstring(L, argv[i]);
+        lua_rawseti(L, -2, i - 1);
+    }
+    lua_setglobal(L, "arg");
+
+    // Stack needs to be error handler -> function -> args
+    lua_pushcfunction(L, add_stack_trace);
+
+    try {
+        check(L, !LoadFile(L, argv[1]));
+    }
+    catch (agi::Exception const &e) {
+        fprintf(stderr, "%s\n", e.GetMessage().c_str());
+    }
+
+    for (int i = 2; i < argc; ++i)
+        lua_pushstring(L, argv[i]);
+
+    int base = lua_gettop(L) - argc + 1;
+    check(L, lua_pcall(L, argc - 2, LUA_MULTRET, base));
+    lua_close(L);
 }
 
diff --git a/libaegisub/ass/dialogue_parser.cpp b/libaegisub/ass/dialogue_parser.cpp
index ea486dcf71cdd75d6b3810172746b31e130676fc..e543cde6c6e0d5f7f259690e968b1e7f196c5ac6 100644
--- a/libaegisub/ass/dialogue_parser.cpp
+++ b/libaegisub/ass/dialogue_parser.cpp
@@ -30,188 +30,191 @@ namespace dt = DialogueTokenType;
 namespace ss = SyntaxStyle;
 
 class SyntaxHighlighter {
-	TokenVec ranges;
-	std::string const& text;
-	agi::SpellChecker *spellchecker;
+    TokenVec ranges;
+    std::string const &text;
+    agi::SpellChecker *spellchecker;
 
-	void SetStyling(size_t len, int type) {
-		if (ranges.size() && ranges.back().type == type)
-			ranges.back().length += len;
-		else
-			ranges.push_back(DialogueToken{type, len});
-	}
+    void SetStyling(size_t len, int type) {
+        if (ranges.size() && ranges.back().type == type)
+            ranges.back().length += len;
+        else
+            ranges.push_back(DialogueToken{type, len});
+    }
 
 public:
-	SyntaxHighlighter(std::string const& text, agi::SpellChecker *spellchecker)
-	: text(text)
-	, spellchecker(spellchecker)
-	{ }
-
-	TokenVec Highlight(TokenVec const& tokens) {
-		if (tokens.empty()) return ranges;
-
-		size_t pos = 0;
-
-		for (auto tok : tokens) {
-			switch (tok.type) {
-				case dt::KARAOKE_TEMPLATE: SetStyling(tok.length, ss::KARAOKE_TEMPLATE); break;
-				case dt::KARAOKE_VARIABLE: SetStyling(tok.length, ss::KARAOKE_VARIABLE); break;
-				case dt::LINE_BREAK: SetStyling(tok.length, ss::LINE_BREAK); break;
-				case dt::ERROR:      SetStyling(tok.length, ss::ERROR);      break;
-				case dt::ARG:        SetStyling(tok.length, ss::PARAMETER);  break;
-				case dt::COMMENT:    SetStyling(tok.length, ss::COMMENT);    break;
-				case dt::DRAWING:    SetStyling(tok.length, ss::DRAWING);    break;
-				case dt::TEXT:       SetStyling(tok.length, ss::NORMAL);     break;
-				case dt::TAG_NAME:   SetStyling(tok.length, ss::TAG);        break;
-				case dt::OPEN_PAREN: case dt::CLOSE_PAREN: case dt::ARG_SEP: case dt::TAG_START:
-					SetStyling(tok.length, ss::PUNCTUATION);
-					break;
-				case dt::OVR_BEGIN: case dt::OVR_END:
-					SetStyling(tok.length, ss::OVERRIDE);
-					break;
-				case dt::WHITESPACE:
-					if (ranges.size() && ranges.back().type == ss::PARAMETER)
-						SetStyling(tok.length, ss::PARAMETER);
-					else
-						SetStyling(tok.length, ss::NORMAL);
-					break;
-				case dt::WORD:
-					if (spellchecker && !spellchecker->CheckWord(text.substr(pos, tok.length)))
-						SetStyling(tok.length, ss::SPELLING);
-					else
-						SetStyling(tok.length, ss::NORMAL);
-					break;
-			}
-
-			pos += tok.length;
-		}
-
-		return ranges;
-	}
+    SyntaxHighlighter(std::string const &text, agi::SpellChecker *spellchecker)
+        : text(text)
+        , spellchecker(spellchecker)
+    { }
+
+    TokenVec Highlight(TokenVec const &tokens) {
+        if (tokens.empty()) return ranges;
+
+        size_t pos = 0;
+
+        for (auto tok : tokens) {
+            switch (tok.type) {
+            case dt::KARAOKE_TEMPLATE: SetStyling(tok.length, ss::KARAOKE_TEMPLATE); break;
+            case dt::KARAOKE_VARIABLE: SetStyling(tok.length, ss::KARAOKE_VARIABLE); break;
+            case dt::LINE_BREAK: SetStyling(tok.length, ss::LINE_BREAK); break;
+            case dt::ERROR:      SetStyling(tok.length, ss::ERROR);      break;
+            case dt::ARG:        SetStyling(tok.length, ss::PARAMETER);  break;
+            case dt::COMMENT:    SetStyling(tok.length, ss::COMMENT);    break;
+            case dt::DRAWING:    SetStyling(tok.length, ss::DRAWING);    break;
+            case dt::TEXT:       SetStyling(tok.length, ss::NORMAL);     break;
+            case dt::TAG_NAME:   SetStyling(tok.length, ss::TAG);        break;
+            case dt::OPEN_PAREN: case dt::CLOSE_PAREN: case dt::ARG_SEP: case dt::TAG_START:
+                SetStyling(tok.length, ss::PUNCTUATION);
+                break;
+            case dt::OVR_BEGIN: case dt::OVR_END:
+                SetStyling(tok.length, ss::OVERRIDE);
+                break;
+            case dt::WHITESPACE:
+                if (ranges.size() && ranges.back().type == ss::PARAMETER)
+                    SetStyling(tok.length, ss::PARAMETER);
+                else
+                    SetStyling(tok.length, ss::NORMAL);
+                break;
+            case dt::WORD:
+                if (spellchecker && !spellchecker->CheckWord(text.substr(pos, tok.length)))
+                    SetStyling(tok.length, ss::SPELLING);
+                else
+                    SetStyling(tok.length, ss::NORMAL);
+                break;
+            }
+
+            pos += tok.length;
+        }
+
+        return ranges;
+    }
 };
 
 class WordSplitter {
-	std::string const& text;
-	std::vector<DialogueToken> &tokens;
-	size_t pos = 0;
-
-	void SwitchTo(size_t &i, int type, size_t len) {
-		auto old = tokens[i];
-		tokens[i].type = type;
-		tokens[i].length = len;
-
-		if (old.length != (size_t)len) {
-			tokens.insert(tokens.begin() + i + 1, DialogueToken{old.type, old.length - len});
-			++i;
-		}
-	}
-
-	void SplitText(size_t &i) {
-		using namespace boost::locale::boundary;
-		ssegment_index map(word, text.begin() + pos, text.begin() + pos + tokens[i].length);
-		for (auto const& segment : map) {
-			auto len = static_cast<size_t>(distance(begin(segment), end(segment)));
-			if (segment.rule() & word_letters)
-				SwitchTo(i, dt::WORD, len);
-			else
-				SwitchTo(i, dt::TEXT, len);
-		}
-	}
+    std::string const &text;
+    std::vector<DialogueToken> &tokens;
+    size_t pos = 0;
+
+    void SwitchTo(size_t &i, int type, size_t len) {
+        auto old = tokens[i];
+        tokens[i].type = type;
+        tokens[i].length = len;
+
+        if (old.length != (size_t)len) {
+            tokens.insert(tokens.begin() + i + 1, DialogueToken{old.type, old.length - len});
+            ++i;
+        }
+    }
+
+    void SplitText(size_t &i) {
+        using namespace boost::locale::boundary;
+        ssegment_index map(word, text.begin() + pos, text.begin() + pos + tokens[i].length);
+        for (auto const &segment : map) {
+            auto len = static_cast<size_t>(distance(begin(segment), end(segment)));
+            if (segment.rule() & word_letters)
+                SwitchTo(i, dt::WORD, len);
+            else
+                SwitchTo(i, dt::TEXT, len);
+        }
+    }
 
 public:
-	WordSplitter(std::string const& text, std::vector<DialogueToken> &tokens)
-	: text(text)
-	, tokens(tokens)
-	{ }
-
-	void SplitWords() {
-		if (tokens.empty()) return;
-
-		for (size_t i = 0; i < tokens.size(); ++i) {
-			size_t len = tokens[i].length;
-			if (tokens[i].type == dt::TEXT)
-				SplitText(i);
-			pos += len;
-		}
-	}
+    WordSplitter(std::string const &text, std::vector<DialogueToken> &tokens)
+        : text(text)
+        , tokens(tokens)
+    { }
+
+    void SplitWords() {
+        if (tokens.empty()) return;
+
+        for (size_t i = 0; i < tokens.size(); ++i) {
+            size_t len = tokens[i].length;
+            if (tokens[i].type == dt::TEXT)
+                SplitText(i);
+            pos += len;
+        }
+    }
 };
 }
 
 namespace agi {
 namespace ass {
 
-std::vector<DialogueToken> SyntaxHighlight(std::string const& text, std::vector<DialogueToken> const& tokens, SpellChecker *spellchecker) {
-	return SyntaxHighlighter(text, spellchecker).Highlight(tokens);
+std::vector<DialogueToken> SyntaxHighlight(std::string const &text, std::vector<DialogueToken> const &tokens, SpellChecker *spellchecker)
+{
+    return SyntaxHighlighter(text, spellchecker).Highlight(tokens);
 }
 
-void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens) {
-	if (tokens.empty()) return;
-
-	size_t last_ovr_end = 0;
-	for (size_t i = tokens.size(); i > 0; --i) {
-		if (tokens[i - 1].type == dt::OVR_END) {
-			last_ovr_end = i;
-			break;
-		}
-	}
-
-	size_t pos = 0;
-	bool in_drawing = false;
-
-	for (size_t i = 0; i < last_ovr_end; ++i) {
-		size_t len = tokens[i].length;
-		switch (tokens[i].type) {
-			case dt::TEXT:
-				if (in_drawing)
-					tokens[i].type = dt::DRAWING;
-				break;
-			case dt::TAG_NAME:
-				if (len != 1 || i + 1 >= tokens.size() || str[pos] != 'p')
-					break;
-
-				in_drawing = false;
-
-				if (i + 1 == last_ovr_end || tokens[i + 1].type != dt::ARG)
-					break;
-
-				for (size_t j = pos + len; j < pos + len + tokens[i + 1].length; ++j) {
-					char c = str[j];
-					// I have no idea why one would use leading zeros for
-					// the scale, but vsfilter allows it
-					if (c >= '1' && c <= '9')
-						in_drawing = true;
-					else if (c != '0')
-						break;
-				}
-				break;
-			default: break;
-		}
-
-		pos += len;
-	}
-
-	// VSFilter treats unclosed override blocks as plain text, so merge all
-	// the tokens after the last override block into a single TEXT (or DRAWING)
-	// token
-	for (size_t i = last_ovr_end; i < tokens.size(); ++i) {
-		switch (tokens[i].type) {
-			case dt::KARAOKE_TEMPLATE: break;
-			case dt::KARAOKE_VARIABLE: break;
-			case dt::LINE_BREAK: break;
-			default:
-				tokens[i].type = in_drawing ? dt::DRAWING : dt::TEXT;
-				if (i > 0 && tokens[i - 1].type == tokens[i].type) {
-					tokens[i - 1].length += tokens[i].length;
-					tokens.erase(tokens.begin() + i);
-					--i;
-				}
-		}
-	}
+void MarkDrawings(std::string const &str, std::vector<DialogueToken> &tokens)
+{
+    if (tokens.empty()) return;
+
+    size_t last_ovr_end = 0;
+    for (size_t i = tokens.size(); i > 0; --i) {
+        if (tokens[i - 1].type == dt::OVR_END) {
+            last_ovr_end = i;
+            break;
+        }
+    }
+
+    size_t pos = 0;
+    bool in_drawing = false;
+
+    for (size_t i = 0; i < last_ovr_end; ++i) {
+        size_t len = tokens[i].length;
+        switch (tokens[i].type) {
+        case dt::TEXT:
+            if (in_drawing)
+                tokens[i].type = dt::DRAWING;
+            break;
+        case dt::TAG_NAME:
+            if (len != 1 || i + 1 >= tokens.size() || str[pos] != 'p')
+                break;
+
+            in_drawing = false;
+
+            if (i + 1 == last_ovr_end || tokens[i + 1].type != dt::ARG)
+                break;
+
+            for (size_t j = pos + len; j < pos + len + tokens[i + 1].length; ++j) {
+                char c = str[j];
+                // I have no idea why one would use leading zeros for
+                // the scale, but vsfilter allows it
+                if (c >= '1' && c <= '9')
+                    in_drawing = true;
+                else if (c != '0')
+                    break;
+            }
+            break;
+        default: break;
+        }
+
+        pos += len;
+    }
+
+    // VSFilter treats unclosed override blocks as plain text, so merge all
+    // the tokens after the last override block into a single TEXT (or DRAWING)
+    // token
+    for (size_t i = last_ovr_end; i < tokens.size(); ++i) {
+        switch (tokens[i].type) {
+        case dt::KARAOKE_TEMPLATE: break;
+        case dt::KARAOKE_VARIABLE: break;
+        case dt::LINE_BREAK: break;
+        default:
+            tokens[i].type = in_drawing ? dt::DRAWING : dt::TEXT;
+            if (i > 0 && tokens[i - 1].type == tokens[i].type) {
+                tokens[i - 1].length += tokens[i].length;
+                tokens.erase(tokens.begin() + i);
+                --i;
+            }
+        }
+    }
 }
 
-void SplitWords(std::string const& str, std::vector<DialogueToken> &tokens) {
-	MarkDrawings(str, tokens);
-	WordSplitter(str, tokens).SplitWords();
+void SplitWords(std::string const &str, std::vector<DialogueToken> &tokens)
+{
+    MarkDrawings(str, tokens);
+    WordSplitter(str, tokens).SplitWords();
 }
 
 }
diff --git a/libaegisub/ass/time.cpp b/libaegisub/ass/time.cpp
index a0ecbdaa5ed35a99eeb3da0df82d41622342390c..de051c9372f894c455cba72642a51e86cced6f0a 100644
--- a/libaegisub/ass/time.cpp
+++ b/libaegisub/ass/time.cpp
@@ -26,52 +26,54 @@
 namespace agi {
 Time::Time(int time) : time(util::mid(0, time, 10 * 60 * 60 * 1000 - 1)) { }
 
-Time::Time(std::string const& text) {
-	int after_decimal = -1;
-	int current = 0;
-	for (char c : text) {
-		if (c == ':') {
-			time = time * 60 + current;
-			current = 0;
-		}
-		else if (c == '.' || c == ',') {
-			time = (time * 60 + current) * 1000;
-			current = 0;
-			after_decimal = 100;
-		}
-		else if (c < '0' || c > '9')
-			continue;
-		else if (after_decimal < 0) {
-			current *= 10;
-			current += c - '0';
-		}
-		else {
-			time += (c - '0') * after_decimal;
-			after_decimal /= 10;
-		}
-	}
+Time::Time(std::string const &text)
+{
+    int after_decimal = -1;
+    int current = 0;
+    for (char c : text) {
+        if (c == ':') {
+            time = time * 60 + current;
+            current = 0;
+        }
+        else if (c == '.' || c == ',') {
+            time = (time * 60 + current) * 1000;
+            current = 0;
+            after_decimal = 100;
+        }
+        else if (c < '0' || c > '9')
+            continue;
+        else if (after_decimal < 0) {
+            current *= 10;
+            current += c - '0';
+        }
+        else {
+            time += (c - '0') * after_decimal;
+            after_decimal /= 10;
+        }
+    }
 
-	// Never saw a decimal, so convert now to ms
-	if (after_decimal < 0)
-		time = (time * 60 + current) * 1000;
+    // Never saw a decimal, so convert now to ms
+    if (after_decimal < 0)
+        time = (time * 60 + current) * 1000;
 
-	// Limit to the valid range
-	time = util::mid(0, time, 10 * 60 * 60 * 1000 - 1);
+    // Limit to the valid range
+    time = util::mid(0, time, 10 * 60 * 60 * 1000 - 1);
 }
 
-std::string Time::GetAssFormatted(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;
+std::string Time::GetAssFormatted(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 Time::GetTimeHours() const { return time / 3600000; }
@@ -81,27 +83,29 @@ int Time::GetTimeMiliseconds() const { return (time % 1000); }
 int Time::GetTimeCentiseconds() const { return (time % 1000) / 10; }
 
 SmpteFormatter::SmpteFormatter(vfr::Framerate fps, char sep)
-: fps(std::move(fps))
-, sep(sep)
+    : fps(std::move(fps))
+    , sep(sep)
 {
 }
 
-std::string SmpteFormatter::ToSMPTE(Time time) const {
-	int h=0, m=0, s=0, f=0;
-	fps.SmpteAtTime(time, &h, &m, &s, &f);
-	return format("%02d%c%02d%c%02d%c%02d", h, sep, m, sep, s, sep, f);
+std::string SmpteFormatter::ToSMPTE(Time time) const
+{
+    int h = 0, m = 0, s = 0, f = 0;
+    fps.SmpteAtTime(time, &h, &m, &s, &f);
+    return format("%02d%c%02d%c%02d%c%02d", h, sep, m, sep, s, sep, f);
 }
 
-Time SmpteFormatter::FromSMPTE(std::string const& str) const {
-	std::vector<std::string> toks;
-	Split(toks, str, sep);
-	if (toks.size() != 4) return 0;
+Time SmpteFormatter::FromSMPTE(std::string const &str) const
+{
+    std::vector<std::string> toks;
+    Split(toks, str, sep);
+    if (toks.size() != 4) return 0;
 
-	int h, m, s, f;
-	util::try_parse(toks[0], &h);
-	util::try_parse(toks[1], &m);
-	util::try_parse(toks[2], &s);
-	util::try_parse(toks[3], &f);
-	return fps.TimeAtSmpte(h, m, s, f);
+    int h, m, s, f;
+    util::try_parse(toks[0], &h);
+    util::try_parse(toks[1], &m);
+    util::try_parse(toks[2], &s);
+    util::try_parse(toks[3], &f);
+    return fps.TimeAtSmpte(h, m, s, f);
 }
 }
diff --git a/libaegisub/ass/uuencode.cpp b/libaegisub/ass/uuencode.cpp
index 182472926c7a9696e0bfa98e35fd1994251a360e..ee61cac0b36a4f63f4b1c8f343b355c756df5cdc 100644
--- a/libaegisub/ass/uuencode.cpp
+++ b/libaegisub/ass/uuencode.cpp
@@ -24,62 +24,66 @@
 // characters, and files with non-multiple-of-three lengths are padded with
 // zero.
 
-namespace agi { namespace ass {
+namespace agi {
+namespace ass {
 
-std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks) {
-	size_t size = std::distance(begin, end);
-	std::string ret;
-	ret.reserve((size * 4 + 2) / 3 + size / 80 * 2);
+std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks)
+{
+    size_t size = std::distance(begin, end);
+    std::string ret;
+    ret.reserve((size * 4 + 2) / 3 + size / 80 * 2);
 
-	size_t written = 0;
-	for (size_t pos = 0; pos < size; pos += 3) {
-		unsigned char src[3] = { '\0', '\0', '\0' };
-		memcpy(src, begin + pos, std::min<size_t>(3u, size - pos));
+    size_t written = 0;
+    for (size_t pos = 0; pos < size; pos += 3) {
+        unsigned char src[3] = { '\0', '\0', '\0' };
+        memcpy(src, begin + pos, std::min<size_t>(3u, size - pos));
 
-		unsigned char dst[4] = {
-			static_cast<unsigned char>(src[0] >> 2),
-			static_cast<unsigned char>(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
-			static_cast<unsigned char>(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
-			static_cast<unsigned char>(src[2] & 0x3F)
-		};
+        unsigned char dst[4] = {
+            static_cast<unsigned char>(src[0] >> 2),
+            static_cast<unsigned char>(((src[0] & 0x3) << 4) | ((src[1] & 0xF0) >> 4)),
+            static_cast<unsigned char>(((src[1] & 0xF) << 2) | ((src[2] & 0xC0) >> 6)),
+            static_cast<unsigned char>(src[2] & 0x3F)
+        };
 
-		for (size_t i = 0; i < std::min<size_t>(size - pos + 1, 4u); ++i) {
-			ret += dst[i] + 33;
+        for (size_t i = 0; i < std::min<size_t>(size - pos + 1, 4u); ++i) {
+            ret += dst[i] + 33;
 
-			if (insert_linebreaks && ++written == 80 && pos + 3 < size) {
-				written = 0;
-				ret += "\r\n";
-			}
-		}
-	}
+            if (insert_linebreaks && ++written == 80 && pos + 3 < size) {
+                written = 0;
+                ret += "\r\n";
+            }
+        }
+    }
 
-	return ret;
+    return ret;
 }
 
-std::vector<char> UUDecode(const char *begin, const char *end) {
-	std::vector<char> ret;
-	size_t len = end - begin;
-	ret.reserve(len * 3 / 4);
+std::vector<char> UUDecode(const char *begin, const char *end)
+{
+    std::vector<char> ret;
+    size_t len = end - begin;
+    ret.reserve(len * 3 / 4);
 
-	for (size_t pos = 0; pos + 1 < len; ) {
-		size_t bytes = 0;
-		unsigned char src[4] = { '\0', '\0', '\0', '\0' };
-		for (size_t i = 0; i < 4 && pos < len; ++pos) {
-			char c = begin[pos];
-			if (c && c != '\n' && c != '\r') {
-				src[i++] = c - 33;
-				++bytes;
-			}
-		}
+    for (size_t pos = 0; pos + 1 < len; ) {
+        size_t bytes = 0;
+        unsigned char src[4] = { '\0', '\0', '\0', '\0' };
+        for (size_t i = 0; i < 4 && pos < len; ++pos) {
+            char c = begin[pos];
+            if (c && c != '\n' && c != '\r') {
+                src[i++] = c - 33;
+                ++bytes;
+            }
+        }
 
-		if (bytes > 1)
-			ret.push_back((src[0] << 2) | (src[1] >> 4));
-		if (bytes > 2)
-			ret.push_back(((src[1] & 0xF) << 4) | (src[2] >> 2));
-		if (bytes > 3)
-			ret.push_back(((src[2] & 0x3) << 6) | (src[3]));
-	}
+        if (bytes > 1)
+            ret.push_back((src[0] << 2) | (src[1] >> 4));
+        if (bytes > 2)
+            ret.push_back(((src[1] & 0xF) << 4) | (src[2] >> 2));
+        if (bytes > 3)
+            ret.push_back(((src[2] & 0x3) << 6) | (src[3]));
+    }
 
-	return ret;
+    return ret;
+}
+}
 }
-} }
diff --git a/libaegisub/audio/provider.cpp b/libaegisub/audio/provider.cpp
index 961e5bf89c47010157ea2025e943333ff1fdd5d8..fa11232f87522b337726ce1f854c5090a8d68c11 100644
--- a/libaegisub/audio/provider.cpp
+++ b/libaegisub/audio/provider.cpp
@@ -22,116 +22,120 @@
 #include "libaegisub/util.h"
 
 namespace agi {
-void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const {
-	GetAudio(buf, start, count);
+void AudioProvider::GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const
+{
+    GetAudio(buf, start, count);
 
-	if (volume == 1.0) return;
-	if (bytes_per_sample != 2)
-		throw agi::InternalError("GetAudioWithVolume called on unconverted audio stream");
+    if (volume == 1.0) return;
+    if (bytes_per_sample != 2)
+        throw agi::InternalError("GetAudioWithVolume called on unconverted audio stream");
 
-	auto buffer = static_cast<int16_t *>(buf);
-	for (size_t i = 0; i < (size_t)count; ++i)
-		buffer[i] = util::mid(-0x8000, static_cast<int>(buffer[i] * volume + 0.5), 0x7FFF);
+    auto buffer = static_cast<int16_t *>(buf);
+    for (size_t i = 0; i < (size_t)count; ++i)
+        buffer[i] = util::mid(-0x8000, static_cast<int>(buffer[i] * volume + 0.5), 0x7FFF);
 }
 
-void AudioProvider::ZeroFill(void *buf, int64_t count) const {
-	if (bytes_per_sample == 1)
-		// 8 bit formats are usually unsigned with bias 128
-		memset(buf, 128, count * channels);
-	else // While everything else is signed
-		memset(buf, 0, count * bytes_per_sample * channels);
+void AudioProvider::ZeroFill(void *buf, int64_t count) const
+{
+    if (bytes_per_sample == 1)
+        // 8 bit formats are usually unsigned with bias 128
+        memset(buf, 128, count * channels);
+    else // While everything else is signed
+        memset(buf, 0, count * bytes_per_sample * channels);
 }
 
-void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const {
-	if (start < 0) {
-		ZeroFill(buf, std::min(-start, count));
-		buf = static_cast<char *>(buf) + -start * bytes_per_sample * channels;
-		count += start;
-		start = 0;
-	}
-
-	if (start + count > num_samples) {
-		int64_t zero_count = std::min(count, start + count - num_samples);
-		count -= zero_count;
-		ZeroFill(static_cast<char *>(buf) + count * bytes_per_sample * channels, zero_count);
-	}
-
-	if (count <= 0) return;
-
-	try {
-		FillBuffer(buf, start, count);
-	}
-	catch (AudioDecodeError const& e) {
-		// We don't have any good way to report errors here, so just log the
-		// failure and return silence
-		LOG_E("audio_provider") << e.GetMessage();
-		ZeroFill(buf, count);
-		return;
-	}
-	catch (...) {
-		LOG_E("audio_provider") << "Unknown audio decoding error";
-		ZeroFill(buf, count);
-		return;
-	}
+void AudioProvider::GetAudio(void *buf, int64_t start, int64_t count) const
+{
+    if (start < 0) {
+        ZeroFill(buf, std::min(-start, count));
+        buf = static_cast<char *>(buf) + -start * bytes_per_sample * channels;
+        count += start;
+        start = 0;
+    }
+
+    if (start + count > num_samples) {
+        int64_t zero_count = std::min(count, start + count - num_samples);
+        count -= zero_count;
+        ZeroFill(static_cast<char *>(buf) + count * bytes_per_sample * channels, zero_count);
+    }
+
+    if (count <= 0) return;
+
+    try {
+        FillBuffer(buf, start, count);
+    }
+    catch (AudioDecodeError const &e) {
+        // We don't have any good way to report errors here, so just log the
+        // failure and return silence
+        LOG_E("audio_provider") << e.GetMessage();
+        ZeroFill(buf, count);
+        return;
+    }
+    catch (...) {
+        LOG_E("audio_provider") << "Unknown audio decoding error";
+        ZeroFill(buf, count);
+        return;
+    }
 }
 
 namespace {
 class writer {
-	io::Save outfile;
-	std::ostream& out;
+    io::Save outfile;
+    std::ostream &out;
 
 public:
-	writer(agi::fs::path const& filename) : outfile(filename, true), out(outfile.Get()) { }
-
-	template<int N>
-	void write(const char(&str)[N]) {
-		out.write(str, N - 1);
-	}
-
-	void write(std::vector<char> const& data) {
-		out.write(data.data(), data.size());
-	}
-
-	template<typename Dest, typename Src>
-	void write(Src v) {
-		auto converted = static_cast<Dest>(v);
-		out.write(reinterpret_cast<char *>(&converted), sizeof(Dest));
-	}
+    writer(agi::fs::path const &filename) : outfile(filename, true), out(outfile.Get()) { }
+
+    template<int N>
+    void write(const char(&str)[N]) {
+        out.write(str, N - 1);
+    }
+
+    void write(std::vector<char> const &data) {
+        out.write(data.data(), data.size());
+    }
+
+    template<typename Dest, typename Src>
+    void write(Src v) {
+        auto converted = static_cast<Dest>(v);
+        out.write(reinterpret_cast<char *>(&converted), sizeof(Dest));
+    }
 };
 }
 
-void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int start_time, int end_time) {
-	const auto max_samples = provider.GetNumSamples();
-	const auto start_sample = std::min(max_samples, ((int64_t)start_time * provider.GetSampleRate() + 999) / 1000);
-	const auto end_sample = util::mid(start_sample, ((int64_t)end_time * provider.GetSampleRate() + 999) / 1000, max_samples);
-
-	const size_t bytes_per_sample = provider.GetBytesPerSample() * provider.GetChannels();
-	const size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
-
-	writer out{path};
-	out.write("RIFF");
-	out.write<int32_t>(bufsize + 36);
-
-	out.write("WAVEfmt ");
-	out.write<int32_t>(16); // Size of chunk
-	out.write<int16_t>(1);  // compression format (PCM)
-	out.write<int16_t>(provider.GetChannels());
-	out.write<int32_t>(provider.GetSampleRate());
-	out.write<int32_t>(provider.GetSampleRate() * provider.GetChannels() * provider.GetBytesPerSample());
-	out.write<int16_t>(provider.GetChannels() * provider.GetBytesPerSample());
-	out.write<int16_t>(provider.GetBytesPerSample() * 8);
-
-	out.write("data");
-	out.write<int32_t>(bufsize);
-
-	// samples per read
-	size_t spr = 65536 / bytes_per_sample;
-	std::vector<char> buf;
-	for (int64_t i = start_sample; i < end_sample; i += spr) {
-		spr = std::min<size_t>(spr, end_sample - i);
-		buf.resize(spr * bytes_per_sample);
-		provider.GetAudio(&buf[0], i, spr);
-		out.write(buf);
-	}
+void SaveAudioClip(AudioProvider const &provider, fs::path const &path, int start_time, int end_time)
+{
+    const auto max_samples = provider.GetNumSamples();
+    const auto start_sample = std::min(max_samples, ((int64_t)start_time * provider.GetSampleRate() + 999) / 1000);
+    const auto end_sample = util::mid(start_sample, ((int64_t)end_time * provider.GetSampleRate() + 999) / 1000, max_samples);
+
+    const size_t bytes_per_sample = provider.GetBytesPerSample() * provider.GetChannels();
+    const size_t bufsize = (end_sample - start_sample) * bytes_per_sample;
+
+    writer out{path};
+    out.write("RIFF");
+    out.write<int32_t>(bufsize + 36);
+
+    out.write("WAVEfmt ");
+    out.write<int32_t>(16); // Size of chunk
+    out.write<int16_t>(1);  // compression format (PCM)
+    out.write<int16_t>(provider.GetChannels());
+    out.write<int32_t>(provider.GetSampleRate());
+    out.write<int32_t>(provider.GetSampleRate() * provider.GetChannels() * provider.GetBytesPerSample());
+    out.write<int16_t>(provider.GetChannels() * provider.GetBytesPerSample());
+    out.write<int16_t>(provider.GetBytesPerSample() * 8);
+
+    out.write("data");
+    out.write<int32_t>(bufsize);
+
+    // samples per read
+    size_t spr = 65536 / bytes_per_sample;
+    std::vector<char> buf;
+    for (int64_t i = start_sample; i < end_sample; i += spr) {
+        spr = std::min<size_t>(spr, end_sample - i);
+        buf.resize(spr * bytes_per_sample);
+        provider.GetAudio(&buf[0], i, spr);
+        out.write(buf);
+    }
 }
 }
diff --git a/libaegisub/audio/provider_convert.cpp b/libaegisub/audio/provider_convert.cpp
index d767288938236e308bcb4adbfd69e971731866fb..1c1a67b57a90cd95bfaddacad8ebef88457699d7 100644
--- a/libaegisub/audio/provider_convert.cpp
+++ b/libaegisub/audio/provider_convert.cpp
@@ -27,181 +27,182 @@ using namespace agi;
 namespace {
 template<class Target>
 class BitdepthConvertAudioProvider final : public AudioProviderWrapper {
-	int src_bytes_per_sample;
-	mutable std::vector<uint8_t> src_buf;
+    int src_bytes_per_sample;
+    mutable std::vector<uint8_t> src_buf;
 
 public:
-	BitdepthConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
-		if (bytes_per_sample > 8)
-			throw AudioProviderError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported");
+    BitdepthConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
+        if (bytes_per_sample > 8)
+            throw AudioProviderError("Audio format converter: audio with bitdepths greater than 64 bits/sample is currently unsupported");
 
-		src_bytes_per_sample = bytes_per_sample;
-		bytes_per_sample = sizeof(Target);
-	}
+        src_bytes_per_sample = bytes_per_sample;
+        bytes_per_sample = sizeof(Target);
+    }
 
-	void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
+    void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
         if (count64 < 0)
             abort(); // Convertion is not safe
-		auto count = static_cast<size_t>(count64);
-
-		src_buf.resize(count * src_bytes_per_sample * channels);
-		source->GetAudio(src_buf.data(), start, count);
-
-		auto dest = static_cast<int16_t*>(buf);
-
-		for (int64_t i = 0; i < static_cast<int64_t>(count) * channels; ++i) {
-			int64_t sample = 0;
-
-			// 8 bits per sample is assumed to be unsigned with a bias of 127,
-			// while everything else is assumed to be signed with zero bias
-			if (src_bytes_per_sample == 1)
-				sample = src_buf[i] - 128;
-			else {
-				for (int j = src_bytes_per_sample; j > 0; --j) {
-					sample <<= 8;
-					sample += src_buf[i * src_bytes_per_sample + j - 1];
-				}
-			}
-
-			if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
-				sample /= 1LL << (src_bytes_per_sample - sizeof(Target)) * 8;
-			else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
-				sample *=  1LL << (sizeof(Target) - src_bytes_per_sample ) * 8;
-
-			dest[i] = static_cast<Target>(sample);
-		}
-	}
+        auto count = static_cast<size_t>(count64);
+
+        src_buf.resize(count * src_bytes_per_sample * channels);
+        source->GetAudio(src_buf.data(), start, count);
+
+        auto dest = static_cast<int16_t *>(buf);
+
+        for (int64_t i = 0; i < static_cast<int64_t>(count) * channels; ++i) {
+            int64_t sample = 0;
+
+            // 8 bits per sample is assumed to be unsigned with a bias of 127,
+            // while everything else is assumed to be signed with zero bias
+            if (src_bytes_per_sample == 1)
+                sample = src_buf[i] - 128;
+            else {
+                for (int j = src_bytes_per_sample; j > 0; --j) {
+                    sample <<= 8;
+                    sample += src_buf[i * src_bytes_per_sample + j - 1];
+                }
+            }
+
+            if (static_cast<size_t>(src_bytes_per_sample) > sizeof(Target))
+                sample /= 1LL << (src_bytes_per_sample - sizeof(Target)) * 8;
+            else if (static_cast<size_t>(src_bytes_per_sample) < sizeof(Target))
+                sample *=  1LL << (sizeof(Target) - src_bytes_per_sample ) * 8;
+
+            dest[i] = static_cast<Target>(sample);
+        }
+    }
 };
 
 /// Floating point -> 16 bit signed machine-endian audio converter
 template<class Source, class Target>
 class FloatConvertAudioProvider final : public AudioProviderWrapper {
-	mutable std::vector<Source> src_buf;
+    mutable std::vector<Source> src_buf;
 
 public:
-	FloatConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
-		bytes_per_sample = sizeof(Target);
-		float_samples = false;
-	}
+    FloatConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
+        bytes_per_sample = sizeof(Target);
+        float_samples = false;
+    }
 
-	void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
+    void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
         if (count64 < 0)
             abort(); // Convertion is not safe
-		auto count = static_cast<size_t>(count64);
+        auto count = static_cast<size_t>(count64);
 
-		src_buf.resize(count * channels);
-		source->GetAudio(&src_buf[0], start, count);
+        src_buf.resize(count * channels);
+        source->GetAudio(&src_buf[0], start, count);
 
-		auto dest = static_cast<Target*>(buf);
+        auto dest = static_cast<Target *>(buf);
 
-		for (size_t i = 0; i < static_cast<size_t>(count * channels); ++i) {
-			Source expanded;
-			if (src_buf[i] < 0)
-				expanded = static_cast<Target>(-src_buf[i] * std::numeric_limits<Target>::min());
-			else
-				expanded = static_cast<Target>(src_buf[i] * std::numeric_limits<Target>::max());
+        for (size_t i = 0; i < static_cast<size_t>(count * channels); ++i) {
+            Source expanded;
+            if (src_buf[i] < 0)
+                expanded = static_cast<Target>(-src_buf[i] * std::numeric_limits<Target>::min());
+            else
+                expanded = static_cast<Target>(src_buf[i] * std::numeric_limits<Target>::max());
 
-			dest[i] = expanded < std::numeric_limits<Target>::min() ? std::numeric_limits<Target>::min() :
-			          expanded > std::numeric_limits<Target>::max() ? std::numeric_limits<Target>::max() :
-			                                                          static_cast<Target>(expanded);
-		}
-	}
+            dest[i] = expanded < std::numeric_limits<Target>::min() ? std::numeric_limits<Target>::min() :
+                      expanded > std::numeric_limits<Target>::max() ? std::numeric_limits<Target>::max() :
+                      static_cast<Target>(expanded);
+        }
+    }
 };
 
 /// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter
 class DownmixAudioProvider final : public AudioProviderWrapper {
-	int src_channels;
-	mutable std::vector<int16_t> src_buf;
+    int src_channels;
+    mutable std::vector<int16_t> src_buf;
 
 public:
-	DownmixAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
-		src_channels = channels;
-		channels = 1;
-	}
+    DownmixAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
+        src_channels = channels;
+        channels = 1;
+    }
 
-	void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
+    void FillBuffer(void *buf, int64_t start, int64_t count64) const override {
         if (count64 < 0)
             abort(); // Convertion is not safe
-		auto count = static_cast<size_t>(count64);
-
-		src_buf.resize(count * src_channels);
-		source->GetAudio(&src_buf[0], start, count);
-
-		auto dst = static_cast<int16_t*>(buf);
-		// Just average the channels together
-		while (count-- > 0) {
-			int sum = 0;
-			for (int c = 0; c < src_channels; ++c)
-				sum += src_buf[count * src_channels + c];
-			dst[count] = static_cast<int16_t>(sum / src_channels);
-		}
-	}
+        auto count = static_cast<size_t>(count64);
+
+        src_buf.resize(count * src_channels);
+        source->GetAudio(&src_buf[0], start, count);
+
+        auto dst = static_cast<int16_t *>(buf);
+        // Just average the channels together
+        while (count-- > 0) {
+            int sum = 0;
+            for (int c = 0; c < src_channels; ++c)
+                sum += src_buf[count * src_channels + c];
+            dst[count] = static_cast<int16_t>(sum / src_channels);
+        }
+    }
 };
 
 /// Sample doubler with linear interpolation for the samples provider
 /// Requires 16-bit mono input
 class SampleDoublingAudioProvider final : public AudioProviderWrapper {
 public:
-	SampleDoublingAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
-		sample_rate *= 2;
-		num_samples *= 2;
-		decoded_samples = decoded_samples * 2;
-	}
-
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override {
-		int16_t *src, *dst = static_cast<int16_t *>(buf);
-
-		// We need to always get at least two samples to be able to interpolate
-		int16_t srcbuf[2];
-		if (count == 1) {
-			source->GetAudio(srcbuf, start / 2, 2);
-			src = srcbuf;
-		}
-		else {
-			source->GetAudio(buf, start / 2, (start + count) / 2 - start / 2 + 1);
-			src = dst;
-		}
-
-		// walking backwards so that the conversion can be done in place
-		for (; count > 0; --count) {
-			auto src_index = (start + count - 1) / 2 - start / 2;
-			auto i = count - 1;
-			if ((start + i) & 1)
-				dst[i] = (int16_t)(((int32_t)src[src_index] + src[src_index + 1]) / 2);
-			else
-				dst[i] = src[src_index];
-		}
-	}
+    SampleDoublingAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
+        sample_rate *= 2;
+        num_samples *= 2;
+        decoded_samples = decoded_samples * 2;
+    }
+
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override {
+        int16_t *src, *dst = static_cast<int16_t *>(buf);
+
+        // We need to always get at least two samples to be able to interpolate
+        int16_t srcbuf[2];
+        if (count == 1) {
+            source->GetAudio(srcbuf, start / 2, 2);
+            src = srcbuf;
+        }
+        else {
+            source->GetAudio(buf, start / 2, (start + count) / 2 - start / 2 + 1);
+            src = dst;
+        }
+
+        // walking backwards so that the conversion can be done in place
+        for (; count > 0; --count) {
+            auto src_index = (start + count - 1) / 2 - start / 2;
+            auto i = count - 1;
+            if ((start + i) & 1)
+                dst[i] = (int16_t)(((int32_t)src[src_index] + src[src_index + 1]) / 2);
+            else
+                dst[i] = src[src_index];
+        }
+    }
 };
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> provider) {
-	// Ensure 16-bit audio with proper endianness
-	if (provider->AreSamplesFloat()) {
-		LOG_D("audio_provider") << "Converting float to S16";
-		if (provider->GetBytesPerSample() == sizeof(float))
-			provider = agi::make_unique<FloatConvertAudioProvider<float, int16_t>>(std::move(provider));
-		else
-			provider = agi::make_unique<FloatConvertAudioProvider<double, int16_t>>(std::move(provider));
-	}
-	if (provider->GetBytesPerSample() != 2) {
-		LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample or wrong endian to S16";
-		provider = agi::make_unique<BitdepthConvertAudioProvider<int16_t>>(std::move(provider));
-	}
-
-	// We currently only support mono audio
-	if (provider->GetChannels() != 1) {
-		LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels";
-		provider = agi::make_unique<DownmixAudioProvider>(std::move(provider));
-	}
-
-	// Some players don't like low sample rate audio
-	while (provider->GetSampleRate() < 32000) {
-		LOG_D("audio_provider") << "Doubling sample rate";
-		provider = agi::make_unique<SampleDoublingAudioProvider>(std::move(provider));
-	}
-
-	return provider;
+std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> provider)
+{
+    // Ensure 16-bit audio with proper endianness
+    if (provider->AreSamplesFloat()) {
+        LOG_D("audio_provider") << "Converting float to S16";
+        if (provider->GetBytesPerSample() == sizeof(float))
+            provider = agi::make_unique<FloatConvertAudioProvider<float, int16_t>>(std::move(provider));
+        else
+            provider = agi::make_unique<FloatConvertAudioProvider<double, int16_t>>(std::move(provider));
+    }
+    if (provider->GetBytesPerSample() != 2) {
+        LOG_D("audio_provider") << "Converting " << provider->GetBytesPerSample() << " bytes per sample or wrong endian to S16";
+        provider = agi::make_unique<BitdepthConvertAudioProvider<int16_t>>(std::move(provider));
+    }
+
+    // We currently only support mono audio
+    if (provider->GetChannels() != 1) {
+        LOG_D("audio_provider") << "Downmixing to mono from " << provider->GetChannels() << " channels";
+        provider = agi::make_unique<DownmixAudioProvider>(std::move(provider));
+    }
+
+    // Some players don't like low sample rate audio
+    while (provider->GetSampleRate() < 32000) {
+        LOG_D("audio_provider") << "Doubling sample rate";
+        provider = agi::make_unique<SampleDoublingAudioProvider>(std::move(provider));
+    }
+
+    return provider;
 }
 }
diff --git a/libaegisub/audio/provider_dummy.cpp b/libaegisub/audio/provider_dummy.cpp
index 3e81ffb57dfe3fd36e42f0851e4824a7ce0a51d7..172aed2a70ec67c3aa71048a723d44193d44e8a9 100644
--- a/libaegisub/audio/provider_dummy.cpp
+++ b/libaegisub/audio/provider_dummy.cpp
@@ -46,35 +46,36 @@
 namespace {
 using namespace agi;
 class DummyAudioProvider final : public AudioProvider {
-	bool noise;
+    bool noise;
 
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override {
-		if (noise) {
-			std::default_random_engine e(int32_t(start >> 32) ^ int32_t(start));
-			std::uniform_int_distribution<int16_t> uniform_dist(-5000, 5000);
-			for (int64_t i = 0; i < count; ++i)
-				static_cast<short *>(buf)[i] = uniform_dist(e);
-		}
-		else
-			memset(buf, 0, static_cast<size_t>(count) * bytes_per_sample);
-	}
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override {
+        if (noise) {
+            std::default_random_engine e(int32_t(start >> 32) ^ int32_t(start));
+            std::uniform_int_distribution<int16_t> uniform_dist(-5000, 5000);
+            for (int64_t i = 0; i < count; ++i)
+                static_cast<short *>(buf)[i] = uniform_dist(e);
+        }
+        else
+            memset(buf, 0, static_cast<size_t>(count) * bytes_per_sample);
+    }
 
 public:
-	DummyAudioProvider(agi::fs::path const& uri) {
-		noise = boost::contains(uri.string(), ":noise?");
-		channels = 1;
-		sample_rate = 44100;
-		bytes_per_sample = 2;
-		float_samples = false;
-		decoded_samples = num_samples = (int64_t)5*30*60*1000 * sample_rate / 1000;
-	}
+    DummyAudioProvider(agi::fs::path const &uri) {
+        noise = boost::contains(uri.string(), ":noise?");
+        channels = 1;
+        sample_rate = 44100;
+        bytes_per_sample = 2;
+        float_samples = false;
+        decoded_samples = num_samples = (int64_t)5 * 30 * 60 * 1000 * sample_rate / 1000;
+    }
 };
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreateDummyAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {
-	if (!boost::starts_with(file.string(), "dummy-audio:"))
-		return {};
-	return agi::make_unique<DummyAudioProvider>(file);
+std::unique_ptr<AudioProvider> CreateDummyAudioProvider(agi::fs::path const &file, agi::BackgroundRunner *)
+{
+    if (!boost::starts_with(file.string(), "dummy-audio:"))
+        return {};
+    return agi::make_unique<DummyAudioProvider>(file);
 }
 }
diff --git a/libaegisub/audio/provider_hd.cpp b/libaegisub/audio/provider_hd.cpp
index 19e33eeed6591667851ef681cd42cda920cbc060..6055af0c1b6b188aaa7d1756feb46c4f7c50394e 100644
--- a/libaegisub/audio/provider_hd.cpp
+++ b/libaegisub/audio/provider_hd.cpp
@@ -31,59 +31,59 @@ namespace {
 using namespace agi;
 
 class HDAudioProvider final : public AudioProviderWrapper {
-	mutable temp_file_mapping file;
-	std::atomic<bool> cancelled = {false};
-	std::thread decoder;
+    mutable temp_file_mapping file;
+    std::atomic<bool> cancelled = {false};
+    std::thread decoder;
 
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override {
-		auto missing = std::min(count, start + count - decoded_samples);
-		if (missing > 0) {
-			memset(static_cast<int16_t*>(buf) + count - missing, 0, missing * bytes_per_sample);
-			count -= missing;
-		}
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override {
+        auto missing = std::min(count, start + count - decoded_samples);
+        if (missing > 0) {
+            memset(static_cast<int16_t *>(buf) + count - missing, 0, missing * bytes_per_sample);
+            count -= missing;
+        }
 
-		if (count > 0) {
-			start *= bytes_per_sample;
-			count *= bytes_per_sample;
-			memcpy(buf, file.read(start, count), count);
-		}
-	}
+        if (count > 0) {
+            start *= bytes_per_sample;
+            count *= bytes_per_sample;
+            memcpy(buf, file.read(start, count), count);
+        }
+    }
 
-	fs::path CacheFilename(fs::path const& dir) {
-		// Check free space
-		if ((uint64_t)num_samples * bytes_per_sample > fs::FreeSpace(dir))
-			throw AudioProviderError("Not enough free disk space in " + dir.string() + " to cache the audio");
+    fs::path CacheFilename(fs::path const &dir) {
+        // Check free space
+        if ((uint64_t)num_samples * bytes_per_sample > fs::FreeSpace(dir))
+            throw AudioProviderError("Not enough free disk space in " + dir.string() + " to cache the audio");
 
-		return format("audio-%lld-%lld", time(nullptr),
-		              boost::interprocess::ipcdetail::get_current_process_id());
-	}
+        return format("audio-%lld-%lld", time(nullptr),
+                      boost::interprocess::ipcdetail::get_current_process_id());
+    }
 
 public:
-	HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir)
-	: AudioProviderWrapper(std::move(src))
-	, file(dir / CacheFilename(dir), num_samples * bytes_per_sample)
-	{
-		decoded_samples = 0;
-		decoder = std::thread([&] {
-			int64_t block = 65536;
-			for (int64_t i = 0; i < num_samples; i += block) {
-				if (cancelled) break;
-				block = std::min(block, num_samples - i);
-				source->GetAudio(file.write(i * bytes_per_sample, block * bytes_per_sample), i, block);
-				decoded_samples += block;
-			}
-		});
-	}
+    HDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const &dir)
+        : AudioProviderWrapper(std::move(src))
+        , file(dir / CacheFilename(dir), num_samples * bytes_per_sample) {
+        decoded_samples = 0;
+        decoder = std::thread([&] {
+            int64_t block = 65536;
+            for (int64_t i = 0; i < num_samples; i += block) {
+                if (cancelled) break;
+                block = std::min(block, num_samples - i);
+                source->GetAudio(file.write(i * bytes_per_sample, block * bytes_per_sample), i, block);
+                decoded_samples += block;
+            }
+        });
+    }
 
-	~HDAudioProvider() {
-		cancelled = true;
-		decoder.join();
-	}
+    ~HDAudioProvider() {
+        cancelled = true;
+        decoder.join();
+    }
 };
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const& dir) {
-	return agi::make_unique<HDAudioProvider>(std::move(src), dir);
+std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> src, agi::fs::path const &dir)
+{
+    return agi::make_unique<HDAudioProvider>(std::move(src), dir);
 }
 }
diff --git a/libaegisub/audio/provider_lock.cpp b/libaegisub/audio/provider_lock.cpp
index eb397e41039758b61f9e1947fc5b82f7ff11ee2c..1f1fb2f90ef64b5cda0527e1cf43a817fb8d5273 100644
--- a/libaegisub/audio/provider_lock.cpp
+++ b/libaegisub/audio/provider_lock.cpp
@@ -22,23 +22,23 @@
 
 namespace {
 class LockAudioProvider final : public agi::AudioProviderWrapper {
-	mutable std::mutex mutex;
+    mutable std::mutex mutex;
 
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override {
-		std::unique_lock<std::mutex> lock(mutex);
-		source->GetAudio(buf, start, count);
-	}
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override {
+        std::unique_lock<std::mutex> lock(mutex);
+        source->GetAudio(buf, start, count);
+    }
 
 public:
-	LockAudioProvider(std::unique_ptr<AudioProvider> src)
-	: AudioProviderWrapper(std::move(src))
-	{
-	}
+    LockAudioProvider(std::unique_ptr<AudioProvider> src)
+        : AudioProviderWrapper(std::move(src)) {
+    }
 };
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> src) {
-	return agi::make_unique<LockAudioProvider>(std::move(src));
+std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> src)
+{
+    return agi::make_unique<LockAudioProvider>(std::move(src));
 }
 }
diff --git a/libaegisub/audio/provider_pcm.cpp b/libaegisub/audio/provider_pcm.cpp
index 13d4538b0e285bcf38353c7cd95cbd0bf92ca7c3..96e2903879824ad7005b6e080eb5e9c4de7ab320 100644
--- a/libaegisub/audio/provider_pcm.cpp
+++ b/libaegisub/audio/provider_pcm.cpp
@@ -27,216 +27,220 @@ namespace {
 using namespace agi;
 
 struct IndexPoint {
-	uint64_t start_byte;
-	uint64_t num_samples;
+    uint64_t start_byte;
+    uint64_t num_samples;
 };
 
 struct file_ended {};
 
 class PCMAudioProvider : public AudioProvider {
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override {
-		auto write_buf = static_cast<char *>(buf);
-		auto bps = bytes_per_sample * channels;
-		uint64_t pos = 0;
-
-		for (auto ip : index_points) {
-			if (count == 0) break;
-			if (pos + ip.num_samples <= (uint64_t)start) {
-				pos += ip.num_samples;
-				continue;
-			}
-
-			auto read_offset = start - pos;
-			auto read_count = std::min<size_t>(count, ip.num_samples - read_offset);
-			auto bytes = read_count * bps;
-			memcpy(write_buf, file.read(ip.start_byte + read_offset * bps, bytes), bytes);
-
-			write_buf += bytes;
-			count -= read_count;
-			start += read_count;
-			pos += ip.num_samples;
-		}
-	}
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override {
+        auto write_buf = static_cast<char *>(buf);
+        auto bps = bytes_per_sample * channels;
+        uint64_t pos = 0;
+
+        for (auto ip : index_points) {
+            if (count == 0) break;
+            if (pos + ip.num_samples <= (uint64_t)start) {
+                pos += ip.num_samples;
+                continue;
+            }
+
+            auto read_offset = start - pos;
+            auto read_count = std::min<size_t>(count, ip.num_samples - read_offset);
+            auto bytes = read_count * bps;
+            memcpy(write_buf, file.read(ip.start_byte + read_offset * bps, bytes), bytes);
+
+            write_buf += bytes;
+            count -= read_count;
+            start += read_count;
+            pos += ip.num_samples;
+        }
+    }
 
 protected:
-	mutable read_file_mapping file;
-	uint64_t file_pos = 0;
+    mutable read_file_mapping file;
+    uint64_t file_pos = 0;
 
-	PCMAudioProvider(fs::path const& filename) : file(filename) { }
+    PCMAudioProvider(fs::path const &filename) : file(filename) { }
 
-	template<typename T, typename UInt>
-	T Read(UInt *data_left) {
-		if (*data_left < sizeof(T)) throw file_ended();
-		if (file.size() - file_pos < sizeof(T)) throw file_ended();
+    template<typename T, typename UInt>
+    T Read(UInt *data_left) {
+        if (*data_left < sizeof(T)) throw file_ended();
+        if (file.size() - file_pos < sizeof(T)) throw file_ended();
 
-		auto data = file.read(file_pos, sizeof(T));
-		file_pos += sizeof(T);
-		*data_left -= sizeof(T);
-		T ret;
-		memcpy(&ret, data, sizeof(T));
-		return ret;
-	}
+        auto data = file.read(file_pos, sizeof(T));
+        file_pos += sizeof(T);
+        *data_left -= sizeof(T);
+        T ret;
+        memcpy(&ret, data, sizeof(T));
+        return ret;
+    }
 
-	std::vector<IndexPoint> index_points;
+    std::vector<IndexPoint> index_points;
 };
 
 struct FourCC {
-	std::array<char, 4> data;
-	bool operator!=(const char *cmp) const {
-		return data[0] != cmp[0] || data[1] != cmp[1]
-			|| data[2] != cmp[2] || data[3] != cmp[3];
-	}
-	bool operator==(const char *cmp) const { return !(*this != cmp); }
+    std::array<char, 4> data;
+    bool operator!=(const char *cmp) const {
+        return data[0] != cmp[0] || data[1] != cmp[1]
+               || data[2] != cmp[2] || data[3] != cmp[3];
+    }
+    bool operator==(const char *cmp) const { return !(*this != cmp); }
 };
 
 // Overview of RIFF WAV: <http://www.sonicspot.com/guide/wavefiles.html>
 struct RiffWav {
-	using DataSize = uint32_t;
-	using ChunkId = FourCC;
+    using DataSize = uint32_t;
+    using ChunkId = FourCC;
 
-	static const char *riff_id() { return "RIFF"; }
-	static const char *wave_id() { return "WAVE"; }
-	static const char *fmt_id()  { return "fmt "; }
-	static const char *data_id() { return "data "; }
+    static const char *riff_id() { return "RIFF"; }
+    static const char *wave_id() { return "WAVE"; }
+    static const char *fmt_id()  { return "fmt "; }
+    static const char *data_id() { return "data "; }
 
-	static const int alignment = 1;
+    static const int alignment = 1;
 
-	static uint32_t data_size(uint32_t size) { return size; }
-	static uint32_t chunk_size(uint32_t size) { return size; }
+    static uint32_t data_size(uint32_t size) { return size; }
+    static uint32_t chunk_size(uint32_t size) { return size; }
 };
 
 typedef std::array<uint8_t, 16> GUID;
 
 static const GUID w64GuidRIFF = {{
-	// {66666972-912E-11CF-A5D6-28DB04C10000}
-	0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
-}};
+        // {66666972-912E-11CF-A5D6-28DB04C10000}
+        0x72, 0x69, 0x66, 0x66, 0x2E, 0x91, 0xCF, 0x11, 0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00
+    }
+};
 
 static const GUID w64GuidWAVE = {{
-	// {65766177-ACF3-11D3-8CD1-00C04F8EDB8A}
-	0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-}};
+        // {65766177-ACF3-11D3-8CD1-00C04F8EDB8A}
+        0x77, 0x61, 0x76, 0x65, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+    }
+};
 
 static const GUID w64Guidfmt = {{
-	// {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A}
-	0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-}};
+        // {20746D66-ACF3-11D3-8CD1-00C04F8EDB8A}
+        0x66, 0x6D, 0x74, 0x20, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+    }
+};
 
 static const GUID w64Guiddata = {{
-	// {61746164-ACF3-11D3-8CD1-00C04F8EDB8A}
-	0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
-}};
+        // {61746164-ACF3-11D3-8CD1-00C04F8EDB8A}
+        0x64, 0x61, 0x74, 0x61, 0xF3, 0xAC, 0xD3, 0x11, 0x8C, 0xD1, 0x00, 0xC0, 0x4F, 0x8E, 0xDB, 0x8A
+    }
+};
 
 // http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
 struct Wave64 {
-	using DataSize = uint64_t;
-	using ChunkId = GUID;
+    using DataSize = uint64_t;
+    using ChunkId = GUID;
 
-	static GUID riff_id() { return w64GuidRIFF; }
-	static GUID wave_id() { return w64GuidWAVE; }
-	static GUID fmt_id()  { return w64Guidfmt; }
-	static GUID data_id() { return w64Guiddata; }
+    static GUID riff_id() { return w64GuidRIFF; }
+    static GUID wave_id() { return w64GuidWAVE; }
+    static GUID fmt_id()  { return w64Guidfmt; }
+    static GUID data_id() { return w64Guiddata; }
 
-	static const uint64_t alignment = 7ULL;
+    static const uint64_t alignment = 7ULL;
 
-	// Wave 64 includes the size of the header in the chunk sizes
-	static uint64_t data_size(uint64_t size) { return size - 16; }
-	static uint64_t chunk_size(uint64_t size) { return size - 24; }
+    // Wave 64 includes the size of the header in the chunk sizes
+    static uint64_t data_size(uint64_t size) { return size - 16; }
+    static uint64_t chunk_size(uint64_t size) { return size - 24; }
 };
 
 template<typename Impl>
 class WavPCMAudioProvider : public PCMAudioProvider {
 public:
-	WavPCMAudioProvider(fs::path const& filename)
-	: PCMAudioProvider(filename)
-	{
-		using DataSize = typename Impl::DataSize;
-		using ChunkId = typename Impl::ChunkId;
-
-		try {
-			auto data_left = std::numeric_limits<DataSize>::max();
-			if (Read<ChunkId>(&data_left) != Impl::riff_id())
-				throw AudioDataNotFound("File is not a RIFF file");
-
-			data_left = Impl::data_size(Read<DataSize>(&data_left));
-
-			if (Read<ChunkId>(&data_left) != Impl::wave_id())
-				throw AudioDataNotFound("File is not a RIFF WAV file");
-
-			while (data_left) {
-				auto chunk_fcc = Read<ChunkId>(&data_left);
-				auto chunk_size = Impl::chunk_size(Read<DataSize>(&data_left));
-
-				data_left -= std::min(chunk_size, data_left);
-
-				if (chunk_fcc == Impl::fmt_id()) {
-					if (channels || sample_rate || bytes_per_sample)
-						throw AudioProviderError("Multiple 'fmt ' chunks not supported");
-
-					auto compression = Read<uint16_t>(&chunk_size);
-					if (compression != 1)
-						throw AudioProviderError("File is not uncompressed PCM");
-
-					channels = Read<uint16_t>(&chunk_size);
-					sample_rate = Read<uint32_t>(&chunk_size);
-					Read<uint32_t>(&chunk_size); // Average bytes per sample; meaningless
-					Read<uint16_t>(&chunk_size); // Block align
-					bytes_per_sample = (Read<uint16_t>(&chunk_size) + 7) / 8;
-				}
-				else if (chunk_fcc == Impl::data_id()) {
-					if (!channels || !sample_rate || !bytes_per_sample)
-						throw AudioProviderError("Found 'data' chunk without format being set.");
-					index_points.emplace_back(IndexPoint{file_pos, chunk_size / bytes_per_sample / channels});
-					num_samples += chunk_size / bytes_per_sample / channels;
-				}
-				// There's a bunch of other chunk types. They're all dumb.
-
-				// blocks are aligned and the padding bytes are not included in
-				// the size of the chunk
-				file_pos += (chunk_size + Impl::alignment) & ~Impl::alignment;
-			}
-
-		}
-		catch (file_ended) {
-			if (!channels || !sample_rate || !bytes_per_sample)
-				throw AudioDataNotFound("File ended before reaching format chunk");
-			// Truncated files are fine otherwise
-		}
-		decoded_samples = num_samples;
-	}
+    WavPCMAudioProvider(fs::path const &filename)
+        : PCMAudioProvider(filename) {
+        using DataSize = typename Impl::DataSize;
+        using ChunkId = typename Impl::ChunkId;
+
+        try {
+            auto data_left = std::numeric_limits<DataSize>::max();
+            if (Read<ChunkId>(&data_left) != Impl::riff_id())
+                throw AudioDataNotFound("File is not a RIFF file");
+
+            data_left = Impl::data_size(Read<DataSize>(&data_left));
+
+            if (Read<ChunkId>(&data_left) != Impl::wave_id())
+                throw AudioDataNotFound("File is not a RIFF WAV file");
+
+            while (data_left) {
+                auto chunk_fcc = Read<ChunkId>(&data_left);
+                auto chunk_size = Impl::chunk_size(Read<DataSize>(&data_left));
+
+                data_left -= std::min(chunk_size, data_left);
+
+                if (chunk_fcc == Impl::fmt_id()) {
+                    if (channels || sample_rate || bytes_per_sample)
+                        throw AudioProviderError("Multiple 'fmt ' chunks not supported");
+
+                    auto compression = Read<uint16_t>(&chunk_size);
+                    if (compression != 1)
+                        throw AudioProviderError("File is not uncompressed PCM");
+
+                    channels = Read<uint16_t>(&chunk_size);
+                    sample_rate = Read<uint32_t>(&chunk_size);
+                    Read<uint32_t>(&chunk_size); // Average bytes per sample; meaningless
+                    Read<uint16_t>(&chunk_size); // Block align
+                    bytes_per_sample = (Read<uint16_t>(&chunk_size) + 7) / 8;
+                }
+                else if (chunk_fcc == Impl::data_id()) {
+                    if (!channels || !sample_rate || !bytes_per_sample)
+                        throw AudioProviderError("Found 'data' chunk without format being set.");
+                    index_points.emplace_back(IndexPoint{file_pos, chunk_size / bytes_per_sample / channels});
+                    num_samples += chunk_size / bytes_per_sample / channels;
+                }
+                // There's a bunch of other chunk types. They're all dumb.
+
+                // blocks are aligned and the padding bytes are not included in
+                // the size of the chunk
+                file_pos += (chunk_size + Impl::alignment) & ~Impl::alignment;
+            }
+
+        }
+        catch (file_ended) {
+            if (!channels || !sample_rate || !bytes_per_sample)
+                throw AudioDataNotFound("File ended before reaching format chunk");
+            // Truncated files are fine otherwise
+        }
+        decoded_samples = num_samples;
+    }
 };
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const& filename, BackgroundRunner *) {
-	bool wrong_file_type = true;
-	std::string msg;
-
-	try {
-		return make_unique<WavPCMAudioProvider<RiffWav>>(filename);
-	}
-	catch (AudioDataNotFound const& err) {
-		msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
-	}
-	catch (AudioProviderError const& err) {
-		wrong_file_type = false;
-		msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
-	}
-
-	try {
-		return make_unique<WavPCMAudioProvider<Wave64>>(filename);
-	}
-	catch (AudioDataNotFound const& err) {
-		msg += "\nWave64 audio provider: " + err.GetMessage();
-	}
-	catch (AudioProviderError const& err) {
-		wrong_file_type = false;
-		msg += "\nWave64 audio provider: " + err.GetMessage();
-	}
-
-	if (wrong_file_type)
-		throw AudioDataNotFound(msg);
-	else
-		throw AudioProviderError(msg);
+std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const &filename, BackgroundRunner *)
+{
+    bool wrong_file_type = true;
+    std::string msg;
+
+    try {
+        return make_unique<WavPCMAudioProvider<RiffWav>>(filename);
+    }
+    catch (AudioDataNotFound const &err) {
+        msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
+    }
+    catch (AudioProviderError const &err) {
+        wrong_file_type = false;
+        msg = "RIFF PCM WAV audio provider: " + err.GetMessage();
+    }
+
+    try {
+        return make_unique<WavPCMAudioProvider<Wave64>>(filename);
+    }
+    catch (AudioDataNotFound const &err) {
+        msg += "\nWave64 audio provider: " + err.GetMessage();
+    }
+    catch (AudioProviderError const &err) {
+        wrong_file_type = false;
+        msg += "\nWave64 audio provider: " + err.GetMessage();
+    }
+
+    if (wrong_file_type)
+        throw AudioDataNotFound(msg);
+    else
+        throw AudioProviderError(msg);
 }
 }
diff --git a/libaegisub/audio/provider_ram.cpp b/libaegisub/audio/provider_ram.cpp
index 0c1da546c12f12e8d603caec86c04938443ceba8..d8d6b72a24593254a623e33723209f1df3403f3a 100644
--- a/libaegisub/audio/provider_ram.cpp
+++ b/libaegisub/audio/provider_ram.cpp
@@ -30,67 +30,68 @@ using namespace agi;
 
 class RAMAudioProvider final : public AudioProviderWrapper {
 #ifdef _MSC_VER
-	boost::container::stable_vector<char[CacheBlockSize]> blockcache;
+    boost::container::stable_vector<char[CacheBlockSize]> blockcache;
 #else
-	boost::container::stable_vector<std::array<char, CacheBlockSize>> blockcache;
+    boost::container::stable_vector<std::array<char, CacheBlockSize>> blockcache;
 #endif
-	std::atomic<bool> cancelled = {false};
-	std::thread decoder;
+    std::atomic<bool> cancelled = {false};
+    std::thread decoder;
 
-	void FillBuffer(void *buf, int64_t start, int64_t count) const override;
+    void FillBuffer(void *buf, int64_t start, int64_t count) const override;
 
 public:
-	RAMAudioProvider(std::unique_ptr<AudioProvider> src)
-	: AudioProviderWrapper(std::move(src))
-	{
-		decoded_samples = 0;
+    RAMAudioProvider(std::unique_ptr<AudioProvider> src)
+        : AudioProviderWrapper(std::move(src)) {
+        decoded_samples = 0;
 
-		try {
-			blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits);
-		}
-		catch (std::bad_alloc const&) {
-			throw AudioProviderError("Not enough memory available to cache in RAM");
-		}
+        try {
+            blockcache.resize((source->GetNumSamples() * source->GetBytesPerSample() + CacheBlockSize - 1) >> CacheBits);
+        }
+        catch (std::bad_alloc const &) {
+            throw AudioProviderError("Not enough memory available to cache in RAM");
+        }
 
-		decoder = std::thread([&] {
-			int64_t readsize = CacheBlockSize / source->GetBytesPerSample();
-			for (size_t i = 0; i < blockcache.size(); i++) {
-				if (cancelled) break;
-				auto actual_read = std::min<int64_t>(readsize, num_samples - i * readsize);
-				source->GetAudio(&blockcache[i][0], i * readsize, actual_read);
-				decoded_samples += actual_read;
-			}
-		});
-	}
+        decoder = std::thread([&] {
+            int64_t readsize = CacheBlockSize / source->GetBytesPerSample();
+            for (size_t i = 0; i < blockcache.size(); i++) {
+                if (cancelled) break;
+                auto actual_read = std::min<int64_t>(readsize, num_samples - i * readsize);
+                source->GetAudio(&blockcache[i][0], i * readsize, actual_read);
+                decoded_samples += actual_read;
+            }
+        });
+    }
 
-	~RAMAudioProvider() {
-		cancelled = true;
-		decoder.join();
-	}
+    ~RAMAudioProvider() {
+        cancelled = true;
+        decoder.join();
+    }
 };
 
-void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
-	auto charbuf = static_cast<char *>(buf);
-	for (int64_t bytes_remaining = count * bytes_per_sample; bytes_remaining; ) {
-		if (start >= decoded_samples) {
-			memset(charbuf, 0, bytes_remaining);
-			break;
-		}
+void RAMAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
+{
+    auto charbuf = static_cast<char *>(buf);
+    for (int64_t bytes_remaining = count * bytes_per_sample; bytes_remaining; ) {
+        if (start >= decoded_samples) {
+            memset(charbuf, 0, bytes_remaining);
+            break;
+        }
 
-		const int i = (start * bytes_per_sample) >> CacheBits;
-		const int start_offset = (start * bytes_per_sample) & (CacheBlockSize-1);
-		const int read_size = std::min<int>(bytes_remaining, CacheBlockSize - start_offset);
+        const int i = (start * bytes_per_sample) >> CacheBits;
+        const int start_offset = (start * bytes_per_sample) & (CacheBlockSize - 1);
+        const int read_size = std::min<int>(bytes_remaining, CacheBlockSize - start_offset);
 
-		memcpy(charbuf, &blockcache[i][start_offset], read_size);
-		charbuf += read_size;
-		bytes_remaining -= read_size;
-		start += read_size / bytes_per_sample;
-	}
+        memcpy(charbuf, &blockcache[i][start_offset], read_size);
+        charbuf += read_size;
+        bytes_remaining -= read_size;
+        start += read_size / bytes_per_sample;
+    }
 }
 }
 
 namespace agi {
-std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> src) {
-	return agi::make_unique<RAMAudioProvider>(std::move(src));
+std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> src)
+{
+    return agi::make_unique<RAMAudioProvider>(std::move(src));
 }
 }
diff --git a/libaegisub/common/cajun/elements.cpp b/libaegisub/common/cajun/elements.cpp
index b8f5a017444c3a97f5f7ea0685ce982e1625ee51..9dd379fc657088416b82ef5b77efb4c29cf63ab0 100644
--- a/libaegisub/common/cajun/elements.cpp
+++ b/libaegisub/common/cajun/elements.cpp
@@ -15,49 +15,49 @@ namespace {
 using namespace json;
 
 class CastVisitorBase : public Visitor {
-	void Visit(Array&) override { }
-	void Visit(Object&) override { }
-	void Visit(Integer&) override { }
-	void Visit(Double&) override { }
-	void Visit(String&) override { }
-	void Visit(Boolean&) override { }
-	void Visit(Null&) override { is_null = true; }
+    void Visit(Array &) override { }
+    void Visit(Object &) override { }
+    void Visit(Integer &) override { }
+    void Visit(Double &) override { }
+    void Visit(String &) override { }
+    void Visit(Boolean &) override { }
+    void Visit(Null &) override { is_null = true; }
 public:
-	bool is_null = false;
+    bool is_null = false;
 };
 
 template<typename T>
 struct CastVisitor final : public CastVisitorBase {
-	T *element = nullptr;
-	void Visit(T& ele) override { element = &ele; }
+    T *element = nullptr;
+    void Visit(T &ele) override { element = &ele; }
 };
 }
 
 namespace json {
 class UnknownElement::Imp {
 public:
-	virtual ~Imp() {}
-	virtual void Accept(ConstVisitor& visitor) const = 0;
-	virtual void Accept(Visitor& visitor) = 0;
+    virtual ~Imp() {}
+    virtual void Accept(ConstVisitor &visitor) const = 0;
+    virtual void Accept(Visitor &visitor) = 0;
 };
 }
 
 namespace {
 template <typename ElementTypeT>
 class Imp_T final : public UnknownElement::Imp {
-	ElementTypeT m_Element;
+    ElementTypeT m_Element;
 
 public:
-	Imp_T(ElementTypeT element) : m_Element(std::move(element)) { }
+    Imp_T(ElementTypeT element) : m_Element(std::move(element)) { }
 
-	void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
-	void Accept(Visitor& visitor)            { visitor.Visit(m_Element); }
+    void Accept(ConstVisitor &visitor) const { visitor.Visit(m_Element); }
+    void Accept(Visitor &visitor)            { visitor.Visit(m_Element); }
 };
 }
 
 namespace json {
 UnknownElement::UnknownElement()                         : m_pImp(new Imp_T<Null>(Null())) {}
-UnknownElement::UnknownElement(UnknownElement&& unknown) : m_pImp(std::move(unknown.m_pImp)) {}
+UnknownElement::UnknownElement(UnknownElement &&unknown) : m_pImp(std::move(unknown.m_pImp)) {}
 UnknownElement::UnknownElement(int number)               : m_pImp(new Imp_T<Integer>(number)) {}
 UnknownElement::UnknownElement(const char *string)       : m_pImp(new Imp_T<String>(string)) {}
 
@@ -76,37 +76,40 @@ DEFINE_UE_TYPE(Boolean)
 DEFINE_UE_TYPE(String)
 DEFINE_UE_TYPE(Null)
 
-UnknownElement& UnknownElement::operator=(UnknownElement&& unknown) {
-   m_pImp = std::move(unknown.m_pImp);
-   return *this;
+UnknownElement &UnknownElement::operator=(UnknownElement &&unknown)
+{
+    m_pImp = std::move(unknown.m_pImp);
+    return *this;
 }
 
 template <typename ElementTypeT>
-ElementTypeT const& UnknownElement::CastTo() const {
-   CastVisitor<ElementTypeT> castVisitor;
-   const_cast<UnknownElement *>(this)->Accept(castVisitor);
-   if (!castVisitor.element)
-      throw Exception("Bad cast");
-   return *castVisitor.element;
+ElementTypeT const &UnknownElement::CastTo() const
+{
+    CastVisitor<ElementTypeT> castVisitor;
+    const_cast<UnknownElement *>(this)->Accept(castVisitor);
+    if (!castVisitor.element)
+        throw Exception("Bad cast");
+    return *castVisitor.element;
 }
 
 template <typename ElementTypeT>
-ElementTypeT& UnknownElement::CastTo() {
-   CastVisitor<ElementTypeT> castVisitor;
-   Accept(castVisitor);
-
-   // If this element is uninitialized, implicitly convert it to the desired type
-   if (castVisitor.is_null) {
-      *this = ElementTypeT();
-      return *this;
-   }
-
-   // Otherwise throw an exception
-   if (!castVisitor.element)
-      throw Exception("Bad cast");
-   return *castVisitor.element;
+ElementTypeT &UnknownElement::CastTo()
+{
+    CastVisitor<ElementTypeT> castVisitor;
+    Accept(castVisitor);
+
+    // If this element is uninitialized, implicitly convert it to the desired type
+    if (castVisitor.is_null) {
+        *this = ElementTypeT();
+        return *this;
+    }
+
+    // Otherwise throw an exception
+    if (!castVisitor.element)
+        throw Exception("Bad cast");
+    return *castVisitor.element;
 }
 
-void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }
-void UnknownElement::Accept(Visitor& visitor)            { m_pImp->Accept(visitor); }
+void UnknownElement::Accept(ConstVisitor &visitor) const { m_pImp->Accept(visitor); }
+void UnknownElement::Accept(Visitor &visitor)            { m_pImp->Accept(visitor); }
 }
diff --git a/libaegisub/common/cajun/reader.cpp b/libaegisub/common/cajun/reader.cpp
index 6f32d2ff45a1e7ed5e917858a71a398b94a6dcc5..192fb313dbeac0a055d90f824c9b676db2e0ef62 100644
--- a/libaegisub/common/cajun/reader.cpp
+++ b/libaegisub/common/cajun/reader.cpp
@@ -22,330 +22,344 @@ TODO:
 namespace json {
 /// Wrapper around istream to keep track of document/line offsets
 class Reader::InputStream {
-	std::istream& m_iStr;
-	Location m_Location;
+    std::istream &m_iStr;
+    Location m_Location;
 public:
-	InputStream(std::istream& iStr) : m_iStr(iStr) { }
-
-	int Get() {
-		assert(!m_iStr.eof());
-		int c = m_iStr.get();
-
-		++m_Location.m_nDocOffset;
-		if (c == '\n') {
-			++m_Location.m_nLine;
-			m_Location.m_nLineOffset = 0;
-		}
-		else {
-			++m_Location.m_nLineOffset;
-		}
-
-		return c;
-	}
-
-	int Peek() {
-		assert(!m_iStr.eof());
-		return m_iStr.peek();
-	}
-
-	bool EOS() {
-		// libc++ doesn't set eof when a peek fails
-		return m_iStr.peek() == EOF || m_iStr.eof();
-	}
-
-	Location const& GetLocation() const { return m_Location; }
+    InputStream(std::istream &iStr) : m_iStr(iStr) { }
+
+    int Get() {
+        assert(!m_iStr.eof());
+        int c = m_iStr.get();
+
+        ++m_Location.m_nDocOffset;
+        if (c == '\n') {
+            ++m_Location.m_nLine;
+            m_Location.m_nLineOffset = 0;
+        }
+        else {
+            ++m_Location.m_nLineOffset;
+        }
+
+        return c;
+    }
+
+    int Peek() {
+        assert(!m_iStr.eof());
+        return m_iStr.peek();
+    }
+
+    bool EOS() {
+        // libc++ doesn't set eof when a peek fails
+        return m_iStr.peek() == EOF || m_iStr.eof();
+    }
+
+    Location const &GetLocation() const { return m_Location; }
 };
 
 class Reader::TokenStream {
-	Tokens const& m_Tokens;
-	Tokens::const_iterator m_itCurrent;
+    Tokens const &m_Tokens;
+    Tokens::const_iterator m_itCurrent;
 
 public:
-	TokenStream(Tokens const& tokens) : m_Tokens(tokens), m_itCurrent(tokens.begin())
-	{ }
-
-	Token const& Peek() {
-		assert(!EOS());
-		return *m_itCurrent;
-	}
-	Token const& Get() {
-		assert(!EOS());
-		return *m_itCurrent++;
-	}
-
-	bool EOS() const { return m_itCurrent == m_Tokens.end(); }
+    TokenStream(Tokens const &tokens) : m_Tokens(tokens), m_itCurrent(tokens.begin())
+    { }
+
+    Token const &Peek() {
+        assert(!EOS());
+        return *m_itCurrent;
+    }
+    Token const &Get() {
+        assert(!EOS());
+        return *m_itCurrent++;
+    }
+
+    bool EOS() const { return m_itCurrent == m_Tokens.end(); }
 };
 
-void Reader::Read(UnknownElement& unknown, std::istream& istr) {
-	Reader reader;
+void Reader::Read(UnknownElement &unknown, std::istream &istr)
+{
+    Reader reader;
 
-	Tokens tokens;
-	InputStream inputStream(istr);
-	reader.Scan(tokens, inputStream);
+    Tokens tokens;
+    InputStream inputStream(istr);
+    reader.Scan(tokens, inputStream);
 
-	TokenStream tokenStream(tokens);
-	unknown = reader.Parse(tokenStream);
+    TokenStream tokenStream(tokens);
+    unknown = reader.Parse(tokenStream);
 
-	if (!tokenStream.EOS()) {
-		Token const& token = tokenStream.Peek();
-		throw ParseException("Expected End of token stream; found " + token.sValue, token.locBegin, token.locEnd);
-	}
+    if (!tokenStream.EOS()) {
+        Token const &token = tokenStream.Peek();
+        throw ParseException("Expected End of token stream; found " + token.sValue, token.locBegin, token.locEnd);
+    }
 }
 
-void Reader::Scan(Tokens& tokens, InputStream& inputStream) {
-	while (EatWhiteSpace(inputStream), !inputStream.EOS()) {
-		// if all goes well, we'll create a token each pass
-		Token token;
-		token.locBegin = inputStream.GetLocation();
-
-		char c = inputStream.Peek();
-		switch (c) {
-			case '{':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_OBJECT_BEGIN;
-				break;
-
-			case '}':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_OBJECT_END;
-				break;
-
-			case '[':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_ARRAY_BEGIN;
-				break;
-
-			case ']':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_ARRAY_END;
-				break;
-
-			case ',':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_NEXT_ELEMENT;
-				break;
-
-			case ':':
-				token.sValue = c;
-				inputStream.Get();
-				token.nType = Token::TOKEN_MEMBER_ASSIGN;
-				break;
-
-			case '"':
-				MatchString(token.sValue, inputStream);
-				token.nType = Token::TOKEN_STRING;
-				break;
-
-			case '-':
-			case '0':
-			case '1':
-			case '2':
-			case '3':
-			case '4':
-			case '5':
-			case '6':
-			case '7':
-			case '8':
-			case '9':
-				MatchNumber(token.sValue, inputStream);
-				token.nType = Token::TOKEN_NUMBER;
-				break;
-
-			case 't':
-				token.sValue = "true";
-				MatchExpectedString(token.sValue, inputStream);
-				token.nType = Token::TOKEN_BOOLEAN;
-				break;
-
-			case 'f':
-				token.sValue = "false";
-				MatchExpectedString(token.sValue, inputStream);
-				token.nType = Token::TOKEN_BOOLEAN;
-				break;
-
-			case 'n':
-				token.sValue = "null";
-				MatchExpectedString(token.sValue, inputStream);
-				token.nType = Token::TOKEN_NULL;
-				break;
-
-			case 0:
-				return;
-
-			default:
-				throw ScanException(std::string("Unexpected character in stream: ") + c, inputStream.GetLocation());
-		}
-
-		token.locEnd = inputStream.GetLocation();
-		tokens.push_back(token);
-	}
+void Reader::Scan(Tokens &tokens, InputStream &inputStream)
+{
+    while (EatWhiteSpace(inputStream), !inputStream.EOS()) {
+        // if all goes well, we'll create a token each pass
+        Token token;
+        token.locBegin = inputStream.GetLocation();
+
+        char c = inputStream.Peek();
+        switch (c) {
+        case '{':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_OBJECT_BEGIN;
+            break;
+
+        case '}':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_OBJECT_END;
+            break;
+
+        case '[':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_ARRAY_BEGIN;
+            break;
+
+        case ']':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_ARRAY_END;
+            break;
+
+        case ',':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_NEXT_ELEMENT;
+            break;
+
+        case ':':
+            token.sValue = c;
+            inputStream.Get();
+            token.nType = Token::TOKEN_MEMBER_ASSIGN;
+            break;
+
+        case '"':
+            MatchString(token.sValue, inputStream);
+            token.nType = Token::TOKEN_STRING;
+            break;
+
+        case '-':
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            MatchNumber(token.sValue, inputStream);
+            token.nType = Token::TOKEN_NUMBER;
+            break;
+
+        case 't':
+            token.sValue = "true";
+            MatchExpectedString(token.sValue, inputStream);
+            token.nType = Token::TOKEN_BOOLEAN;
+            break;
+
+        case 'f':
+            token.sValue = "false";
+            MatchExpectedString(token.sValue, inputStream);
+            token.nType = Token::TOKEN_BOOLEAN;
+            break;
+
+        case 'n':
+            token.sValue = "null";
+            MatchExpectedString(token.sValue, inputStream);
+            token.nType = Token::TOKEN_NULL;
+            break;
+
+        case 0:
+            return;
+
+        default:
+            throw ScanException(std::string("Unexpected character in stream: ") + c, inputStream.GetLocation());
+        }
+
+        token.locEnd = inputStream.GetLocation();
+        tokens.push_back(token);
+    }
 }
 
-void Reader::EatWhiteSpace(InputStream& inputStream) {
-	while (!inputStream.EOS() && ::isspace(inputStream.Peek()))
-		inputStream.Get();
+void Reader::EatWhiteSpace(InputStream &inputStream)
+{
+    while (!inputStream.EOS() && ::isspace(inputStream.Peek()))
+        inputStream.Get();
 }
 
-void Reader::MatchExpectedString(std::string const& sExpected, InputStream& inputStream) {
-	for (char c : sExpected) {
-		if (inputStream.EOS() || inputStream.Get() != c)
-			throw ScanException("Expected string: " + sExpected, inputStream.GetLocation());
-	}
+void Reader::MatchExpectedString(std::string const &sExpected, InputStream &inputStream)
+{
+    for (char c : sExpected) {
+        if (inputStream.EOS() || inputStream.Get() != c)
+            throw ScanException("Expected string: " + sExpected, inputStream.GetLocation());
+    }
 }
 
-void Reader::MatchString(std::string& string, InputStream& inputStream) {
-	MatchExpectedString("\"", inputStream);
-
-	while (!inputStream.EOS() && inputStream.Peek() != '"') {
-		char c = inputStream.Get();
-
-		// escape?
-		if (c == '\\' && !inputStream.EOS()) { // shouldn't have reached the end yet
-			c = inputStream.Get();
-			switch (c) {
-				case '/':  string.push_back('/');  break;
-				case '"':  string.push_back('"');  break;
-				case '\\': string.push_back('\\'); break;
-				case 'b':  string.push_back('\b'); break;
-				case 'f':  string.push_back('\f'); break;
-				case 'n':  string.push_back('\n'); break;
-				case 'r':  string.push_back('\r'); break;
-				case 't':  string.push_back('\t'); break;
-				case 'u':  // TODO: what do we do with this?
-				default:
-					throw ScanException(std::string("Unrecognized escape sequence found in string: \\") + c, inputStream.GetLocation());
-			}
-		}
-		else {
-			string.push_back(c);
-		}
-	}
-
-	// eat the last '"' that we hopefully just peeked
-	MatchExpectedString("\"", inputStream);
+void Reader::MatchString(std::string &string, InputStream &inputStream)
+{
+    MatchExpectedString("\"", inputStream);
+
+    while (!inputStream.EOS() && inputStream.Peek() != '"') {
+        char c = inputStream.Get();
+
+        // escape?
+        if (c == '\\' && !inputStream.EOS()) { // shouldn't have reached the end yet
+            c = inputStream.Get();
+            switch (c) {
+            case '/':  string.push_back('/');  break;
+            case '"':  string.push_back('"');  break;
+            case '\\': string.push_back('\\'); break;
+            case 'b':  string.push_back('\b'); break;
+            case 'f':  string.push_back('\f'); break;
+            case 'n':  string.push_back('\n'); break;
+            case 'r':  string.push_back('\r'); break;
+            case 't':  string.push_back('\t'); break;
+            case 'u':  // TODO: what do we do with this?
+            default:
+                throw ScanException(std::string("Unrecognized escape sequence found in string: \\") + c, inputStream.GetLocation());
+            }
+        }
+        else {
+            string.push_back(c);
+        }
+    }
+
+    // eat the last '"' that we hopefully just peeked
+    MatchExpectedString("\"", inputStream);
 }
 
-void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream) {
-	const char numericChars[] = "0123456789.eE-+";
-	while (!inputStream.EOS() && std::find(numericChars, std::end(numericChars), inputStream.Peek()) != std::end(numericChars))
-		sNumber.push_back(inputStream.Get());
+void Reader::MatchNumber(std::string &sNumber, InputStream &inputStream)
+{
+    const char numericChars[] = "0123456789.eE-+";
+    while (!inputStream.EOS() && std::find(numericChars, std::end(numericChars), inputStream.Peek()) != std::end(numericChars))
+        sNumber.push_back(inputStream.Get());
 }
 
-UnknownElement Reader::Parse(Reader::TokenStream& tokenStream) {
-	if (tokenStream.EOS())
-		throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
-
-	Token const& token = tokenStream.Peek();
-	switch (token.nType) {
-		case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
-		case Token::TOKEN_ARRAY_BEGIN:  return ParseArray(tokenStream);
-		case Token::TOKEN_STRING:       return ParseString(tokenStream);
-		case Token::TOKEN_NUMBER:       return ParseNumber(tokenStream);
-		case Token::TOKEN_BOOLEAN:      return ParseBoolean(tokenStream);
-		case Token::TOKEN_NULL:         return ParseNull(tokenStream);
-		default:
-			throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
-	}
+UnknownElement Reader::Parse(Reader::TokenStream &tokenStream)
+{
+    if (tokenStream.EOS())
+        throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
+
+    Token const &token = tokenStream.Peek();
+    switch (token.nType) {
+    case Token::TOKEN_OBJECT_BEGIN: return ParseObject(tokenStream);
+    case Token::TOKEN_ARRAY_BEGIN:  return ParseArray(tokenStream);
+    case Token::TOKEN_STRING:       return ParseString(tokenStream);
+    case Token::TOKEN_NUMBER:       return ParseNumber(tokenStream);
+    case Token::TOKEN_BOOLEAN:      return ParseBoolean(tokenStream);
+    case Token::TOKEN_NULL:         return ParseNull(tokenStream);
+    default:
+        throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
+    }
 }
 
-UnknownElement Reader::ParseObject(Reader::TokenStream& tokenStream) {
-	MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
+UnknownElement Reader::ParseObject(Reader::TokenStream &tokenStream)
+{
+    MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
 
-	Object object;
+    Object object;
 
-	while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END) {
-		// first the member name. save the token in case we have to throw an exception
-		Token const& tokenName = tokenStream.Peek();
-		std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+    while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END) {
+        // first the member name. save the token in case we have to throw an exception
+        Token const &tokenName = tokenStream.Peek();
+        std::string const &name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
 
-		if (object.count(name))
-			throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
+        if (object.count(name))
+            throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
 
-		// ...then the key/value separator...
-		MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
+        // ...then the key/value separator...
+        MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
 
-		// ...then the value itself (can be anything).
-		object[name] = Parse(tokenStream);
+        // ...then the value itself (can be anything).
+        object[name] = Parse(tokenStream);
 
-		if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
-			MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
-	}
+        if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_OBJECT_END)
+            MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+    }
 
-	MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
+    MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
 
-	return std::move(object);
+    return std::move(object);
 }
 
-UnknownElement Reader::ParseArray(Reader::TokenStream& tokenStream) {
-	MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
+UnknownElement Reader::ParseArray(Reader::TokenStream &tokenStream)
+{
+    MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
 
-	Array array;
+    Array array;
 
-	while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END) {
-		array.push_back(Parse(tokenStream));
+    while (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END) {
+        array.push_back(Parse(tokenStream));
 
-		if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
-			MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
-	}
+        if (!tokenStream.EOS() && tokenStream.Peek().nType != Token::TOKEN_ARRAY_END)
+            MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+    }
 
-	MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
+    MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
 
-	return std::move(array);
+    return std::move(array);
 }
 
-UnknownElement Reader::ParseString(Reader::TokenStream& tokenStream) {
-	return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+UnknownElement Reader::ParseString(Reader::TokenStream &tokenStream)
+{
+    return MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
 }
 
-UnknownElement Reader::ParseNumber(Reader::TokenStream& tokenStream) {
-	Token const& currentToken = tokenStream.Peek(); // might need this later for throwing exception
-	std::string const& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
+UnknownElement Reader::ParseNumber(Reader::TokenStream &tokenStream)
+{
+    Token const &currentToken = tokenStream.Peek(); // might need this later for throwing exception
+    std::string const &sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
 
-	// First try to parse it as an int
-	boost::interprocess::ibufferstream iStr(sValue.data(), sValue.size());
-	int64_t iValue;
-	iStr >> iValue;
+    // First try to parse it as an int
+    boost::interprocess::ibufferstream iStr(sValue.data(), sValue.size());
+    int64_t iValue;
+    iStr >> iValue;
 
-	// If the entire token was consumed then it's not a double
-	if (iStr.eof())
-		return iValue;
+    // If the entire token was consumed then it's not a double
+    if (iStr.eof())
+        return iValue;
 
-	// Try again as a double
-	iStr.seekg(0, std::ios::beg);
-	double dValue;
-	iStr >> dValue;
+    // Try again as a double
+    iStr.seekg(0, std::ios::beg);
+    double dValue;
+    iStr >> dValue;
 
-	// If there's still stuff left in the token then it's malformed
-	if (!iStr.eof())
-		throw ParseException(std::string("Unexpected character in NUMBER token: ") + (char)iStr.peek(),
-			currentToken.locBegin, currentToken.locEnd);
+    // If there's still stuff left in the token then it's malformed
+    if (!iStr.eof())
+        throw ParseException(std::string("Unexpected character in NUMBER token: ") + (char)iStr.peek(),
+                             currentToken.locBegin, currentToken.locEnd);
 
-	return dValue;
+    return dValue;
 }
 
-UnknownElement Reader::ParseBoolean(Reader::TokenStream& tokenStream) {
-	return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
+UnknownElement Reader::ParseBoolean(Reader::TokenStream &tokenStream)
+{
+    return MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream) == "true";
 }
 
-UnknownElement Reader::ParseNull(Reader::TokenStream& tokenStream) {
-	MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
-	return Null();
+UnknownElement Reader::ParseNull(Reader::TokenStream &tokenStream)
+{
+    MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
+    return Null();
 }
 
-std::string const& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream) {
-	if (tokenStream.EOS())
-		throw ParseException("Unexpected End of token stream", Location(), Location()); // nowhere to point to
+std::string const &Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream &tokenStream)
+{
+    if (tokenStream.EOS())
+        throw ParseException("Unexpected End of token stream", Location(), Location()); // nowhere to point to
 
-	Token const& token = tokenStream.Get();
-	if (token.nType != nExpected)
-		throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
+    Token const &token = tokenStream.Get();
+    if (token.nType != nExpected)
+        throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
 
-	return token.sValue;
+    return token.sValue;
 }
 
 }
diff --git a/libaegisub/common/cajun/writer.cpp b/libaegisub/common/cajun/writer.cpp
index 6f337d44e6be343a4cd97b843ca87fd6ab166bbd..678810a5b8a554f913e654b89b06713a90b74efa 100644
--- a/libaegisub/common/cajun/writer.cpp
+++ b/libaegisub/common/cajun/writer.cpp
@@ -20,81 +20,85 @@
 #include <iomanip>
 
 namespace agi {
-void JsonWriter::Visit(json::Array const& array) {
-	if (array.empty()) {
-		ostr << "[]";
-		return;
-	}
-
-	indent += '\t';
-	ostr << "[\n";
-
-	bool first = true;
-	for (auto const& entry : array) {
-		if (!first) ostr << ",\n";
-		first = false;
-
-		ostr << indent;
-		Visit(entry);
-	}
-
-	indent.pop_back();
-	ostr << '\n' << indent << ']';
+void JsonWriter::Visit(json::Array const &array)
+{
+    if (array.empty()) {
+        ostr << "[]";
+        return;
+    }
+
+    indent += '\t';
+    ostr << "[\n";
+
+    bool first = true;
+    for (auto const &entry : array) {
+        if (!first) ostr << ",\n";
+        first = false;
+
+        ostr << indent;
+        Visit(entry);
+    }
+
+    indent.pop_back();
+    ostr << '\n' << indent << ']';
 }
 
-void JsonWriter::Visit(json::Object const& object) {
-	if (object.empty()) {
-		ostr << "{}";
-		return;
-	}
-
-	indent += '\t';
-	ostr << "{\n";
-
-	bool first = true;
-	for (auto const& entry : object) {
-		if (!first) ostr << ",\n";
-		first = false;
-
-		ostr << indent;
-		Visit(entry.first);
-		ostr << " : ";
-		Visit(entry.second);
-	}
-
-	indent.pop_back();
-	ostr << '\n' << indent << '}';
+void JsonWriter::Visit(json::Object const &object)
+{
+    if (object.empty()) {
+        ostr << "{}";
+        return;
+    }
+
+    indent += '\t';
+    ostr << "{\n";
+
+    bool first = true;
+    for (auto const &entry : object) {
+        if (!first) ostr << ",\n";
+        first = false;
+
+        ostr << indent;
+        Visit(entry.first);
+        ostr << " : ";
+        Visit(entry.second);
+    }
+
+    indent.pop_back();
+    ostr << '\n' << indent << '}';
 }
 
-void JsonWriter::Visit(double d) {
-	ostr << std::setprecision(20) << d;
+void JsonWriter::Visit(double d)
+{
+    ostr << std::setprecision(20) << d;
 
-	double unused;
-	if (!std::modf(d, &unused))
-		ostr << ".0";
+    double unused;
+    if (!std::modf(d, &unused))
+        ostr << ".0";
 }
 
-void JsonWriter::Visit(std::string const& str) {
-	ostr << '"';
-
-	for (auto c : str) {
-		switch (c) {
-			case '"':  ostr << "\\\""; break;
-			case '\\': ostr << "\\\\"; break;
-			case '\b': ostr << "\\b";  break;
-			case '\f': ostr << "\\f";  break;
-			case '\n': ostr << "\\n";  break;
-			case '\r': ostr << "\\r";  break;
-			case '\t': ostr << "\\t";  break;
-			default:   ostr << c;      break;
-		}
-	}
-
-	ostr << '"';
+void JsonWriter::Visit(std::string const &str)
+{
+    ostr << '"';
+
+    for (auto c : str) {
+        switch (c) {
+        case '"':  ostr << "\\\""; break;
+        case '\\': ostr << "\\\\"; break;
+        case '\b': ostr << "\\b";  break;
+        case '\f': ostr << "\\f";  break;
+        case '\n': ostr << "\\n";  break;
+        case '\r': ostr << "\\r";  break;
+        case '\t': ostr << "\\t";  break;
+        default:   ostr << c;      break;
+        }
+    }
+
+    ostr << '"';
 }
 
 void JsonWriter::Visit(int64_t i) { ostr << i; }
 void JsonWriter::Visit(bool b) { ostr << (b ? "true" : "false"); }
-void JsonWriter::Visit(json::Null const&) { ostr << "null"; }
-void JsonWriter::Visit(json::UnknownElement const& unknown) { unknown.Accept(*this); }
+void JsonWriter::Visit(json::Null const &) { ostr << "null"; }
+void JsonWriter::Visit(json::UnknownElement const &unknown) { unknown.Accept(*this); }
 }
diff --git a/libaegisub/common/calltip_provider.cpp b/libaegisub/common/calltip_provider.cpp
index de693e6377eee0469cb6324c5d565ff3dde546b9..de7aced3947d30299fb4186ec9db13296567c7a7 100644
--- a/libaegisub/common/calltip_provider.cpp
+++ b/libaegisub/common/calltip_provider.cpp
@@ -22,192 +22,194 @@
 
 namespace {
 struct proto_lit {
-	const char *name;
-	const char *args;
+    const char *name;
+    const char *args;
 };
 
 // NOTE: duplicate tag names sorted by number of arguments
 const proto_lit proto_1[] = {
-	{"K", "\\KDuration"},
-	{"a", "\\aAlignment"},
-	{"b", "\\bWeight"},
-	{"c", "\\cColour"},
-	{"i", "\\i1/0"},
-	{"k", "\\kDuration"},
-	{"p", "\\pExponent"},
-	{"q", "\\qWrap Style"},
-	{"r", "\\rStyle"},
-	{"s", "\\s1/0"},
-	{"t", "\\t(Acceleration,Tags)"},
-	{"t", "\\t(Start Time,End Time,Tags)"},
-	{"t", "\\t(Start Time,End Time,Acceleration,Tags)"},
-	{"u", "\\u1/0"},
-	{nullptr, nullptr}
+    {"K", "\\KDuration"},
+    {"a", "\\aAlignment"},
+    {"b", "\\bWeight"},
+    {"c", "\\cColour"},
+    {"i", "\\i1/0"},
+    {"k", "\\kDuration"},
+    {"p", "\\pExponent"},
+    {"q", "\\qWrap Style"},
+    {"r", "\\rStyle"},
+    {"s", "\\s1/0"},
+    {"t", "\\t(Acceleration,Tags)"},
+    {"t", "\\t(Start Time,End Time,Tags)"},
+    {"t", "\\t(Start Time,End Time,Acceleration,Tags)"},
+    {"u", "\\u1/0"},
+    {nullptr, nullptr}
 };
 
 const proto_lit proto_2[] = {
-	{"1a", "\\1aAlpha"},
-	{"1c", "\\1cColour"},
-	{"2a", "\\2aAlpha"},
-	{"2c", "\\2cColour"},
-	{"3a", "\\3aAlpha"},
-	{"3c", "\\3cColour"},
-	{"4a", "\\4aAlpha"},
-	{"4c", "\\4cColour"},
-	{"an", "\\anAlignment"},
-	{"be", "\\beStrength"},
-	{"fe", "\\feEncoding"},
-	{"fn", "\\fnFont Name"},
-	{"fr", "\\frAngle"},
-	{"fs", "\\fsFont Size"},
-	{"kf", "\\kfDuration"},
-	{"ko", "\\koDuration"},
-	{nullptr, nullptr}
+    {"1a", "\\1aAlpha"},
+    {"1c", "\\1cColour"},
+    {"2a", "\\2aAlpha"},
+    {"2c", "\\2cColour"},
+    {"3a", "\\3aAlpha"},
+    {"3c", "\\3cColour"},
+    {"4a", "\\4aAlpha"},
+    {"4c", "\\4cColour"},
+    {"an", "\\anAlignment"},
+    {"be", "\\beStrength"},
+    {"fe", "\\feEncoding"},
+    {"fn", "\\fnFont Name"},
+    {"fr", "\\frAngle"},
+    {"fs", "\\fsFont Size"},
+    {"kf", "\\kfDuration"},
+    {"ko", "\\koDuration"},
+    {nullptr, nullptr}
 };
 
 const proto_lit proto_3[] = {
-	{"fax", "\\faxFactor"},
-	{"fay", "\\fayFactor"},
-	{"frx", "\\frxAngle"},
-	{"fry", "\\fryAngle"},
-	{"frz", "\\frzAngle"},
-	{"fsp", "\\fspSpacing"},
-	{"org", "\\org(X,Y)"},
-	{"pbo", "\\pboOffset"},
-	{"pos", "\\pos(X,Y)"},
-	{nullptr, nullptr}
+    {"fax", "\\faxFactor"},
+    {"fay", "\\fayFactor"},
+    {"frx", "\\frxAngle"},
+    {"fry", "\\fryAngle"},
+    {"frz", "\\frzAngle"},
+    {"fsp", "\\fspSpacing"},
+    {"org", "\\org(X,Y)"},
+    {"pbo", "\\pboOffset"},
+    {"pos", "\\pos(X,Y)"},
+    {nullptr, nullptr}
 };
 
 const proto_lit proto_4[] = {
-	{"blur", "\\blurStrength"},
-	{"bord", "\\bordWidth"},
-	{"clip", "\\clip(Command)"},
-	{"clip", "\\clip(Scale,Command)"},
-	{"clip", "\\clip(X1,Y1,X2,Y2)"},
-	{"fad", "\\fad(Start Time,End Time)"},
-	{"fade", "\\fade(Start Alpha,Middle Alpha,End Alpha,Start In,End In,Start Out,End Out)"},
-	{"fscx", "\\fscxScale"},
-	{"fscy", "\\fscyScale"},
-	{"move", "\\move(X1,Y1,X2,Y2)"},
-	{"move", "\\move(X1,Y1,X2,Y2,Start Time,End Time)"},
-	{"shad", "\\shadDepth"},
-	{nullptr, nullptr}
+    {"blur", "\\blurStrength"},
+    {"bord", "\\bordWidth"},
+    {"clip", "\\clip(Command)"},
+    {"clip", "\\clip(Scale,Command)"},
+    {"clip", "\\clip(X1,Y1,X2,Y2)"},
+    {"fad", "\\fad(Start Time,End Time)"},
+    {"fade", "\\fade(Start Alpha,Middle Alpha,End Alpha,Start In,End In,Start Out,End Out)"},
+    {"fscx", "\\fscxScale"},
+    {"fscy", "\\fscyScale"},
+    {"move", "\\move(X1,Y1,X2,Y2)"},
+    {"move", "\\move(X1,Y1,X2,Y2,Start Time,End Time)"},
+    {"shad", "\\shadDepth"},
+    {nullptr, nullptr}
 };
 
 const proto_lit proto_5[] = {
-	{"alpha", "\\alphaAlpha"},
-	{"iclip", "\\iclip(Command)"},
-	{"iclip", "\\iclip(Scale,Command)"},
-	{"iclip", "\\iclip(X1,Y1,X2,Y2)"},
-	{"xbord", "\\xbordWidth"},
-	{"xshad", "\\xshadDepth"},
-	{"ybord", "\\ybordWidth"},
-	{"yshad", "\\yshadDepth"},
-	{nullptr, nullptr}
+    {"alpha", "\\alphaAlpha"},
+    {"iclip", "\\iclip(Command)"},
+    {"iclip", "\\iclip(Scale,Command)"},
+    {"iclip", "\\iclip(X1,Y1,X2,Y2)"},
+    {"xbord", "\\xbordWidth"},
+    {"xshad", "\\xshadDepth"},
+    {"ybord", "\\ybordWidth"},
+    {"yshad", "\\yshadDepth"},
+    {nullptr, nullptr}
 };
 
 const proto_lit *all_protos[] = {proto_1, proto_2, proto_3, proto_4, proto_5};
 }
 
 namespace agi {
-Calltip GetCalltip(std::vector<ass::DialogueToken> const& tokens, std::string const& text, size_t pos) {
-	namespace dt = ass::DialogueTokenType;
-
-	Calltip ret = { nullptr, 0, 0, 0 };
-
-	size_t idx = 0;
-	size_t tag_name_idx = 0;
-	size_t commas = 0;
-	for (; idx < tokens.size() && pos > 0; ++idx) {
-		switch (tokens[idx].type) {
-			case dt::COMMENT:
-			case dt::OVR_END:
-				tag_name_idx = 0;
-				break;
-			case dt::TAG_NAME:
-				tag_name_idx = idx;
-				commas = 0;
-				break;
-			case dt::ARG_SEP:
-				++commas;
-				break;
-			default: break;
-		}
-		pos -= std::min(pos, tokens[idx].length);
-	}
-
-	// Either didn't hit a tag or the override block ended before reaching the
-	// current position
-	if (tag_name_idx == 0)
-		return ret;
-
-	size_t tag_name_start = 0;
-	for (size_t i = 0; i < tag_name_idx; ++i)
-		tag_name_start += tokens[i].length;
-	size_t tag_name_length = tokens[tag_name_idx].length;
-
-	// No tags exist with length over five
-	if (tag_name_length > 5)
-		return ret;
-
-	auto valid = [&](const proto_lit *it) {
-		return it->name && strncmp(it->name, &text[tag_name_start], tag_name_length) == 0;
-	};
-
-	// Find the prototype for this tag
-	auto proto = all_protos[tag_name_length - 1];
-	while (proto->name && strncmp(proto->name, &text[tag_name_start], tag_name_length) < 0)
-		++proto;
-
-	if (!valid(proto))
-		return ret;
-
-	// If there's multiple overloads, check how many total arguments we have
-	// and pick the one with the least args >= current arg count
-	if (valid(proto + 1)) {
-		size_t args = commas + 1;
-		for (size_t i = idx + 1; i < tokens.size(); ++i) {
-			int type = tokens[i].type;
-			if (type == dt::ARG_SEP)
-				++args;
-			else if (type != dt::ARG && type != dt::WHITESPACE)
-				break;
-		}
-
-		auto arg_count = [](const proto_lit *it) -> size_t {
-			size_t count = 1;
-			for (const char *s = it->args; *s; ++s) {
-				if (*s == ',') ++count;
-			}
-			return count;
-		};
-
-		while (valid(proto + 1) && args > arg_count(proto))
-			++proto;
-	}
-
-	ret.highlight_start = tag_name_length + 1;
-	if (proto->args[ret.highlight_start] != '(')
-		ret.highlight_end = strlen(proto->args);
-	else {
-		auto start = proto->args + tag_name_length + 2; // One for slash, one for open paren
-		for (; commas > 0; --commas) {
-			start = strchr(start, ',');
-			if (!start) return ret; // No calltip if we're after the last arg
-			++start;
-		}
-
-		ret.highlight_start = std::distance(proto->args, start);
-		const char *end = strchr(start, ',');
-		if (end)
-			ret.highlight_end = std::distance(start, end) + ret.highlight_start;
-		else
-			ret.highlight_end = strlen(proto->args) - 1; // -1 for close paren
-	}
-
-	ret.text = proto->args;
-	ret.tag_position = tag_name_start;
-
-	return ret;
+Calltip GetCalltip(std::vector<ass::DialogueToken> const &tokens, std::string const &text, size_t pos)
+{
+    namespace dt = ass::DialogueTokenType;
+
+    Calltip ret = { nullptr, 0, 0, 0 };
+
+    size_t idx = 0;
+    size_t tag_name_idx = 0;
+    size_t commas = 0;
+    for (; idx < tokens.size() && pos > 0; ++idx) {
+        switch (tokens[idx].type) {
+        case dt::COMMENT:
+        case dt::OVR_END:
+            tag_name_idx = 0;
+            break;
+        case dt::TAG_NAME:
+            tag_name_idx = idx;
+            commas = 0;
+            break;
+        case dt::ARG_SEP:
+            ++commas;
+            break;
+        default: break;
+        }
+        pos -= std::min(pos, tokens[idx].length);
+    }
+
+    // Either didn't hit a tag or the override block ended before reaching the
+    // current position
+    if (tag_name_idx == 0)
+        return ret;
+
+    size_t tag_name_start = 0;
+    for (size_t i = 0; i < tag_name_idx; ++i)
+        tag_name_start += tokens[i].length;
+    size_t tag_name_length = tokens[tag_name_idx].length;
+
+    // No tags exist with length over five
+    if (tag_name_length > 5)
+        return ret;
+
+    auto valid = [&](const proto_lit * it) {
+        return it->name && strncmp(it->name, &text[tag_name_start], tag_name_length) == 0;
+    };
+
+    // Find the prototype for this tag
+    auto proto = all_protos[tag_name_length - 1];
+    while (proto->name && strncmp(proto->name, &text[tag_name_start], tag_name_length) < 0)
+        ++proto;
+
+    if (!valid(proto))
+        return ret;
+
+    // If there's multiple overloads, check how many total arguments we have
+    // and pick the one with the least args >= current arg count
+    if (valid(proto + 1)) {
+        size_t args = commas + 1;
+        for (size_t i = idx + 1; i < tokens.size(); ++i) {
+            int type = tokens[i].type;
+            if (type == dt::ARG_SEP)
+                ++args;
+            else if (type != dt::ARG && type != dt::WHITESPACE)
+                break;
+        }
+
+        auto arg_count = [](const proto_lit * it) -> size_t {
+            size_t count = 1;
+            for (const char *s = it->args; *s; ++s)
+            {
+                if (*s == ',') ++count;
+            }
+            return count;
+        };
+
+        while (valid(proto + 1) && args > arg_count(proto))
+            ++proto;
+    }
+
+    ret.highlight_start = tag_name_length + 1;
+    if (proto->args[ret.highlight_start] != '(')
+        ret.highlight_end = strlen(proto->args);
+    else {
+        auto start = proto->args + tag_name_length + 2; // One for slash, one for open paren
+        for (; commas > 0; --commas) {
+            start = strchr(start, ',');
+            if (!start) return ret; // No calltip if we're after the last arg
+            ++start;
+        }
+
+        ret.highlight_start = std::distance(proto->args, start);
+        const char *end = strchr(start, ',');
+        if (end)
+            ret.highlight_end = std::distance(start, end) + ret.highlight_start;
+        else
+            ret.highlight_end = strlen(proto->args) - 1; // -1 for close paren
+    }
+
+    ret.text = proto->args;
+    ret.tag_position = tag_name_start;
+
+    return ret;
 }
 }
diff --git a/libaegisub/common/character_count.cpp b/libaegisub/common/character_count.cpp
index 4563157a181b6094b5c9ea9e041a57c542a9c1fe..fca6c01ec66a3990da8e8b33fb23d2235bc74d0c 100644
--- a/libaegisub/common/character_count.cpp
+++ b/libaegisub/common/character_count.cpp
@@ -27,143 +27,150 @@
 
 namespace {
 struct utext_deleter {
-	void operator()(UText *ut) { if (ut) utext_close(ut); }
+    void operator()(UText *ut) { if (ut) utext_close(ut); }
 };
 using utext_ptr = std::unique_ptr<UText, utext_deleter>;
 
 UChar32 ass_special_chars[] = {'n', 'N', 'h'};
 
-icu::BreakIterator& get_break_iterator(const char *ptr, size_t len) {
-	static std::unique_ptr<icu::BreakIterator> bi;
-	static std::once_flag token;
-	std::call_once(token, [&] {
-		UErrorCode status = U_ZERO_ERROR;
-		bi.reset(icu::BreakIterator::createCharacterInstance(icu::Locale::getDefault(), status));
-		if (U_FAILURE(status)) throw agi::InternalError("Failed to create character iterator");
-	});
+icu::BreakIterator &get_break_iterator(const char *ptr, size_t len)
+{
+    static std::unique_ptr<icu::BreakIterator> bi;
+    static std::once_flag token;
+    std::call_once(token, [&] {
+        UErrorCode status = U_ZERO_ERROR;
+        bi.reset(icu::BreakIterator::createCharacterInstance(icu::Locale::getDefault(), status));
+        if (U_FAILURE(status)) throw agi::InternalError("Failed to create character iterator");
+    });
 
-	UErrorCode err = U_ZERO_ERROR;
-	utext_ptr ut(utext_openUTF8(nullptr, ptr, len, &err));
-	if (U_FAILURE(err)) throw agi::InternalError("Failed to open utext");
+    UErrorCode err = U_ZERO_ERROR;
+    utext_ptr ut(utext_openUTF8(nullptr, ptr, len, &err));
+    if (U_FAILURE(err)) throw agi::InternalError("Failed to open utext");
 
-	bi->setText(ut.get(), err);
-	if (U_FAILURE(err)) throw agi::InternalError("Failed to set break iterator text");
+    bi->setText(ut.get(), err);
+    if (U_FAILURE(err)) throw agi::InternalError("Failed to set break iterator text");
 
-	return *bi;
+    return *bi;
 }
 
 template <typename Iterator>
-size_t count_in_range(Iterator begin, Iterator end, int mask) {
-	if (begin == end) return 0;
-
-	auto& character_bi = get_break_iterator(&*begin, end - begin);
-
-	size_t count = 0;
-	auto pos = character_bi.first();
-	for (auto end = character_bi.next(); end != icu::BreakIterator::DONE; pos = end, end = character_bi.next()) {
-		if (!mask)
-			++count;
-		else {
-			UChar32 c;
-			int i = 0;
-			U8_NEXT_UNSAFE(begin + pos, i, c);
-			if ((U_GET_GC_MASK(c) & mask) == 0) {
-				if (mask & U_GC_Z_MASK && pos != 0) {
-					UChar32 *result = std::find(std::begin(ass_special_chars), std::end(ass_special_chars), c);
-					if (result != std::end(ass_special_chars)) {
-						UChar32 c2;
-						i = 0;
-						U8_PREV_UNSAFE(begin + pos, i, c2);
-						if (c2 != (UChar32) '\\')
-							++count;
-						else if (!(mask & U_GC_P_MASK))
-							--count;
-					}
-					else
-						++count;
-				}
-				else
-					++count;
-			}
-		}
-	}
-	return count;
+size_t count_in_range(Iterator begin, Iterator end, int mask)
+{
+    if (begin == end) return 0;
+
+    auto &character_bi = get_break_iterator(&*begin, end - begin);
+
+    size_t count = 0;
+    auto pos = character_bi.first();
+    for (auto end = character_bi.next(); end != icu::BreakIterator::DONE; pos = end, end = character_bi.next()) {
+        if (!mask)
+            ++count;
+        else {
+            UChar32 c;
+            int i = 0;
+            U8_NEXT_UNSAFE(begin + pos, i, c);
+            if ((U_GET_GC_MASK(c) & mask) == 0) {
+                if (mask & U_GC_Z_MASK && pos != 0) {
+                    UChar32 *result = std::find(std::begin(ass_special_chars), std::end(ass_special_chars), c);
+                    if (result != std::end(ass_special_chars)) {
+                        UChar32 c2;
+                        i = 0;
+                        U8_PREV_UNSAFE(begin + pos, i, c2);
+                        if (c2 != (UChar32) '\\')
+                            ++count;
+                        else if (!(mask & U_GC_P_MASK))
+                            --count;
+                    }
+                    else
+                        ++count;
+                }
+                else
+                    ++count;
+            }
+        }
+    }
+    return count;
 }
 
-int ignore_mask_to_icu_mask(int mask) {
-	int ret = 0;
-	if (mask & agi::IGNORE_PUNCTUATION)
-		ret |= U_GC_P_MASK;
-	if (mask & agi::IGNORE_WHITESPACE)
-		ret |= U_GC_Z_MASK;
-	return ret;
+int ignore_mask_to_icu_mask(int mask)
+{
+    int ret = 0;
+    if (mask & agi::IGNORE_PUNCTUATION)
+        ret |= U_GC_P_MASK;
+    if (mask & agi::IGNORE_WHITESPACE)
+        ret |= U_GC_Z_MASK;
+    return ret;
 }
 }
 
 namespace agi {
-size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, int ignore) {
-	int mask = ignore_mask_to_icu_mask(ignore);
-	if ((ignore & agi::IGNORE_BLOCKS) == 0)
-		return count_in_range(begin, end, mask);
-
-	size_t characters = 0;
-	auto pos = begin;
-	do {
-		auto it = std::find(pos, end, '{');
-		characters += count_in_range(pos, it, mask);
-		if (it == end) break;
-
-		pos = std::find(pos, end, '}');
-		if (pos == end) {
-			characters += count_in_range(it, pos, mask);
-			break;
-		}
-	} while (++pos != end);
-
-	return characters;
+size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, int ignore)
+{
+    int mask = ignore_mask_to_icu_mask(ignore);
+    if ((ignore & agi::IGNORE_BLOCKS) == 0)
+        return count_in_range(begin, end, mask);
+
+    size_t characters = 0;
+    auto pos = begin;
+    do {
+        auto it = std::find(pos, end, '{');
+        characters += count_in_range(pos, it, mask);
+        if (it == end) break;
+
+        pos = std::find(pos, end, '}');
+        if (pos == end) {
+            characters += count_in_range(it, pos, mask);
+            break;
+        }
+    } while (++pos != end);
+
+    return characters;
 }
 
-size_t CharacterCount(std::string const& str, int mask) {
-	return CharacterCount(begin(str), end(str), mask);
+size_t CharacterCount(std::string const &str, int mask)
+{
+    return CharacterCount(begin(str), end(str), mask);
 }
 
-size_t MaxLineLength(std::string const& text, int mask) {
-	mask = ignore_mask_to_icu_mask(mask);
-	auto tokens = agi::ass::TokenizeDialogueBody(text);
-	agi::ass::MarkDrawings(text, tokens);
-
-	size_t pos = 0;
-	size_t max_line_length = 0;
-	size_t current_line_length = 0;
-	for (auto token : tokens) {
-		if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) {
-			if (text[pos + 1] == 'h') {
-				if (!(mask & U_GC_Z_MASK))
-					current_line_length += 1;
-			}
-			else { // N or n
-				max_line_length = std::max(max_line_length, current_line_length);
-				current_line_length = 0;
-			}
-		}
-		else if (token.type == agi::ass::DialogueTokenType::TEXT)
-			current_line_length += count_in_range(begin(text) + pos, begin(text) + pos + token.length, mask);
-
-		pos += token.length;
-	}
-
-	return std::max(max_line_length, current_line_length);
+size_t MaxLineLength(std::string const &text, int mask)
+{
+    mask = ignore_mask_to_icu_mask(mask);
+    auto tokens = agi::ass::TokenizeDialogueBody(text);
+    agi::ass::MarkDrawings(text, tokens);
+
+    size_t pos = 0;
+    size_t max_line_length = 0;
+    size_t current_line_length = 0;
+    for (auto token : tokens) {
+        if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) {
+            if (text[pos + 1] == 'h') {
+                if (!(mask & U_GC_Z_MASK))
+                    current_line_length += 1;
+            }
+            else { // N or n
+                max_line_length = std::max(max_line_length, current_line_length);
+                current_line_length = 0;
+            }
+        }
+        else if (token.type == agi::ass::DialogueTokenType::TEXT)
+            current_line_length += count_in_range(begin(text) + pos, begin(text) + pos + token.length, mask);
+
+        pos += token.length;
+    }
+
+    return std::max(max_line_length, current_line_length);
 }
 
-size_t IndexOfCharacter(std::string const& str, size_t n) {
-	if (str.empty() || n == 0) return 0;
-	auto& bi = get_break_iterator(&str[0], str.size());
-
-	for (auto pos = bi.first(), end = bi.next(); ; --n, pos = end, end = bi.next()) {
-		if (end == icu::BreakIterator::DONE)
-			return str.size();
-		if (n == 0)
-			return pos;
-	}
+size_t IndexOfCharacter(std::string const &str, size_t n)
+{
+    if (str.empty() || n == 0) return 0;
+    auto &bi = get_break_iterator(&str[0], str.size());
+
+    for (auto pos = bi.first(), end = bi.next(); ; --n, pos = end, end = bi.next()) {
+        if (end == icu::BreakIterator::DONE)
+            return str.size();
+        if (n == 0)
+            return pos;
+    }
 }
 }
diff --git a/libaegisub/common/charset.cpp b/libaegisub/common/charset.cpp
index 1addb90ee6b4b60f58cbb9cf356f215a66f896e7..8f46536a8f9dd9b097dfeca47d2cb8c656736bdb 100644
--- a/libaegisub/common/charset.cpp
+++ b/libaegisub/common/charset.cpp
@@ -25,50 +25,53 @@
 #include <uchardet/uchardet.h>
 #endif
 
-namespace agi { namespace charset {
-std::string Detect(agi::fs::path const& file) {
-	agi::read_file_mapping fp(file);
+namespace agi {
+namespace charset {
+std::string Detect(agi::fs::path const &file)
+{
+    agi::read_file_mapping fp(file);
 
-	// 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
-	if (fp.size() > 100 * 1024 * 1024)
-		return "binary";
+    // 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
+    if (fp.size() > 100 * 1024 * 1024)
+        return "binary";
 
-	uint64_t binaryish = 0;
+    uint64_t binaryish = 0;
 
 #ifdef WITH_UCHARDET
-	agi::scoped_holder<uchardet_t> ud(uchardet_new(), uchardet_delete);
-	for (uint64_t offset = 0; offset < fp.size(); ) {
-		auto read = std::min<uint64_t>(4096, fp.size() - offset);
-		auto buf = fp.read(offset, read);
-		uchardet_handle_data(ud, buf, read);
-		uchardet_data_end(ud);
-		if (*uchardet_get_charset(ud))
-			return uchardet_get_charset(ud);
+    agi::scoped_holder<uchardet_t> ud(uchardet_new(), uchardet_delete);
+    for (uint64_t offset = 0; offset < fp.size(); ) {
+        auto read = std::min<uint64_t>(4096, fp.size() - offset);
+        auto buf = fp.read(offset, read);
+        uchardet_handle_data(ud, buf, read);
+        uchardet_data_end(ud);
+        if (*uchardet_get_charset(ud))
+            return uchardet_get_charset(ud);
 
-		offset += read;
+        offset += read;
 
-		// A dumb heuristic to detect binary files
-		for (size_t i = 0; i < read; ++i) {
-			if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
-				++binaryish;
-		}
+        // A dumb heuristic to detect binary files
+        for (size_t i = 0; i < read; ++i) {
+            if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
+                ++binaryish;
+        }
 
-		if (binaryish > offset / 8)
-			return "binary";
-	}
-	return uchardet_get_charset(ud);
+        if (binaryish > offset / 8)
+            return "binary";
+    }
+    return uchardet_get_charset(ud);
 #else
-	auto read = std::min<uint64_t>(4096, fp.size());
-	auto buf = fp.read(0, read);
-	for (size_t i = 0; i < read; ++i) {
-		if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
-			++binaryish;
-	}
+    auto read = std::min<uint64_t>(4096, fp.size());
+    auto buf = fp.read(0, read);
+    for (size_t i = 0; i < read; ++i) {
+        if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
+            ++binaryish;
+    }
 
-	if (binaryish > read / 8)
-		return "binary";
-	return "utf-8";
+    if (binaryish > read / 8)
+        return "binary";
+    return "utf-8";
 #endif
 }
-} }
+}
+}
diff --git a/libaegisub/common/charset_6937.cpp b/libaegisub/common/charset_6937.cpp
index c0e446a14e60253e7e590e48eeede692c91f49df..c0c1988b3f1172f212b5e31724716704a413019a 100644
--- a/libaegisub/common/charset_6937.cpp
+++ b/libaegisub/common/charset_6937.cpp
@@ -27,147 +27,152 @@ namespace {
 
 // ISO-6937-2 values for the first 383 codepoints
 const int iso6937_codepoints[] = {
-	0x00,   0x01,   0x02,   0x03,   0x04,
-	0x05,   0x06,   0x07,   0x08,   0x09,
-	0x0A,   0x0B,   0x0C,   0x0D,   0x0E,
-	0x0F,   0x10,   0x11,   0x12,   0x13,
-	0x14,   0x15,   0x16,   0x17,   0x18,
-	0x19,   0x1A,   0x1B,   0x1C,   0x1D,
-	0x1E,   0x1F,   0x20,   0x21,   0x22,
-	0x23,   0x24,   0x25,   0x26,   0x27,
-	0x28,   0x29,   0x2A,   0x2B,   0x2C,
-	0x2D,   0x2E,   0x2F,   0x30,   0x31,
-	0x32,   0x33,   0x34,   0x35,   0x36,
-	0x37,   0x38,   0x39,   0x3A,   0x3B,
-	0x3C,   0x3D,   0x3E,   0x3F,   0x40,
-	0x41,   0x42,   0x43,   0x44,   0x45,
-	0x46,   0x47,   0x48,   0x49,   0x4A,
-	0x4B,   0x4C,   0x4D,   0x4E,   0x4F,
-	0x50,   0x51,   0x52,   0x53,   0x54,
-	0x55,   0x56,   0x57,   0x58,   0x59,
-	0x5A,   0x5B,   0x5C,   0x5D,   0x5E,
-	0x5F,   0x60,   0x61,   0x62,   0x63,
-	0x64,   0x65,   0x66,   0x67,   0x68,
-	0x69,   0x6A,   0x6B,   0x6C,   0x6D,
-	0x6E,   0x6F,   0x70,   0x71,   0x72,
-	0x73,   0x74,   0x75,   0x76,   0x77,
-	0x78,   0x79,   0x7A,   0x7B,   0x7C,
-	0x7D,   0x7E,   0x7F,   0x80,   0x81,
-	0x82,   0x83,   0x84,   0x85,   0x86,
-	0x87,   0x88,   0x89,   0x8A,   0x8B,
-	0x8C,   0x8D,   0x8E,   0x8F,   0x90,
-	0x91,   0x92,   0x93,   0x94,   0x95,
-	0x96,   0x97,   0x98,   0x99,   0x9A,
-	0x9B,   0x9C,   0x9D,   0x9E,   0x9F,
-	0xA0,   0xA1,   0xA2,   0xA3,   0xA8,
-	0xA5,   0x00,   0xA7,   0xC820, 0xD3,
-	0xE3,   0xAB,   0x00,   0x00,   0xD2,
-	0xC520, 0xB0,   0xB1,   0xB2,   0xB3,
-	0xC220, 0xB5,   0xB6,   0xB7,   0xCB20,
-	0xD1,   0xEB,   0xBB,   0xBC,   0xBD,
-	0xBE,   0xBF,   0xC141, 0xC241, 0xC341,
-	0xC441, 0xC841, 0xCA41, 0xE1,   0xCB43,
-	0xC145, 0xC245, 0xC345, 0xC845, 0xC149,
-	0xC249, 0xC349, 0xC849, 0xE2,   0xC44E,
-	0xC14F, 0xC24F, 0xC34F, 0xC44F, 0xC84F,
-	0xB4,   0xE9,   0xC155, 0xC255, 0xC355,
-	0xC855, 0xC259, 0xEC,   0xFB,   0xC161,
-	0xC261, 0xC361, 0xC461, 0xC861, 0xCA61,
-	0xF1,   0xCB63, 0xC165, 0xC265, 0xC365,
-	0xC865, 0xC169, 0xC269, 0xC369, 0xC869,
-	0xF3,   0xC46E, 0xC16F, 0xC26F, 0xC36F,
-	0xC46F, 0xC86F, 0xB8,   0xF9,   0xC175,
-	0xC275, 0xC375, 0xC875, 0xC279, 0xFC,
-	0xC879, 0xC541, 0xC561, 0xC641, 0xC661,
-	0xCE41, 0xCE61, 0xC243, 0xC263, 0xC343,
-	0xC363, 0xC743, 0xC763, 0xCF43, 0xCF63,
-	0xCF44, 0xCF64, 0x00,   0xF2,   0xC545,
-	0xC565, 0x00,   0x00,   0xC745, 0xC765,
-	0xCE45, 0xCE65, 0xCF45, 0xCF65, 0xC347,
-	0xC367, 0xC647, 0xC667, 0xC747, 0xC767,
-	0xCB47, 0xCB67, 0xC348, 0xC368, 0xE4,
-	0xF4,   0xC449, 0xC469, 0xC549, 0xC569,
-	0x00,   0x00,   0xCE49, 0xCE69, 0xC749,
-	0xF5,   0xE6,   0xF6,   0xC34A, 0xC36A,
-	0xCB4B, 0xCB6B, 0xF0,   0xC24C, 0xC26C,
-	0xCB4C, 0xCB6C, 0xCF4C, 0xCF6C, 0xE7,
-	0xF7,   0xE8,   0xF8,   0xC24E, 0xC26E,
-	0xCB4E, 0xCB6E, 0xCF4E, 0xCF6E, 0xEF,
-	0xEE,   0xFE,   0xC54F, 0xC56F, 0x00,
-	0x00,   0xCD4F, 0xCD6F, 0xEA,   0xFA,
-	0xC252, 0xC272, 0xCB52, 0xCB72, 0xCF52,
-	0xCF72, 0xC253, 0xC273, 0xC353, 0xC373,
-	0xCB53, 0xCB73, 0xCF53, 0xCF73, 0xCB54,
-	0xCB74, 0xCF54, 0xCF74, 0xED,   0xFD,
-	0xC455, 0xC475, 0xC555, 0xC575, 0xC655,
-	0xC675, 0xCA55, 0xCA75, 0xCD55, 0xCD75,
-	0xCE55, 0xCE75, 0xC357, 0xC377, 0xC359,
-	0xC379, 0xC859, 0xC25A, 0xC27A, 0xC75A,
-	0xC77A, 0xCF5A, 0xCF7A
+    0x00,   0x01,   0x02,   0x03,   0x04,
+    0x05,   0x06,   0x07,   0x08,   0x09,
+    0x0A,   0x0B,   0x0C,   0x0D,   0x0E,
+    0x0F,   0x10,   0x11,   0x12,   0x13,
+    0x14,   0x15,   0x16,   0x17,   0x18,
+    0x19,   0x1A,   0x1B,   0x1C,   0x1D,
+    0x1E,   0x1F,   0x20,   0x21,   0x22,
+    0x23,   0x24,   0x25,   0x26,   0x27,
+    0x28,   0x29,   0x2A,   0x2B,   0x2C,
+    0x2D,   0x2E,   0x2F,   0x30,   0x31,
+    0x32,   0x33,   0x34,   0x35,   0x36,
+    0x37,   0x38,   0x39,   0x3A,   0x3B,
+    0x3C,   0x3D,   0x3E,   0x3F,   0x40,
+    0x41,   0x42,   0x43,   0x44,   0x45,
+    0x46,   0x47,   0x48,   0x49,   0x4A,
+    0x4B,   0x4C,   0x4D,   0x4E,   0x4F,
+    0x50,   0x51,   0x52,   0x53,   0x54,
+    0x55,   0x56,   0x57,   0x58,   0x59,
+    0x5A,   0x5B,   0x5C,   0x5D,   0x5E,
+    0x5F,   0x60,   0x61,   0x62,   0x63,
+    0x64,   0x65,   0x66,   0x67,   0x68,
+    0x69,   0x6A,   0x6B,   0x6C,   0x6D,
+    0x6E,   0x6F,   0x70,   0x71,   0x72,
+    0x73,   0x74,   0x75,   0x76,   0x77,
+    0x78,   0x79,   0x7A,   0x7B,   0x7C,
+    0x7D,   0x7E,   0x7F,   0x80,   0x81,
+    0x82,   0x83,   0x84,   0x85,   0x86,
+    0x87,   0x88,   0x89,   0x8A,   0x8B,
+    0x8C,   0x8D,   0x8E,   0x8F,   0x90,
+    0x91,   0x92,   0x93,   0x94,   0x95,
+    0x96,   0x97,   0x98,   0x99,   0x9A,
+    0x9B,   0x9C,   0x9D,   0x9E,   0x9F,
+    0xA0,   0xA1,   0xA2,   0xA3,   0xA8,
+    0xA5,   0x00,   0xA7,   0xC820, 0xD3,
+    0xE3,   0xAB,   0x00,   0x00,   0xD2,
+    0xC520, 0xB0,   0xB1,   0xB2,   0xB3,
+    0xC220, 0xB5,   0xB6,   0xB7,   0xCB20,
+    0xD1,   0xEB,   0xBB,   0xBC,   0xBD,
+    0xBE,   0xBF,   0xC141, 0xC241, 0xC341,
+    0xC441, 0xC841, 0xCA41, 0xE1,   0xCB43,
+    0xC145, 0xC245, 0xC345, 0xC845, 0xC149,
+    0xC249, 0xC349, 0xC849, 0xE2,   0xC44E,
+    0xC14F, 0xC24F, 0xC34F, 0xC44F, 0xC84F,
+    0xB4,   0xE9,   0xC155, 0xC255, 0xC355,
+    0xC855, 0xC259, 0xEC,   0xFB,   0xC161,
+    0xC261, 0xC361, 0xC461, 0xC861, 0xCA61,
+    0xF1,   0xCB63, 0xC165, 0xC265, 0xC365,
+    0xC865, 0xC169, 0xC269, 0xC369, 0xC869,
+    0xF3,   0xC46E, 0xC16F, 0xC26F, 0xC36F,
+    0xC46F, 0xC86F, 0xB8,   0xF9,   0xC175,
+    0xC275, 0xC375, 0xC875, 0xC279, 0xFC,
+    0xC879, 0xC541, 0xC561, 0xC641, 0xC661,
+    0xCE41, 0xCE61, 0xC243, 0xC263, 0xC343,
+    0xC363, 0xC743, 0xC763, 0xCF43, 0xCF63,
+    0xCF44, 0xCF64, 0x00,   0xF2,   0xC545,
+    0xC565, 0x00,   0x00,   0xC745, 0xC765,
+    0xCE45, 0xCE65, 0xCF45, 0xCF65, 0xC347,
+    0xC367, 0xC647, 0xC667, 0xC747, 0xC767,
+    0xCB47, 0xCB67, 0xC348, 0xC368, 0xE4,
+    0xF4,   0xC449, 0xC469, 0xC549, 0xC569,
+    0x00,   0x00,   0xCE49, 0xCE69, 0xC749,
+    0xF5,   0xE6,   0xF6,   0xC34A, 0xC36A,
+    0xCB4B, 0xCB6B, 0xF0,   0xC24C, 0xC26C,
+    0xCB4C, 0xCB6C, 0xCF4C, 0xCF6C, 0xE7,
+    0xF7,   0xE8,   0xF8,   0xC24E, 0xC26E,
+    0xCB4E, 0xCB6E, 0xCF4E, 0xCF6E, 0xEF,
+    0xEE,   0xFE,   0xC54F, 0xC56F, 0x00,
+    0x00,   0xCD4F, 0xCD6F, 0xEA,   0xFA,
+    0xC252, 0xC272, 0xCB52, 0xCB72, 0xCF52,
+    0xCF72, 0xC253, 0xC273, 0xC353, 0xC373,
+    0xCB53, 0xCB73, 0xCF53, 0xCF73, 0xCB54,
+    0xCB74, 0xCF54, 0xCF74, 0xED,   0xFD,
+    0xC455, 0xC475, 0xC555, 0xC575, 0xC655,
+    0xC675, 0xCA55, 0xCA75, 0xCD55, 0xCD75,
+    0xCE55, 0xCE75, 0xC357, 0xC377, 0xC359,
+    0xC379, 0xC859, 0xC25A, 0xC27A, 0xC75A,
+    0xC77A, 0xCF5A, 0xCF7A
 };
 
 struct extended_range {
-	const int codepoint;
-	const int value;
+    const int codepoint;
+    const int value;
 };
 
 #ifdef _MSC_VER
 // Needed for msvc's debug assertions
-bool operator<(extended_range const& lft, extended_range const& rgt) {
-	return lft.codepoint < rgt.codepoint;
+bool operator<(extended_range const &lft, extended_range const &rgt)
+{
+    return lft.codepoint < rgt.codepoint;
 }
 
-bool operator<(int lft, extended_range const& rgt) {
-	return lft < rgt.codepoint;
+bool operator<(int lft, extended_range const &rgt)
+{
+    return lft < rgt.codepoint;
 }
 #endif
 
-bool operator<(extended_range const& lft, int rgt) {
-	return lft.codepoint < rgt;
+bool operator<(extended_range const &lft, int rgt)
+{
+    return lft.codepoint < rgt;
 }
 
 // ISO-6937-2 values for codepoints that don't come in a nice contiguous block
 const extended_range iso6937_extended_codepoints[] = {
-	{ 0x02C7, 0xCF20 },
-	{ 0x02D8, 0xC620 },
-	{ 0x02D9, 0xC720 },
-	{ 0x02DA, 0xCA20 },
-	{ 0x02DB, 0xCE20 },
-	{ 0x02DD, 0xCD20 },
-	{ 0x2014, 0xD0 },
-	{ 0x2018, 0xA9 },
-	{ 0x2019, 0xB9 },
-	{ 0x201C, 0xAA },
-	{ 0x201D, 0xBA },
-	{ 0x2022, 0xD4 },
-	{ 0x20AC, 0xA4 }, // ETSI EN 300 468 extension: euro sign at A4
-	{ 0x2126, 0xE0 },
-	{ 0x215B, 0xDC },
-	{ 0x215C, 0xDD },
-	{ 0x215D, 0xDE },
-	{ 0x2190, 0xAC },
-	{ 0x2191, 0xAD },
-	{ 0x2192, 0xAE },
-	{ 0x2193, 0xAF },
-	{ 0x266A, 0xD5 }
+    { 0x02C7, 0xCF20 },
+    { 0x02D8, 0xC620 },
+    { 0x02D9, 0xC720 },
+    { 0x02DA, 0xCA20 },
+    { 0x02DB, 0xCE20 },
+    { 0x02DD, 0xCD20 },
+    { 0x2014, 0xD0 },
+    { 0x2018, 0xA9 },
+    { 0x2019, 0xB9 },
+    { 0x201C, 0xAA },
+    { 0x201D, 0xBA },
+    { 0x2022, 0xD4 },
+    { 0x20AC, 0xA4 }, // ETSI EN 300 468 extension: euro sign at A4
+    { 0x2126, 0xE0 },
+    { 0x215B, 0xDC },
+    { 0x215C, 0xDD },
+    { 0x215D, 0xDE },
+    { 0x2190, 0xAC },
+    { 0x2191, 0xAD },
+    { 0x2192, 0xAE },
+    { 0x2193, 0xAF },
+    { 0x266A, 0xD5 }
 };
 
 #define countof(array) (sizeof(array) / sizeof((array)[0]))
 
 /// Get the ISO-6937-2 value for the given unicode codepoint or 0 if it cannot be mapped
-int get_iso6937(int codepoint) {
-	if (static_cast<size_t>(codepoint) < countof(iso6937_codepoints))
-		return iso6937_codepoints[codepoint];
-
-	auto ext = boost::lower_bound(iso6937_extended_codepoints, codepoint);
-	if (ext == std::end(iso6937_extended_codepoints) || ext->codepoint != codepoint)
-		return 0;
-	return ext->value;
+int get_iso6937(int codepoint)
+{
+    if (static_cast<size_t>(codepoint) < countof(iso6937_codepoints))
+        return iso6937_codepoints[codepoint];
+
+    auto ext = boost::lower_bound(iso6937_extended_codepoints, codepoint);
+    if (ext == std::end(iso6937_extended_codepoints) || ext->codepoint != codepoint)
+        return 0;
+    return ext->value;
 }
 
 } // namespace {
 
-namespace agi { namespace charset {
+namespace agi {
+namespace charset {
 
 #ifdef _LIBICONV_VERSION
 #define INTERNAL_CHARSET "UCS-4-INTERNAL"
@@ -176,50 +181,51 @@ namespace agi { namespace charset {
 #endif
 
 Converter6937::Converter6937(bool subst, const char *src)
-: to_ucs4(new IconvWrapper(src, INTERNAL_CHARSET))
-, subst(subst)
+    : to_ucs4(new IconvWrapper(src, INTERNAL_CHARSET))
+    , subst(subst)
 {
 }
 
-size_t Converter6937::Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) {
-	// No state to reset
-	if (!inbuf || !inbytesleft)
-		return 0;
-
-	size_t bytes_written = 0;
-
-	while (*inbytesleft > 0) {
-		int in_val = 0;
-
-		// Copy inbuf/inbytesleft so that we don't update them if the
-		// conversion fails (due to not enough space or a bad sequence)
-		const char *inbuftmp = *inbuf;
-		size_t inbyteslefttmp = *inbytesleft;
-
-		char *val_buf = reinterpret_cast<char *>(&in_val);
-		size_t val_buf_size = sizeof(in_val);
-
-		// Get the next unicode character from the input
-		size_t ret = to_ucs4->Convert(&inbuftmp, &inbyteslefttmp, &val_buf, &val_buf_size);
-		if (ret == (size_t)-1 && errno != E2BIG)
-			return ret;
-
-		// And convert that to ISO-6937-2
-		int val = get_iso6937(in_val);
-		if (!val && in_val) {
-			if (subst) {
-				val = '?';
-			}
-			else {
-				errno = EILSEQ;
-				return (size_t)-1;
-			}
-		}
-
-		if (*outbytesleft < 1 || (val > 255 && *outbytesleft < 2)) {
-			errno = E2BIG;
-			return (size_t)-1;
-		}
+size_t Converter6937::Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+    // No state to reset
+    if (!inbuf || !inbytesleft)
+        return 0;
+
+    size_t bytes_written = 0;
+
+    while (*inbytesleft > 0) {
+        int in_val = 0;
+
+        // Copy inbuf/inbytesleft so that we don't update them if the
+        // conversion fails (due to not enough space or a bad sequence)
+        const char *inbuftmp = *inbuf;
+        size_t inbyteslefttmp = *inbytesleft;
+
+        char *val_buf = reinterpret_cast<char *>(&in_val);
+        size_t val_buf_size = sizeof(in_val);
+
+        // Get the next unicode character from the input
+        size_t ret = to_ucs4->Convert(&inbuftmp, &inbyteslefttmp, &val_buf, &val_buf_size);
+        if (ret == (size_t) -1 && errno != E2BIG)
+            return ret;
+
+        // And convert that to ISO-6937-2
+        int val = get_iso6937(in_val);
+        if (!val && in_val) {
+            if (subst) {
+                val = '?';
+            }
+            else {
+                errno = EILSEQ;
+                return (size_t) -1;
+            }
+        }
+
+        if (*outbytesleft < 1 || (val > 255 && *outbytesleft < 2)) {
+            errno = E2BIG;
+            return (size_t) -1;
+        }
 
 #define WRITE_BYTE(b) \
 		do { \
@@ -228,19 +234,20 @@ size_t Converter6937::Convert(const char **inbuf, size_t *inbytesleft, char **ou
 			++bytes_written; \
 		} while(0)
 
-		if (val <= 255)
-			WRITE_BYTE(val);
-		else {
-			WRITE_BYTE((val >> 8) & 0xFF);
-			WRITE_BYTE(val & 0xFF);
-		}
+        if (val <= 255)
+            WRITE_BYTE(val);
+        else {
+            WRITE_BYTE((val >> 8) & 0xFF);
+            WRITE_BYTE(val & 0xFF);
+        }
 
-		// Update the input pointers now that the conversion has succeeded
-		*inbuf = inbuftmp;
-		*inbytesleft = inbyteslefttmp;
-	}
+        // Update the input pointers now that the conversion has succeeded
+        *inbuf = inbuftmp;
+        *inbytesleft = inbyteslefttmp;
+    }
 
-	return bytes_written;
+    return bytes_written;
 }
 
-} } // namespace agi::charset
+}
+} // namespace agi::charset
diff --git a/libaegisub/common/charset_6937.h b/libaegisub/common/charset_6937.h
index b95113bf08871e58a660b1006b960e3cbe8d2573..9794317a5caab74f3732f2be62def68792fe8ff5 100644
--- a/libaegisub/common/charset_6937.h
+++ b/libaegisub/common/charset_6937.h
@@ -19,27 +19,29 @@
 #include <libaegisub/charset_conv.h>
 #include <memory>
 
-namespace agi { namespace charset {
+namespace agi {
+namespace charset {
 
 /// @brief A charset converter for ISO-6937-2
 ///
 /// While glibc iconv supports ISO-6937-2, GNU libiconv does not due to that
 /// it's not used by anything but old subtitle formats
 class Converter6937 final : public Converter {
-	/// Converter to UCS-4 so that we only have to deal with unicode codepoints
-	std::unique_ptr<IconvWrapper> to_ucs4;
+    /// Converter to UCS-4 so that we only have to deal with unicode codepoints
+    std::unique_ptr<IconvWrapper> to_ucs4;
 
-	/// Should unsupported characters be replaced with '?'
-	const bool subst;
+    /// Should unsupported characters be replaced with '?'
+    const bool subst;
 
 public:
-	/// Constructor
-	/// @param subst Enable substitution for unsupported characters
-	/// @param src Source encoding
-	Converter6937(bool subst, const char *src);
+    /// Constructor
+    /// @param subst Enable substitution for unsupported characters
+    /// @param src Source encoding
+    Converter6937(bool subst, const char *src);
 
-	/// Convert a string. Interface is the same as iconv.
-	size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft);
+    /// Convert a string. Interface is the same as iconv.
+    size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
 };
 
-} }
+}
+}
diff --git a/libaegisub/common/charset_conv.cpp b/libaegisub/common/charset_conv.cpp
index a8efba7eb368fb463ed2346db3f1e1ec061654f1..95d0b3b68e7e50ec4cc1b94a3cdfd3850f3501dc 100644
--- a/libaegisub/common/charset_conv.cpp
+++ b/libaegisub/common/charset_conv.cpp
@@ -39,390 +39,405 @@
 #define ICONV_CONST_CAST(a) const_cast<char **>(a)
 #endif
 
-static const iconv_t iconv_invalid = (iconv_t)-1;
-static const size_t iconv_failed = (size_t)-1;
+static const iconv_t iconv_invalid = (iconv_t) -1;
+static const size_t iconv_failed = (size_t) -1;
 
 namespace {
-	using namespace agi::charset;
+using namespace agi::charset;
 
-	Converter *get_converter(bool subst, const char *src, const char *dst);
+Converter *get_converter(bool subst, const char *src, const char *dst);
 
 /// @brief Map a user-friendly encoding name to the real encoding name
-	const char *get_real_encoding_name(const char *name) {
-		struct pair { const char *pretty; const char *real; };
-		static pair pretty_names[] = {
+const char *get_real_encoding_name(const char *name)
+{
+    struct pair { const char *pretty; const char *real; };
+    static pair pretty_names[] = {
 #			define ADD(pretty, real) pair{pretty, real},
 #			include <libaegisub/charsets.def>
 #			undef ADD
-		};
-
-		static bool init = false;
-		if (!init) {
-			init = true;
-			boost::sort(pretty_names, [](pair a, pair b) {
-				return strcmp(a.pretty, b.pretty) < 0;
-			});
-		}
-
-		auto enc = boost::lower_bound(pretty_names, name, [](pair a, const char *b) {
-			return strcmp(a.pretty, b) < 0;
-		});
-
-		if (enc != std::end(pretty_names) && strcmp(enc->pretty, name) == 0)
-			return enc->real;
-		return name;
-	}
-
-	size_t get_bom_size(Iconv& cd) {
-		// Most (but not all) iconv implementations automatically insert a BOM
-		// at the beginning of text converted to UTF-8, UTF-16 and UTF-32, but
-		// we usually don't want this, as some of the wxString using code
-		// assumes there is no BOM (as the exact encoding is known externally)
-		// As such, when doing conversions we will strip the BOM if it exists,
-		// then manually add it when writing files
-
-		char buff[8];
-		const char* src = "";
-		char *dst = buff;
-		size_t srcLen = 1;
-		size_t dstLen = 8;
-
-		size_t res = cd(&src, &srcLen, &dst, &dstLen);
-		assert(res != iconv_failed);
-		assert(srcLen == 0);
-
-		size_t size = 0;
-		for (src = buff; src < dst; ++src) {
-			if (*src) ++size;
-		}
-		if (size) {
-			// If there is a BOM, it will always be at least as big as the NUL
-			size = std::max(size, (8 - dstLen) / 2);
-		}
-		return size;
-	}
-
-	void eat_bom(Iconv& cd, size_t bomSize, const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
-		// If this encoding has a forced BOM (i.e. it's UTF-16 or UTF-32 without
-		// a specified byte order), skip over it
-		if (bomSize > 0 && inbytesleft && *inbytesleft) {
-			// libiconv marks the bom as written after writing the first
-			// character after the bom rather than when it writes the bom, so
-			// convert at least one extra character
-			char bom[8];
-			char *dst = bom;
-			size_t dstSize = std::min((size_t)8, bomSize + *outbytesleft);
-			const char *src = *inbuf;
-			size_t srcSize = *inbytesleft;
-			cd(&src, &srcSize, &dst, &dstSize);
-		}
-	}
-
-	// Calculate the size of NUL in the given character set
-	size_t nul_size(const char *encoding) {
-		// We need a character set to convert from with a known encoding of NUL
-		// UTF-8 seems like the obvious choice
-		std::unique_ptr<Converter> cd(get_converter(false, "UTF-8", encoding));
-
-		char dbuff[4];
-		char sbuff[] = "";
-		char* dst = dbuff;
-		const char* src = sbuff;
-		size_t dstLen = sizeof(dbuff);
-		size_t srcLen = 1;
-
-		size_t ret = cd->Convert(&src, &srcLen, &dst, &dstLen);
-		assert(ret != iconv_failed);
-		assert(dst - dbuff > 0);
-
-		return dst - dbuff;
-	}
+    };
+
+    static bool init = false;
+    if (!init) {
+        init = true;
+        boost::sort(pretty_names, [](pair a, pair b) {
+            return strcmp(a.pretty, b.pretty) < 0;
+        });
+    }
+
+    auto enc = boost::lower_bound(pretty_names, name, [](pair a, const char *b) {
+        return strcmp(a.pretty, b) < 0;
+    });
+
+    if (enc != std::end(pretty_names) && strcmp(enc->pretty, name) == 0)
+        return enc->real;
+    return name;
+}
+
+size_t get_bom_size(Iconv &cd)
+{
+    // Most (but not all) iconv implementations automatically insert a BOM
+    // at the beginning of text converted to UTF-8, UTF-16 and UTF-32, but
+    // we usually don't want this, as some of the wxString using code
+    // assumes there is no BOM (as the exact encoding is known externally)
+    // As such, when doing conversions we will strip the BOM if it exists,
+    // then manually add it when writing files
+
+    char buff[8];
+    const char *src = "";
+    char *dst = buff;
+    size_t srcLen = 1;
+    size_t dstLen = 8;
+
+    size_t res = cd(&src, &srcLen, &dst, &dstLen);
+    assert(res != iconv_failed);
+    assert(srcLen == 0);
+
+    size_t size = 0;
+    for (src = buff; src < dst; ++src) {
+        if (*src) ++size;
+    }
+    if (size) {
+        // If there is a BOM, it will always be at least as big as the NUL
+        size = std::max(size, (8 - dstLen) / 2);
+    }
+    return size;
+}
+
+void eat_bom(Iconv &cd, size_t bomSize, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+    // If this encoding has a forced BOM (i.e. it's UTF-16 or UTF-32 without
+    // a specified byte order), skip over it
+    if (bomSize > 0 && inbytesleft && *inbytesleft) {
+        // libiconv marks the bom as written after writing the first
+        // character after the bom rather than when it writes the bom, so
+        // convert at least one extra character
+        char bom[8];
+        char *dst = bom;
+        size_t dstSize = std::min((size_t)8, bomSize + *outbytesleft);
+        const char *src = *inbuf;
+        size_t srcSize = *inbytesleft;
+        cd(&src, &srcSize, &dst, &dstSize);
+    }
+}
+
+// Calculate the size of NUL in the given character set
+size_t nul_size(const char *encoding)
+{
+    // We need a character set to convert from with a known encoding of NUL
+    // UTF-8 seems like the obvious choice
+    std::unique_ptr<Converter> cd(get_converter(false, "UTF-8", encoding));
+
+    char dbuff[4];
+    char sbuff[] = "";
+    char *dst = dbuff;
+    const char *src = sbuff;
+    size_t dstLen = sizeof(dbuff);
+    size_t srcLen = 1;
+
+    size_t ret = cd->Convert(&src, &srcLen, &dst, &dstLen);
+    assert(ret != iconv_failed);
+    assert(dst - dbuff > 0);
+
+    return dst - dbuff;
+}
 
 #ifdef ICONV_POSIX
-	class ConverterImpl final : public Converter {
-		size_t bomSize;
-		Iconv cd;
-	public:
-		// subst is not used here because POSIX doesn't let you disable substitution
-		ConverterImpl(bool, const char* sourceEncoding, const char* destEncoding)
-		{
-			const char *dstEnc = get_real_encoding_name(destEncoding);
-			cd = Iconv("utf-8", dstEnc);
-
-			bomSize = get_bom_size(cd);
-			cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
-		}
-
-		size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
-			eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
-
-			size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
-
-			// This loop never does anything useful with a POSIX-compliant iconv
-			// implementation, but those don't seem to actually exist
-			while (res == iconv_failed && errno != E2BIG) {
-				++*inbuf;
-				--*inbytesleft;
-				res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
-			}
-
-			return res;
-		}
-	};
+class ConverterImpl final : public Converter {
+    size_t bomSize;
+    Iconv cd;
+public:
+    // subst is not used here because POSIX doesn't let you disable substitution
+    ConverterImpl(bool, const char *sourceEncoding, const char *destEncoding) {
+        const char *dstEnc = get_real_encoding_name(destEncoding);
+        cd = Iconv("utf-8", dstEnc);
+
+        bomSize = get_bom_size(cd);
+        cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
+    }
+
+    size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) {
+        eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
+
+        size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
+
+        // This loop never does anything useful with a POSIX-compliant iconv
+        // implementation, but those don't seem to actually exist
+        while (res == iconv_failed && errno != E2BIG) {
+            ++*inbuf;
+            --*inbytesleft;
+            res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
+        }
+
+        return res;
+    }
+};
 
 #else
 
-	class ConverterImpl final : public iconv_fallbacks, public Converter {
-		size_t bomSize;
-		char invalidRep[8];
-		size_t invalidRepSize;
-		Iconv cd;
-		static void fallback(
-			unsigned int code,
-			void (*callback) (const char *buf, size_t buflen, void* callback_arg),
-			void *callback_arg,
-			void *convPtr)
-		{
-			// At some point in the future, this should probably switch to a real mapping
-			// For now, there's just three cases: BOM to nothing, '\' to itself
-			// (for Shift-JIS, which does not have \) and everything else to '?'
-			if (code == 0xFEFF) return;
-			if (code == 0x5C) callback("\\", 1, callback_arg);
-			else {
-				ConverterImpl *self = static_cast<ConverterImpl *>(convPtr);
-				callback(self->invalidRep, self->invalidRepSize, callback_arg);
-			}
-		}
-
-	public:
-		ConverterImpl(bool subst, const char* sourceEncoding, const char* destEncoding)
-		{
-			const char *dstEnc = get_real_encoding_name(destEncoding);
-			cd = Iconv("utf-8", dstEnc);
-
-			bomSize = get_bom_size(cd);
-
-			// Get fallback character
-			const char sbuff[] = "?";
-			const char *src = sbuff;
-			char *dst = invalidRep;
-			size_t dstLen = 4;
-			size_t srcLen = 1;
-
-			size_t res = Convert(&src, &srcLen, &dst, &dstLen);
-			assert(res != iconv_failed);
-			assert(srcLen == 0);
-
-			invalidRepSize = 4 - dstLen;
-
-			cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
-
-			if (subst) {
-				data = this;
-				mb_to_uc_fallback = nullptr;
-				mb_to_wc_fallback = nullptr;
-				uc_to_mb_fallback = fallback;
-				wc_to_mb_fallback = nullptr;
-
-				int transliterate = 1;
-				iconvctl(cd, ICONV_SET_TRANSLITERATE, &transliterate);
-				iconvctl(cd, ICONV_SET_FALLBACKS, static_cast<iconv_fallbacks*>(this));
-			}
-		}
-
-		size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) override {
-			eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
-			size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
-
-			if (res == iconv_failed && errno == E2BIG && *outbytesleft == 0) {
-				// libiconv checks if there are any bytes left in the output buffer
-				// before checking if the conversion would actually write any
-				// characters to the output buffer, resulting in occasional invalid
-				// E2BIG false positives
-				char buff[8];
-				size_t buffsize = 8;
-				char* out = buff;
-				const char* in = *inbuf;
-				size_t insize = *inbytesleft;
-
-				res = cd(&in, &insize, &out, &buffsize);
-				// If no bytes of the output buffer were used, the original
-				// conversion may have been successful
-				if (buffsize != 8) {
-					errno = E2BIG;
-					res = iconv_failed;
-				}
-			}
-
-			return res;
-		}
-	};
+class ConverterImpl final : public iconv_fallbacks, public Converter {
+    size_t bomSize;
+    char invalidRep[8];
+    size_t invalidRepSize;
+    Iconv cd;
+    static void fallback(
+        unsigned int code,
+        void (*callback) (const char *buf, size_t buflen, void *callback_arg),
+        void *callback_arg,
+        void *convPtr) {
+        // At some point in the future, this should probably switch to a real mapping
+        // For now, there's just three cases: BOM to nothing, '\' to itself
+        // (for Shift-JIS, which does not have \) and everything else to '?'
+        if (code == 0xFEFF) return;
+        if (code == 0x5C) callback("\\", 1, callback_arg);
+        else {
+            ConverterImpl *self = static_cast<ConverterImpl *>(convPtr);
+            callback(self->invalidRep, self->invalidRepSize, callback_arg);
+        }
+    }
+
+public:
+    ConverterImpl(bool subst, const char *sourceEncoding, const char *destEncoding) {
+        const char *dstEnc = get_real_encoding_name(destEncoding);
+        cd = Iconv("utf-8", dstEnc);
+
+        bomSize = get_bom_size(cd);
+
+        // Get fallback character
+        const char sbuff[] = "?";
+        const char *src = sbuff;
+        char *dst = invalidRep;
+        size_t dstLen = 4;
+        size_t srcLen = 1;
+
+        size_t res = Convert(&src, &srcLen, &dst, &dstLen);
+        assert(res != iconv_failed);
+        assert(srcLen == 0);
+
+        invalidRepSize = 4 - dstLen;
+
+        cd = Iconv(get_real_encoding_name(sourceEncoding), dstEnc);
+
+        if (subst) {
+            data = this;
+            mb_to_uc_fallback = nullptr;
+            mb_to_wc_fallback = nullptr;
+            uc_to_mb_fallback = fallback;
+            wc_to_mb_fallback = nullptr;
+
+            int transliterate = 1;
+            iconvctl(cd, ICONV_SET_TRANSLITERATE, &transliterate);
+            iconvctl(cd, ICONV_SET_FALLBACKS, static_cast<iconv_fallbacks *>(this));
+        }
+    }
+
+    size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) override {
+        eat_bom(cd, bomSize, inbuf, inbytesleft, outbuf, outbytesleft);
+        size_t res = cd(inbuf, inbytesleft, outbuf, outbytesleft);
+
+        if (res == iconv_failed && errno == E2BIG && *outbytesleft == 0) {
+            // libiconv checks if there are any bytes left in the output buffer
+            // before checking if the conversion would actually write any
+            // characters to the output buffer, resulting in occasional invalid
+            // E2BIG false positives
+            char buff[8];
+            size_t buffsize = 8;
+            char *out = buff;
+            const char *in = *inbuf;
+            size_t insize = *inbytesleft;
+
+            res = cd(&in, &insize, &out, &buffsize);
+            // If no bytes of the output buffer were used, the original
+            // conversion may have been successful
+            if (buffsize != 8) {
+                errno = E2BIG;
+                res = iconv_failed;
+            }
+        }
+
+        return res;
+    }
+};
 #endif
 
-	Converter *get_converter(bool subst, const char *src, const char *dst) {
-		try {
-			return new ConverterImpl(subst, src, dst);
-		}
-		catch (UnsupportedConversion const&) {
-			if (strcmp(dst, "ISO-6937-2"))
-				throw;
-			return new Converter6937(subst, src);
-		}
-	}
+Converter *get_converter(bool subst, const char *src, const char *dst)
+{
+    try {
+        return new ConverterImpl(subst, src, dst);
+    }
+    catch (UnsupportedConversion const &) {
+        if (strcmp(dst, "ISO-6937-2"))
+            throw;
+        return new Converter6937(subst, src);
+    }
+}
 } // namespace {
 
-namespace agi { namespace charset {
+namespace agi {
+namespace charset {
 Iconv::Iconv() : cd(iconv_invalid) { }
 
 Iconv::Iconv(const char *source, const char *dest)
-: cd(iconv_open(dest, source))
+    : cd(iconv_open(dest, source))
 {
-	if (cd == iconv_invalid)
-		throw UnsupportedConversion(std::string("Cannot convert from ") + source + " to " + dest);
+    if (cd == iconv_invalid)
+        throw UnsupportedConversion(std::string("Cannot convert from ") + source + " to " + dest);
 }
 
-Iconv::~Iconv() {
-	if (cd != iconv_invalid) iconv_close(cd);
+Iconv::~Iconv()
+{
+    if (cd != iconv_invalid) iconv_close(cd);
 }
 
-size_t Iconv::operator()(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) {
-	return iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
+size_t Iconv::operator()(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
+{
+    return iconv(cd, ICONV_CONST_CAST(inbuf), inbytesleft, outbuf, outbytesleft);
 }
 
-IconvWrapper::IconvWrapper(const char* sourceEncoding, const char* destEncoding, bool enableSubst)
-: conv(get_converter(enableSubst, sourceEncoding, destEncoding))
+IconvWrapper::IconvWrapper(const char *sourceEncoding, const char *destEncoding, bool enableSubst)
+    : conv(get_converter(enableSubst, sourceEncoding, destEncoding))
 {
-	// These need to be set only after we verify that the source and dest
-	// charsets are valid
-	toNulLen = nul_size(destEncoding);
-	fromNulLen = nul_size(sourceEncoding);
+    // These need to be set only after we verify that the source and dest
+    // charsets are valid
+    toNulLen = nul_size(destEncoding);
+    fromNulLen = nul_size(sourceEncoding);
 }
 
 IconvWrapper::~IconvWrapper() { }
 
-std::string IconvWrapper::Convert(const char *source, size_t len) {
-	std::string dest;
-	Convert(source, len, dest);
-	return dest;
+std::string IconvWrapper::Convert(const char *source, size_t len)
+{
+    std::string dest;
+    Convert(source, len, dest);
+    return dest;
 }
 
-void IconvWrapper::Convert(const char *src, size_t srcLen, std::string &dest) {
-	char buff[512];
-
-	size_t res;
-	do {
-		char *dst = buff;
-		size_t dstLen = sizeof(buff);
-		res = conv->Convert(&src, &srcLen, &dst, &dstLen);
-		if (res == 0) conv->Convert(nullptr, nullptr, &dst, &dstLen);
-
-		dest.append(buff, sizeof(buff) - dstLen);
-	} while (res == iconv_failed && errno == E2BIG);
-
-	if (res == iconv_failed) {
-		switch (errno) {
-			case EILSEQ:
-			case EINVAL:
-				throw BadInput(
-					"One or more characters in the input string were not valid "
-					"characters in the given input encoding");
-			default:
-				throw ConversionFailure("An unknown conversion failure occurred");
-		}
-	}
+void IconvWrapper::Convert(const char *src, size_t srcLen, std::string &dest)
+{
+    char buff[512];
+
+    size_t res;
+    do {
+        char *dst = buff;
+        size_t dstLen = sizeof(buff);
+        res = conv->Convert(&src, &srcLen, &dst, &dstLen);
+        if (res == 0) conv->Convert(nullptr, nullptr, &dst, &dstLen);
+
+        dest.append(buff, sizeof(buff) - dstLen);
+    } while (res == iconv_failed && errno == E2BIG);
+
+    if (res == iconv_failed) {
+        switch (errno) {
+        case EILSEQ:
+        case EINVAL:
+            throw BadInput(
+                "One or more characters in the input string were not valid "
+                "characters in the given input encoding");
+        default:
+            throw ConversionFailure("An unknown conversion failure occurred");
+        }
+    }
 }
 
-size_t IconvWrapper::Convert(const char* source, size_t sourceSize, char *dest, size_t destSize) {
-	if (sourceSize == (size_t)-1)
-		sourceSize = SrcStrLen(source);
-
-	size_t res = conv->Convert(&source, &sourceSize, &dest, &destSize);
-	if (res == 0) res = conv->Convert(nullptr, nullptr, &dest, &destSize);
-
-	if (res == iconv_failed) {
-		switch (errno) {
-			case E2BIG:
-				throw BufferTooSmall(
-					"Destination buffer was not large enough to fit converted "
-					"string.");
-			case EINVAL:
-			case EILSEQ:
-				throw BadInput(
-					"One or more characters in the input string were not valid "
-					"characters in the given input encoding");
-			default:
-				throw ConversionFailure("An unknown conversion failure occurred");
-		}
-	}
-	return res;
+size_t IconvWrapper::Convert(const char *source, size_t sourceSize, char *dest, size_t destSize)
+{
+    if (sourceSize == (size_t) -1)
+        sourceSize = SrcStrLen(source);
+
+    size_t res = conv->Convert(&source, &sourceSize, &dest, &destSize);
+    if (res == 0) res = conv->Convert(nullptr, nullptr, &dest, &destSize);
+
+    if (res == iconv_failed) {
+        switch (errno) {
+        case E2BIG:
+            throw BufferTooSmall(
+                "Destination buffer was not large enough to fit converted "
+                "string.");
+        case EINVAL:
+        case EILSEQ:
+            throw BadInput(
+                "One or more characters in the input string were not valid "
+                "characters in the given input encoding");
+        default:
+            throw ConversionFailure("An unknown conversion failure occurred");
+        }
+    }
+    return res;
 }
 
-size_t IconvWrapper::Convert(const char** source, size_t* sourceSize, char** dest, size_t* destSize) {
-	return conv->Convert(source, sourceSize, dest, destSize);
+size_t IconvWrapper::Convert(const char **source, size_t *sourceSize, char **dest, size_t *destSize)
+{
+    return conv->Convert(source, sourceSize, dest, destSize);
 }
 
-size_t IconvWrapper::RequiredBufferSize(std::string const& str) {
-	return RequiredBufferSize(str.data(), str.size());
+size_t IconvWrapper::RequiredBufferSize(std::string const &str)
+{
+    return RequiredBufferSize(str.data(), str.size());
 }
 
-size_t IconvWrapper::RequiredBufferSize(const char* src, size_t srcLen) {
-	char buff[512];
-	size_t charsWritten = 0;
-	size_t res;
-
-	do {
-		char* dst = buff;
-		size_t dstSize = sizeof(buff);
-		res = conv->Convert(&src, &srcLen, &dst, &dstSize);
-		conv->Convert(nullptr, nullptr, &dst, &dstSize);
-
-		charsWritten += dst - buff;
-	} while (res == iconv_failed && errno == E2BIG);
-
-	if (res == iconv_failed) {
-		switch (errno) {
-			case EINVAL:
-			case EILSEQ:
-				throw BadInput(
-					"One or more characters in the input string were not valid "
-					"characters in the given input encoding");
-			default:
-				throw ConversionFailure("An unknown conversion failure occurred");
-		}
-	}
-	return charsWritten;
+size_t IconvWrapper::RequiredBufferSize(const char *src, size_t srcLen)
+{
+    char buff[512];
+    size_t charsWritten = 0;
+    size_t res;
+
+    do {
+        char *dst = buff;
+        size_t dstSize = sizeof(buff);
+        res = conv->Convert(&src, &srcLen, &dst, &dstSize);
+        conv->Convert(nullptr, nullptr, &dst, &dstSize);
+
+        charsWritten += dst - buff;
+    } while (res == iconv_failed && errno == E2BIG);
+
+    if (res == iconv_failed) {
+        switch (errno) {
+        case EINVAL:
+        case EILSEQ:
+            throw BadInput(
+                "One or more characters in the input string were not valid "
+                "characters in the given input encoding");
+        default:
+            throw ConversionFailure("An unknown conversion failure occurred");
+        }
+    }
+    return charsWritten;
 }
 
-static size_t mbstrlen(const char* str, size_t nulLen) {
-	const char *ptr;
-	switch (nulLen) {
-		case 1:
-			return strlen(str);
-		case 2:
-			for (ptr = str; *reinterpret_cast<const uint16_t *>(ptr) != 0; ptr += 2) ;
-			return ptr - str;
-		case 4:
-			for (ptr = str; *reinterpret_cast<const uint32_t *>(ptr) != 0; ptr += 4) ;
-			return ptr - str;
-		default:
-			return (size_t)-1;
-	}
+static size_t mbstrlen(const char *str, size_t nulLen)
+{
+    const char *ptr;
+    switch (nulLen) {
+    case 1:
+        return strlen(str);
+    case 2:
+        for (ptr = str; *reinterpret_cast<const uint16_t *>(ptr) != 0; ptr += 2) ;
+        return ptr - str;
+    case 4:
+        for (ptr = str; *reinterpret_cast<const uint32_t *>(ptr) != 0; ptr += 4) ;
+        return ptr - str;
+    default:
+        return (size_t) -1;
+    }
 }
 
-size_t IconvWrapper::SrcStrLen(const char* str) {
-	return mbstrlen(str, fromNulLen);
+size_t IconvWrapper::SrcStrLen(const char *str)
+{
+    return mbstrlen(str, fromNulLen);
 
 }
-size_t IconvWrapper::DstStrLen(const char* str) {
-	return mbstrlen(str, toNulLen);
+size_t IconvWrapper::DstStrLen(const char *str)
+{
+    return mbstrlen(str, toNulLen);
 }
 
-bool IsConversionSupported(const char *src, const char *dst) {
-	iconv_t cd = iconv_open(dst, src);
-	bool supported = cd != iconv_invalid;
-	iconv_close(cd);
-	return supported;
+bool IsConversionSupported(const char *src, const char *dst)
+{
+    iconv_t cd = iconv_open(dst, src);
+    bool supported = cd != iconv_invalid;
+    iconv_close(cd);
+    return supported;
 }
 
-	}
+}
 }
diff --git a/libaegisub/common/color.cpp b/libaegisub/common/color.cpp
index 7381e92ffc5cd8c014a56b0c9c92b996951e283b..87f47e19143fd91bc4f798b08aeb8203007fadbf 100644
--- a/libaegisub/common/color.cpp
+++ b/libaegisub/common/color.cpp
@@ -21,41 +21,49 @@
 namespace agi {
 
 Color::Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-: r(r), g(g), b(b), a(a)
+    : r(r), g(g), b(b), a(a)
 { }
 
-Color::Color(std::string const& str) {
-	parser::parse(*this, str);
+Color::Color(std::string const &str)
+{
+    parser::parse(*this, str);
 }
 
-std::string Color::GetAssStyleFormatted() const {
-	return agi::format("&H%02X%02X%02X%02X", a, b, g, r);
+std::string Color::GetAssStyleFormatted() const
+{
+    return agi::format("&H%02X%02X%02X%02X", a, b, g, r);
 }
 
-std::string Color::GetAssOverrideFormatted() const {
-	return agi::format("&H%02X%02X%02X&", b, g, r);
+std::string Color::GetAssOverrideFormatted() const
+{
+    return agi::format("&H%02X%02X%02X&", b, g, r);
 }
 
-std::string Color::GetSsaFormatted() const {
-	return std::to_string((a << 24) + (b << 16) + (g << 8) + r);
+std::string Color::GetSsaFormatted() const
+{
+    return std::to_string((a << 24) + (b << 16) + (g << 8) + r);
 }
 
-std::string Color::GetHexFormatted(bool rgba) const {
-	if (rgba)
-		return agi::format("#%02X%02X%02X%02X", r, g, b, a);
-	return agi::format("#%02X%02X%02X", r, g, b);
+std::string Color::GetHexFormatted(bool rgba) const
+{
+    if (rgba)
+        return agi::format("#%02X%02X%02X%02X", r, g, b, a);
+    return agi::format("#%02X%02X%02X", r, g, b);
 }
 
-std::string Color::GetRgbFormatted() const {
-	return agi::format("rgb(%d, %d, %d)", r, g, b);
+std::string Color::GetRgbFormatted() const
+{
+    return agi::format("rgb(%d, %d, %d)", r, g, b);
 }
 
-bool Color::operator==(Color const& col) const {
-	return r == col.r && g == col.g && b == col.b && a == col.a;
+bool Color::operator==(Color const &col) const
+{
+    return r == col.r && g == col.g && b == col.b && a == col.a;
 }
 
-bool Color::operator!=(Color const& col) const {
-	return !(*this == col);
+bool Color::operator!=(Color const &col) const
+{
+    return !(*this == col);
 }
 
 }
diff --git a/libaegisub/common/dispatch.cpp b/libaegisub/common/dispatch.cpp
index 434195836eae5b6ce7af24894976fd0585562169..141c95efc1f362dcfe13c432e26a4ec9148f6d9b 100644
--- a/libaegisub/common/dispatch.cpp
+++ b/libaegisub/common/dispatch.cpp
@@ -26,115 +26,127 @@
 #include <thread>
 
 namespace {
-	boost::asio::io_service *service;
-	std::function<void (agi::dispatch::Thunk)> invoke_main;
-	std::atomic<uint_fast32_t> threads_running;
-
-	class MainQueue final : public agi::dispatch::Queue {
-		void DoInvoke(agi::dispatch::Thunk thunk) override {
-			invoke_main(thunk);
-		}
-	};
-
-	class BackgroundQueue final : public agi::dispatch::Queue {
-		void DoInvoke(agi::dispatch::Thunk thunk) override {
-			service->post(thunk);
-		}
-	};
-
-	class SerialQueue final : public agi::dispatch::Queue {
-		boost::asio::io_service::strand strand;
-
-		void DoInvoke(agi::dispatch::Thunk thunk) override {
-			strand.post(thunk);
-		}
-	public:
-		SerialQueue() : strand(*service) { }
-	};
-
-	struct IOServiceThreadPool {
-		boost::asio::io_service io_service;
-		std::unique_ptr<boost::asio::io_service::work> work;
-		std::vector<std::thread> threads;
-
-		IOServiceThreadPool() : work(new boost::asio::io_service::work(io_service)) { }
-		~IOServiceThreadPool() {
-			work.reset();
+boost::asio::io_service *service;
+std::function<void (agi::dispatch::Thunk)> invoke_main;
+std::atomic<uint_fast32_t> threads_running;
+
+class MainQueue final : public agi::dispatch::Queue {
+    void DoInvoke(agi::dispatch::Thunk thunk) override {
+        invoke_main(thunk);
+    }
+};
+
+class BackgroundQueue final : public agi::dispatch::Queue {
+    void DoInvoke(agi::dispatch::Thunk thunk) override {
+        service->post(thunk);
+    }
+};
+
+class SerialQueue final : public agi::dispatch::Queue {
+    boost::asio::io_service::strand strand;
+
+    void DoInvoke(agi::dispatch::Thunk thunk) override {
+        strand.post(thunk);
+    }
+public:
+    SerialQueue() : strand(*service) { }
+};
+
+struct IOServiceThreadPool {
+    boost::asio::io_service io_service;
+    std::unique_ptr<boost::asio::io_service::work> work;
+    std::vector<std::thread> threads;
+
+    IOServiceThreadPool() : work(new boost::asio::io_service::work(io_service)) { }
+    ~IOServiceThreadPool() {
+        work.reset();
 #ifndef _WIN32
-			for (auto& thread : threads) thread.join();
+        for (auto &thread : threads) thread.join();
 #else
-			// Calling join() after main() returns deadlocks
-			// https://connect.microsoft.com/VisualStudio/feedback/details/747145
-			for (auto& thread : threads) thread.detach();
-			while (threads_running) std::this_thread::yield();
+        // Calling join() after main() returns deadlocks
+        // https://connect.microsoft.com/VisualStudio/feedback/details/747145
+        for (auto &thread : threads) thread.detach();
+        while (threads_running) std::this_thread::yield();
 #endif
-		}
-	};
+    }
+};
 }
 
-namespace agi { namespace dispatch {
-
-void Init(std::function<void (Thunk)> invoke_main) {
-	static IOServiceThreadPool thread_pool;
-	::service = &thread_pool.io_service;
-	::invoke_main = invoke_main;
-
-	thread_pool.threads.reserve(std::max<unsigned>(4, std::thread::hardware_concurrency()));
-	for (size_t i = 0; i < thread_pool.threads.capacity(); ++i) {
-		thread_pool.threads.emplace_back([]{
-			++threads_running;
-			agi::util::SetThreadName("Dispatch Worker");
-			service->run();
-			--threads_running;
-		});
-	}
+namespace agi {
+namespace dispatch {
+
+void Init(std::function<void (Thunk)> invoke_main)
+{
+    static IOServiceThreadPool thread_pool;
+    ::service = &thread_pool.io_service;
+    ::invoke_main = invoke_main;
+
+    thread_pool.threads.reserve(std::max<unsigned>(4, std::thread::hardware_concurrency()));
+    for (size_t i = 0; i < thread_pool.threads.capacity(); ++i) {
+        thread_pool.threads.emplace_back([] {
+            ++threads_running;
+            agi::util::SetThreadName("Dispatch Worker");
+            service->run();
+            --threads_running;
+        });
+    }
 }
 
-void Queue::Async(Thunk thunk) {
-	DoInvoke([=] {
-		try {
-			thunk();
-		}
-		catch (...) {
-			auto e = std::current_exception();
-			invoke_main([=] { std::rethrow_exception(e); });
-		}
-	});
+void Queue::Async(Thunk thunk)
+{
+    DoInvoke([ = ] {
+        try
+        {
+            thunk();
+        }
+        catch (...)
+        {
+            auto e = std::current_exception();
+            invoke_main([ = ] { std::rethrow_exception(e); });
+        }
+    });
 }
 
-void Queue::Sync(Thunk thunk) {
-	std::mutex m;
-	std::condition_variable cv;
-	std::unique_lock<std::mutex> l(m);
-	std::exception_ptr e;
-	bool done = false;
-	DoInvoke([&]{
-		std::unique_lock<std::mutex> l(m);
-		try {
-			thunk();
-		}
-		catch (...) {
-			e = std::current_exception();
-		}
-		done = true;
-		cv.notify_all();
-	});
-	cv.wait(l, [&]{ return done; });
-	if (e) std::rethrow_exception(e);
+void Queue::Sync(Thunk thunk)
+{
+    std::mutex m;
+    std::condition_variable cv;
+    std::unique_lock<std::mutex> l(m);
+    std::exception_ptr e;
+    bool done = false;
+    DoInvoke([&] {
+        std::unique_lock<std::mutex> l(m);
+        try
+        {
+            thunk();
+        }
+        catch (...)
+        {
+            e = std::current_exception();
+        }
+        done = true;
+        cv.notify_all();
+    });
+    cv.wait(l, [&] { return done; });
+    if (e) std::rethrow_exception(e);
 }
 
-Queue& Main() {
-	static MainQueue q;
-	return q;
+Queue &Main()
+{
+    static MainQueue q;
+    return q;
 }
 
-Queue& Background() {
-	static BackgroundQueue q;
-	return q;
+Queue &Background()
+{
+    static BackgroundQueue q;
+    return q;
 }
 
-std::unique_ptr<Queue> Create() {
-	return std::unique_ptr<Queue>(new SerialQueue);
+std::unique_ptr<Queue> Create()
+{
+    return std::unique_ptr<Queue>(new SerialQueue);
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/file_mapping.cpp b/libaegisub/common/file_mapping.cpp
index 5102694963c8fbb3628e00ad3bfcd641e4bc2a3d..499059b3c9e6f1677e0ac7c798239c1064e73eb9 100644
--- a/libaegisub/common/file_mapping.cpp
+++ b/libaegisub/common/file_mapping.cpp
@@ -32,141 +32,146 @@ using namespace boost::interprocess;
 
 namespace {
 char *map(int64_t s_offset, uint64_t length, boost::interprocess::mode_t mode,
-	uint64_t file_size, agi::file_mapping const& file,
-	std::unique_ptr<mapped_region>& region, uint64_t& mapping_start)
+          uint64_t file_size, agi::file_mapping const &file,
+          std::unique_ptr<mapped_region> &region, uint64_t &mapping_start)
 {
-	static char dummy = 0;
-	if (length == 0) return &dummy;
-
-	auto offset = static_cast<uint64_t>(s_offset);
-	if (offset + length > file_size)
-		throw agi::InternalError("Attempted to map beyond end of file");
-
-	// Check if we can just use the current mapping
-	if (region && offset >= mapping_start && offset + length <= mapping_start + region->get_size())
-		return static_cast<char *>(region->get_address()) + offset - mapping_start;
-
-	if (sizeof(size_t) == 4) {
-		mapping_start = offset & ~0xFFFFFULL; // Align to 1 MB bondary
-		length += static_cast<size_t>(offset - mapping_start);
-		// Map 16 MB or length rounded up to the next MB
-		length = std::min<uint64_t>(std::max<uint64_t>(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start);
-	}
-	else {
-		// Just map the whole file
-		mapping_start = 0;
-		length = file_size;
-	}
-
-	if (length > std::numeric_limits<size_t>::max())
-		throw std::bad_alloc();
-
-	try {
-		region = agi::make_unique<mapped_region>(file, mode, mapping_start, static_cast<size_t>(length));
-	}
-	catch (interprocess_exception const&) {
-		throw agi::fs::FileSystemUnknownError("Failed mapping a view of the file");
-	}
-
-	return static_cast<char *>(region->get_address()) + offset - mapping_start;
+    static char dummy = 0;
+    if (length == 0) return &dummy;
+
+    auto offset = static_cast<uint64_t>(s_offset);
+    if (offset + length > file_size)
+        throw agi::InternalError("Attempted to map beyond end of file");
+
+    // Check if we can just use the current mapping
+    if (region && offset >= mapping_start && offset + length <= mapping_start + region->get_size())
+        return static_cast<char *>(region->get_address()) + offset - mapping_start;
+
+    if (sizeof(size_t) == 4) {
+        mapping_start = offset & ~0xFFFFFULL; // Align to 1 MB bondary
+        length += static_cast<size_t>(offset - mapping_start);
+        // Map 16 MB or length rounded up to the next MB
+        length = std::min<uint64_t>(std::max<uint64_t>(0x1000000U, (length + 0xFFFFF) & ~0xFFFFF), file_size - mapping_start);
+    }
+    else {
+        // Just map the whole file
+        mapping_start = 0;
+        length = file_size;
+    }
+
+    if (length > std::numeric_limits<size_t>::max())
+        throw std::bad_alloc();
+
+    try {
+        region = agi::make_unique<mapped_region>(file, mode, mapping_start, static_cast<size_t>(length));
+    }
+    catch (interprocess_exception const &) {
+        throw agi::fs::FileSystemUnknownError("Failed mapping a view of the file");
+    }
+
+    return static_cast<char *>(region->get_address()) + offset - mapping_start;
 }
 
 }
 
 namespace agi {
-file_mapping::file_mapping(fs::path const& filename, bool temporary)
+file_mapping::file_mapping(fs::path const &filename, bool temporary)
 #ifdef _WIN32
-: handle(CreateFileW(filename.wstring().c_str(),
-	temporary ? read_write : read_only,
-	temporary ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ,
-	nullptr,
-	temporary ? OPEN_ALWAYS : OPEN_EXISTING,
-	0, 0))
+    : handle(CreateFileW(filename.wstring().c_str(),
+                         temporary ? read_write : read_only,
+                         temporary ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ,
+                         nullptr,
+                         temporary ? OPEN_ALWAYS : OPEN_EXISTING,
+                         0, 0))
 {
-	if (handle == ipcdetail::invalid_file()) {
-		switch (GetLastError()) {
-		case ERROR_FILE_NOT_FOUND:
-		case ERROR_PATH_NOT_FOUND:
-			throw fs::FileNotFound(filename);
-		case ERROR_ACCESS_DENIED:
-			throw fs::ReadDenied(filename);
-		default:
-			throw fs::FileSystemUnknownError(util::ErrorString(GetLastError()));
-		}
-	}
+    if (handle == ipcdetail::invalid_file()) {
+        switch (GetLastError()) {
+        case ERROR_FILE_NOT_FOUND:
+        case ERROR_PATH_NOT_FOUND:
+            throw fs::FileNotFound(filename);
+        case ERROR_ACCESS_DENIED:
+            throw fs::ReadDenied(filename);
+        default:
+            throw fs::FileSystemUnknownError(util::ErrorString(GetLastError()));
+        }
+    }
 #else
-: handle(temporary
-	? ipcdetail::create_or_open_file(filename.string().c_str(), read_write)
-	: ipcdetail::open_existing_file(filename.string().c_str(), read_only))
+    : handle(temporary
+             ? ipcdetail::create_or_open_file(filename.string().c_str(), read_write)
+             : ipcdetail::open_existing_file(filename.string().c_str(), read_only))
 {
-	if (handle == ipcdetail::invalid_file()) {
-		switch (errno) {
-		case ENOENT:
-			throw fs::FileNotFound(filename);
-		case EACCES:
-			throw fs::ReadDenied(filename);
-		case EIO:
-			throw fs::FileSystemUnknownError("Fatal I/O opening path: " + filename.string());
-		}
-	}
+    if (handle == ipcdetail::invalid_file()) {
+        switch (errno) {
+        case ENOENT:
+            throw fs::FileNotFound(filename);
+        case EACCES:
+            throw fs::ReadDenied(filename);
+        case EIO:
+            throw fs::FileSystemUnknownError("Fatal I/O opening path: " + filename.string());
+        }
+    }
 #endif
 }
 
-file_mapping::~file_mapping() {
-	if (handle != ipcdetail::invalid_file()) {
-		ipcdetail::close_file(handle);
-	}
+file_mapping::~file_mapping()
+{
+    if (handle != ipcdetail::invalid_file()) {
+        ipcdetail::close_file(handle);
+    }
 }
 
-read_file_mapping::read_file_mapping(fs::path const& filename)
-: file(filename, false)
+read_file_mapping::read_file_mapping(fs::path const &filename)
+    : file(filename, false)
 {
-	offset_t size = 0;
-	ipcdetail::get_file_size(file.get_mapping_handle().handle, size);
-	file_size = static_cast<uint64_t>(size);
+    offset_t size = 0;
+    ipcdetail::get_file_size(file.get_mapping_handle().handle, size);
+    file_size = static_cast<uint64_t>(size);
 }
 
 read_file_mapping::~read_file_mapping() { }
 
-const char *read_file_mapping::read() {
-	return read(0, size());
+const char *read_file_mapping::read()
+{
+    return read(0, size());
 }
 
-const char *read_file_mapping::read(int64_t offset, uint64_t length) {
-	return map(offset, length, read_only, file_size, file, region, mapping_start);
+const char *read_file_mapping::read(int64_t offset, uint64_t length)
+{
+    return map(offset, length, read_only, file_size, file, region, mapping_start);
 }
 
-temp_file_mapping::temp_file_mapping(fs::path const& filename, uint64_t size)
-: file(filename, true)
-, file_size(size)
+temp_file_mapping::temp_file_mapping(fs::path const &filename, uint64_t size)
+    : file(filename, true)
+    , file_size(size)
 {
-	auto handle = file.get_mapping_handle().handle;
+    auto handle = file.get_mapping_handle().handle;
 #ifdef _WIN32
-	LARGE_INTEGER li;
-	li.QuadPart = size;
-	SetFilePointerEx(handle, li, nullptr, FILE_BEGIN);
-	SetEndOfFile(handle);
+    LARGE_INTEGER li;
+    li.QuadPart = size;
+    SetFilePointerEx(handle, li, nullptr, FILE_BEGIN);
+    SetEndOfFile(handle);
 #else
-	unlink(filename.string().c_str());
-	if (ftruncate(handle, size) == -1) {
-		switch (errno) {
-		case EBADF:  throw InternalError("Error opening file " + filename.string() + " not handled");
-		case EFBIG:  throw fs::DriveFull(filename);
-		case EINVAL: throw InternalError("File opened incorrectly: " + filename.string());
-		case EROFS:  throw fs::WriteDenied(filename);
-		default: throw fs::FileSystemUnknownError("Unknown error opening file: " + filename.string());
-		}
-	}
+    unlink(filename.string().c_str());
+    if (ftruncate(handle, size) == -1) {
+        switch (errno) {
+        case EBADF:  throw InternalError("Error opening file " + filename.string() + " not handled");
+        case EFBIG:  throw fs::DriveFull(filename);
+        case EINVAL: throw InternalError("File opened incorrectly: " + filename.string());
+        case EROFS:  throw fs::WriteDenied(filename);
+        default: throw fs::FileSystemUnknownError("Unknown error opening file: " + filename.string());
+        }
+    }
 #endif
 }
 
 temp_file_mapping::~temp_file_mapping() { }
 
-const char *temp_file_mapping::read(int64_t offset, uint64_t length) {
-	return map(offset, length, read_only, file_size, file, read_region, read_mapping_start);
+const char *temp_file_mapping::read(int64_t offset, uint64_t length)
+{
+    return map(offset, length, read_only, file_size, file, read_region, read_mapping_start);
 }
 
-char *temp_file_mapping::write(int64_t offset, uint64_t length) {
-	return map(offset, length, read_write, file_size, file, write_region, write_mapping_start);
+char *temp_file_mapping::write(int64_t offset, uint64_t length)
+{
+    return map(offset, length, read_write, file_size, file, write_region, write_mapping_start);
 }
 }
diff --git a/libaegisub/common/format.cpp b/libaegisub/common/format.cpp
index 829baa782a3ddbc186522547f1864bd187ad3a5a..54c17d5bf34a4b6a3cf97a2b406902b930ebf438 100644
--- a/libaegisub/common/format.cpp
+++ b/libaegisub/common/format.cpp
@@ -34,183 +34,191 @@ template class boost::interprocess::basic_vectorbuf<std::wstring>;
 
 namespace {
 template<typename Char>
-int actual_len(int max_len, const Char *value) {
-	int len = 0;
-	while (value[len] && (max_len <= 0 || len < max_len)) ++len;
-	return len;
+int actual_len(int max_len, const Char *value)
+{
+    int len = 0;
+    while (value[len] && (max_len <= 0 || len < max_len)) ++len;
+    return len;
 }
 
 template<typename Char>
-int actual_len(int max_len, std::basic_string<Char> value) {
-	if (max_len > 0 && static_cast<size_t>(max_len) < value.size())
-		return max_len;
-	return value.size();
+int actual_len(int max_len, std::basic_string<Char> value)
+{
+    if (max_len > 0 && static_cast<size_t>(max_len) < value.size())
+        return max_len;
+    return value.size();
 }
 
 template<typename Char>
-void do_write_str(std::basic_ostream<Char>& out, const Char *str, int len) {
-	out.write(str, len);
+void do_write_str(std::basic_ostream<Char> &out, const Char *str, int len)
+{
+    out.write(str, len);
 }
 
 template<typename Src, typename Dst>
-void convert_and_write(std::basic_ostream<Dst>& out, const Src *str, int len, const char *src_enc, const char *dst_enc) {
-	Dst buffer[512];
-	agi::charset::Iconv cd(src_enc, dst_enc);
-	size_t in_len = len * sizeof(Src);
-	const char *in = reinterpret_cast<const char *>(str);
-
-	size_t res;
-	do {
-		char *out_buf = reinterpret_cast<char *>(buffer);
-		size_t out_len = sizeof(buffer);
-		res = cd(&in, &in_len, &out_buf, &out_len);
-		if (res == 0) cd(nullptr, nullptr, &out_buf, &out_len);
-
-		out.write(buffer, (sizeof(buffer) - out_len) / sizeof(Dst));
-	} while (res == (size_t)-1 && errno == E2BIG);
+void convert_and_write(std::basic_ostream<Dst> &out, const Src *str, int len, const char *src_enc, const char *dst_enc)
+{
+    Dst buffer[512];
+    agi::charset::Iconv cd(src_enc, dst_enc);
+    size_t in_len = len * sizeof(Src);
+    const char *in = reinterpret_cast<const char *>(str);
+
+    size_t res;
+    do {
+        char *out_buf = reinterpret_cast<char *>(buffer);
+        size_t out_len = sizeof(buffer);
+        res = cd(&in, &in_len, &out_buf, &out_len);
+        if (res == 0) cd(nullptr, nullptr, &out_buf, &out_len);
+
+        out.write(buffer, (sizeof(buffer) - out_len) / sizeof(Dst));
+    } while (res == (size_t) -1 && errno == E2BIG);
 }
 
-void do_write_str(std::ostream& out, const wchar_t *str, int len) {
-	convert_and_write(out, str, len, WCHAR_T_ENC, "utf-8");
+void do_write_str(std::ostream &out, const wchar_t *str, int len)
+{
+    convert_and_write(out, str, len, WCHAR_T_ENC, "utf-8");
 }
 
-void do_write_str(std::wostream& out, const char *str, int len) {
-	convert_and_write(out, str, len, "utf-8", WCHAR_T_ENC);
+void do_write_str(std::wostream &out, const char *str, int len)
+{
+    convert_and_write(out, str, len, "utf-8", WCHAR_T_ENC);
 }
 
 template<typename Char>
 struct format_parser {
-	agi::format_detail::formatter_state<Char> &s;
-
-	void read_and_append_up_to_next_specifier() {
-		for (std::streamsize len = 0; ; ++len) {
-			// Ran out of format specifiers; not an error due to that
-			// translated strings may not need them all
-			if (!s.fmt[len]) {
-				s.out.write(s.fmt, len);
-				s.fmt += len;
-				return;
-			}
-
-			if (s.fmt[len] == '%') {
-				if (s.fmt[len + 1] == '%') {
-					s.out.write(s.fmt, len);
-					s.fmt += len + 1;
-					len = 0;
-					continue;
-				}
-
-				s.out.write(s.fmt, len);
-				s.fmt += len;
-				break;
-			}
-		}
-	}
-
-	int read_int() {
-		int i = 0;
-		for (; *s.fmt_cur >= '0' && *s.fmt_cur <= '9'; ++s.fmt_cur)
-			i = 10 * i + (*s.fmt_cur - '0');
-		return i;
-	}
-
-	void parse_flags() {
-		for (; ; ++s.fmt_cur) {
-			switch (*s.fmt_cur) {
-			// Not supported: ' ' (add a space before positive numers to align with negative)
-			case '#':
-				s.out.setf(std::ios::showpoint | std::ios::showbase);
-				continue;
-			case '0':
-				// overridden by left alignment ('-' flag)
-				if (!(s.out.flags() & std::ios::left)) {
-					// Use internal padding so that numeric values are
-					// formatted correctly, eg -00010 rather than 000-10
-					s.out.fill('0');
-					s.out.setf(std::ios::internal, std::ios::adjustfield);
-				}
-				continue;
-			case '-':
-				s.out.fill(' ');
-				s.out.setf(std::ios::left, std::ios::adjustfield);
-				continue;
-			case '+':
-				s.out.setf(std::ios::showpos);
-				continue;
-			}
-			break;
-		}
-	}
-
-	void parse_width() {
-		if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
-			s.width = read_int();
-		else if (*s.fmt_cur == '*') {
-			s.read_width = true;
-			s.pending = true;
-			++s.fmt_cur;
-		}
-	}
-
-	void parse_precision() {
-		if (*s.fmt_cur != '.') return;
-		++s.fmt_cur;
-
-		// Ignoring negative precision because it's dumb and pointless
-		if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
-			s.precision = read_int();
-		else if (*s.fmt_cur == '*') {
-			s.read_precision = true;
-			s.pending = true;
-			++s.fmt_cur;
-		}
-		else
-			s.precision = 0;
-	}
-
-	void parse_length_modifiers() {
-		// Where "parse" means "skip" since we don't need them
-		for (Char c = *s.fmt_cur;
-			c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
-			c = *++s.fmt_cur);
-	}
-
-	void parse_format_specifier() {
-		s.width = 0;
-		s.precision = -1;
-		s.out.fill(' ');
-		s.out.unsetf(
-			std::ios::adjustfield |
-			std::ios::basefield   |
-			std::ios::boolalpha   |
-			std::ios::floatfield  |
-			std::ios::showbase    |
-			std::ios::showpoint   |
-			std::ios::showpos     |
-			std::ios::uppercase);
-
-		// Don't touch fmt until the specifier is fully applied so that if we
-		// have insufficient arguments it'll get passed through to the output
-		s.fmt_cur = s.fmt + 1;
-
-		parse_flags();
-		parse_width();
-		parse_precision();
-		parse_length_modifiers();
-	}
+    agi::format_detail::formatter_state<Char> &s;
+
+    void read_and_append_up_to_next_specifier() {
+        for (std::streamsize len = 0; ; ++len) {
+            // Ran out of format specifiers; not an error due to that
+            // translated strings may not need them all
+            if (!s.fmt[len]) {
+                s.out.write(s.fmt, len);
+                s.fmt += len;
+                return;
+            }
+
+            if (s.fmt[len] == '%') {
+                if (s.fmt[len + 1] == '%') {
+                    s.out.write(s.fmt, len);
+                    s.fmt += len + 1;
+                    len = 0;
+                    continue;
+                }
+
+                s.out.write(s.fmt, len);
+                s.fmt += len;
+                break;
+            }
+        }
+    }
+
+    int read_int() {
+        int i = 0;
+        for (; *s.fmt_cur >= '0' && *s.fmt_cur <= '9'; ++s.fmt_cur)
+            i = 10 * i + (*s.fmt_cur - '0');
+        return i;
+    }
+
+    void parse_flags() {
+        for (; ; ++s.fmt_cur) {
+            switch (*s.fmt_cur) {
+            // Not supported: ' ' (add a space before positive numers to align with negative)
+            case '#':
+                s.out.setf(std::ios::showpoint | std::ios::showbase);
+                continue;
+            case '0':
+                // overridden by left alignment ('-' flag)
+                if (!(s.out.flags() & std::ios::left)) {
+                    // Use internal padding so that numeric values are
+                    // formatted correctly, eg -00010 rather than 000-10
+                    s.out.fill('0');
+                    s.out.setf(std::ios::internal, std::ios::adjustfield);
+                }
+                continue;
+            case '-':
+                s.out.fill(' ');
+                s.out.setf(std::ios::left, std::ios::adjustfield);
+                continue;
+            case '+':
+                s.out.setf(std::ios::showpos);
+                continue;
+            }
+            break;
+        }
+    }
+
+    void parse_width() {
+        if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
+            s.width = read_int();
+        else if (*s.fmt_cur == '*') {
+            s.read_width = true;
+            s.pending = true;
+            ++s.fmt_cur;
+        }
+    }
+
+    void parse_precision() {
+        if (*s.fmt_cur != '.') return;
+        ++s.fmt_cur;
+
+        // Ignoring negative precision because it's dumb and pointless
+        if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9')
+            s.precision = read_int();
+        else if (*s.fmt_cur == '*') {
+            s.read_precision = true;
+            s.pending = true;
+            ++s.fmt_cur;
+        }
+        else
+            s.precision = 0;
+    }
+
+    void parse_length_modifiers() {
+        // Where "parse" means "skip" since we don't need them
+        for (Char c = *s.fmt_cur;
+             c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't';
+             c = *++s.fmt_cur);
+    }
+
+    void parse_format_specifier() {
+        s.width = 0;
+        s.precision = -1;
+        s.out.fill(' ');
+        s.out.unsetf(
+            std::ios::adjustfield |
+            std::ios::basefield   |
+            std::ios::boolalpha   |
+            std::ios::floatfield  |
+            std::ios::showbase    |
+            std::ios::showpoint   |
+            std::ios::showpos     |
+            std::ios::uppercase);
+
+        // Don't touch fmt until the specifier is fully applied so that if we
+        // have insufficient arguments it'll get passed through to the output
+        s.fmt_cur = s.fmt + 1;
+
+        parse_flags();
+        parse_width();
+        parse_precision();
+        parse_length_modifiers();
+    }
 };
 }
 
 namespace agi {
 
 template<typename StreamChar, typename Char>
-void writer<StreamChar, const Char *>::write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value) {
-	do_write_str(out, value, actual_len(max_len, value));
+void writer<StreamChar, const Char *>::write(std::basic_ostream<StreamChar> &out, int max_len, const Char *value)
+{
+    do_write_str(out, value, actual_len(max_len, value));
 }
 
 template<typename StreamChar, typename Char>
-void writer<StreamChar, std::basic_string<Char>>::write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value) {
-	do_write_str(out, value.data(), actual_len(max_len, value));
+void writer<StreamChar, std::basic_string<Char>>::write(std::basic_ostream<StreamChar> &out, int max_len, std::basic_string<Char> const &value)
+{
+    do_write_str(out, value.data(), actual_len(max_len, value));
 }
 
 template struct writer<char, const char *>;
@@ -225,91 +233,95 @@ template struct writer<wchar_t, std::wstring>;
 namespace format_detail {
 
 template<typename Char>
-bool formatter<Char>::parse_next() {
-	format_parser<Char> parser{*static_cast<formatter_state<Char> *>(this)};
-	parser.read_and_append_up_to_next_specifier();
-	if (!*this->fmt) return false;
-	parser.parse_format_specifier();
-	return true;
+bool formatter<Char>::parse_next()
+{
+    format_parser<Char> parser{*static_cast<formatter_state<Char> *>(this)};
+    parser.read_and_append_up_to_next_specifier();
+    if (!*this->fmt) return false;
+    parser.parse_format_specifier();
+    return true;
 }
 
 template<typename Char>
-Char formatter<Char>::next_format() {
-	this->pending = false;
-
-	if (this->width < 0) {
-		this->out.fill(' ');
-		this->out.setf(std::ios::left, std::ios::adjustfield);
-		this->width = -this->width;
-	}
-	this->out.width(this->width);
-	this->out.precision(this->precision < 0 ? 6 : this->precision);
-
-	Char c = *this->fmt_cur ? this->fmt_cur[0] : 's';
-	if (c >= 'A' && c <= 'Z') {
-		this->out.setf(std::ios::uppercase);
-		c += 'a' - 'A';
-	}
-
-	switch (c) {
-		case 'c':
-			this->out.setf(std::ios::dec, std::ios::basefield);
-			break;
-		case 'd': case 'i':
-			this->out.setf(std::ios::dec, std::ios::basefield);
-			break;
-		case 'o':
-			this->out.setf(std::ios::oct, std::ios::basefield);
-			break;
-		case 'x':
-			this->out.setf(std::ios::hex, std::ios::basefield);
-			break;
-		case 'u':
-			this->out.setf(std::ios::dec, std::ios::basefield);
-			break;
-		case 'e':
-			this->out.setf(std::ios::scientific, std::ios::floatfield);
-			this->out.setf(std::ios::dec, std::ios::basefield);
-			break;
-		case 'f':
-			this->out.setf(std::ios::fixed, std::ios::floatfield);
-			break;
-		case 'g':
-			this->out.setf(std::ios::dec, std::ios::basefield);
-			this->out.flags(this->out.flags() & ~std::ios::floatfield);
-			break;
-		case 'p':
-			this->out.setf(std::ios::hex, std::ios::basefield);
-			break;
-		default: // s and other
-			this->out.setf(std::ios::boolalpha);
-			break;
-	}
-
-	this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur;
-	return c;
+Char formatter<Char>::next_format()
+{
+    this->pending = false;
+
+    if (this->width < 0) {
+        this->out.fill(' ');
+        this->out.setf(std::ios::left, std::ios::adjustfield);
+        this->width = -this->width;
+    }
+    this->out.width(this->width);
+    this->out.precision(this->precision < 0 ? 6 : this->precision);
+
+    Char c = *this->fmt_cur ? this->fmt_cur[0] : 's';
+    if (c >= 'A' && c <= 'Z') {
+        this->out.setf(std::ios::uppercase);
+        c += 'a' - 'A';
+    }
+
+    switch (c) {
+    case 'c':
+        this->out.setf(std::ios::dec, std::ios::basefield);
+        break;
+    case 'd': case 'i':
+        this->out.setf(std::ios::dec, std::ios::basefield);
+        break;
+    case 'o':
+        this->out.setf(std::ios::oct, std::ios::basefield);
+        break;
+    case 'x':
+        this->out.setf(std::ios::hex, std::ios::basefield);
+        break;
+    case 'u':
+        this->out.setf(std::ios::dec, std::ios::basefield);
+        break;
+    case 'e':
+        this->out.setf(std::ios::scientific, std::ios::floatfield);
+        this->out.setf(std::ios::dec, std::ios::basefield);
+        break;
+    case 'f':
+        this->out.setf(std::ios::fixed, std::ios::floatfield);
+        break;
+    case 'g':
+        this->out.setf(std::ios::dec, std::ios::basefield);
+        this->out.flags(this->out.flags() & ~std::ios::floatfield);
+        break;
+    case 'p':
+        this->out.setf(std::ios::hex, std::ios::basefield);
+        break;
+    default: // s and other
+        this->out.setf(std::ios::boolalpha);
+        break;
+    }
+
+    this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur;
+    return c;
 }
 
 template<typename Char>
-formatter<Char>::~formatter() {
-	// Write remaining formatting string
-	for (std::streamsize len = 0; ; ++len) {
-		if (!this->fmt[len]) {
-			this->out.write(this->fmt, len);
-			return;
-		}
-
-		if (this->fmt[len] == '%' && this->fmt[len + 1] == '%') {
-			this->out.write(this->fmt, len);
-			this->fmt += len + 1;
-			len = 0;
-			continue;
-		}
-	}
+formatter<Char>::~formatter()
+{
+    // Write remaining formatting string
+    for (std::streamsize len = 0; ; ++len) {
+        if (!this->fmt[len]) {
+            this->out.write(this->fmt, len);
+            return;
+        }
+
+        if (this->fmt[len] == '%' && this->fmt[len + 1] == '%') {
+            this->out.write(this->fmt, len);
+            this->fmt += len + 1;
+            len = 0;
+            continue;
+        }
+    }
 }
 
 template class formatter<char>;
 template class formatter<wchar_t>;
 
-} }
+}
+}
 
diff --git a/libaegisub/common/fs.cpp b/libaegisub/common/fs.cpp
index 6ce45a08ee1c5c21e7d3b2b6b18fcbb8b17e7f1e..563afc3d9c5e79eae40e235c8e8e712ec293cfad 100644
--- a/libaegisub/common/fs.cpp
+++ b/libaegisub/common/fs.cpp
@@ -68,38 +68,44 @@ namespace ec = boost::system::errc;
 // sasuga windows.h
 #undef CreateDirectory
 
-namespace agi { namespace fs {
+namespace agi {
+namespace fs {
 namespace {
-	WRAP_BFS(file_size, SizeImpl)
-	WRAP_BFS(space, Space)
+WRAP_BFS(file_size, SizeImpl)
+WRAP_BFS(space, Space)
 }
 
-	WRAP_BFS_IGNORE_ERROR(exists, Exists)
-	WRAP_BFS_IGNORE_ERROR(is_regular_file, FileExists)
-	WRAP_BFS_IGNORE_ERROR(is_directory, DirectoryExists)
-	WRAP_BFS(last_write_time, ModifiedTime)
-	WRAP_BFS(create_directories, CreateDirectory)
-	WRAP_BFS(remove, Remove)
-	WRAP_BFS(canonical, Canonicalize)
+WRAP_BFS_IGNORE_ERROR(exists, Exists)
+WRAP_BFS_IGNORE_ERROR(is_regular_file, FileExists)
+WRAP_BFS_IGNORE_ERROR(is_directory, DirectoryExists)
+WRAP_BFS(last_write_time, ModifiedTime)
+WRAP_BFS(create_directories, CreateDirectory)
+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 Size(path const &p)
+{
+    if (DirectoryExists(p))
+        throw NotAFile(p);
+    return SizeImpl(p);
+}
 
-	uintmax_t FreeSpace(path const& p) {
-		return Space(p).available;
-	}
+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 Rename(const path &from, const path &to)
+{
+    CHECKED_CALL(bfs::rename(from, to, ec), from, to);
+}
 
-	bool HasExtension(path const& p, std::string const& ext) {
-		auto filename = p.filename().string();
-		if (filename.size() < ext.size() + 1) return false;
-		if (filename[filename.size() - ext.size() - 1] != '.') return false;
-		return boost::iends_with(filename, ext);
-	}
-} }
+bool HasExtension(path const &p, std::string const &ext)
+{
+    auto filename = p.filename().string();
+    if (filename.size() < ext.size() + 1) return false;
+    if (filename[filename.size() - ext.size() - 1] != '.') return false;
+    return boost::iends_with(filename, ext);
+}
+}
+}
diff --git a/libaegisub/common/hotkey.cpp b/libaegisub/common/hotkey.cpp
index a2cebeef93281ed28afe29ed086823a6a3151a97..3e567e834714d2f7dcd30a4defef049bf74307a8 100644
--- a/libaegisub/common/hotkey.cpp
+++ b/libaegisub/common/hotkey.cpp
@@ -24,190 +24,200 @@
 #include <boost/range/algorithm/equal_range.hpp>
 #include <tuple>
 
-namespace agi { namespace hotkey {
+namespace agi {
+namespace hotkey {
 namespace {
 struct combo_cmp {
-	bool operator()(const Combo *a, const Combo *b) {
-		return a->Str() < b->Str();
-	}
+    bool operator()(const Combo *a, const Combo *b) {
+        return a->Str() < b->Str();
+    }
 
-	bool operator()(const Combo *a, std::string const& b) {
-		return a->Str() < b;
-	}
+    bool operator()(const Combo *a, std::string const &b) {
+        return a->Str() < b;
+    }
 
-	bool operator()(std::string const& a, const Combo *b) {
-		return a < b->Str();
-	}
+    bool operator()(std::string const &a, const Combo *b) {
+        return a < b->Str();
+    }
 };
 
 struct hotkey_visitor : json::ConstVisitor {
-	std::string const& context;
-	std::string const& command;
-	Hotkey::HotkeyMap& map;
-	bool needs_backup = false;
-
-	hotkey_visitor(std::string const& context, std::string const& command, Hotkey::HotkeyMap& map)
-	: context(context), command(command), map(map) { }
-
-	void Visit(std::string const& string) override {
-		map.insert(make_pair(command, Combo(context, command, string)));
-	}
-
-	void Visit(json::Object const& hotkey) override {
-		auto mod_it = hotkey.find("modifiers");
-		if (mod_it == end(hotkey)) {
-			LOG_E("agi/hotkey/load") << "Hotkey for command '" << command << "' is missing modifiers";
-			return;
-		}
-		auto key_it = hotkey.find("key");
-		if (key_it == end(hotkey)) {
-			LOG_E("agi/hotkey/load") << "Hotkey for command '" << command << "' is missing the key";
-			return;
-		}
-
-		std::string key_str;
-		json::Array const& arr_mod = mod_it->second;
-		for (std::string const& mod : arr_mod) {
-			key_str += mod;
-			key_str += '-';
-		}
-		key_str += static_cast<std::string const&>(key_it->second);
-
-		map.insert(make_pair(command, Combo(context, command, std::move(key_str))));
-		needs_backup = true;
-	}
-
-	void Visit(const json::Array& array) override { }
-	void Visit(int64_t number) override { }
-	void Visit(double number) override { }
-	void Visit(bool boolean) override { }
-	void Visit(const json::Null& null) override { }
+    std::string const &context;
+    std::string const &command;
+    Hotkey::HotkeyMap &map;
+    bool needs_backup = false;
+
+    hotkey_visitor(std::string const &context, std::string const &command, Hotkey::HotkeyMap &map)
+        : context(context), command(command), map(map) { }
+
+    void Visit(std::string const &string) override {
+        map.insert(make_pair(command, Combo(context, command, string)));
+    }
+
+    void Visit(json::Object const &hotkey) override {
+        auto mod_it = hotkey.find("modifiers");
+        if (mod_it == end(hotkey)) {
+            LOG_E("agi/hotkey/load") << "Hotkey for command '" << command << "' is missing modifiers";
+            return;
+        }
+        auto key_it = hotkey.find("key");
+        if (key_it == end(hotkey)) {
+            LOG_E("agi/hotkey/load") << "Hotkey for command '" << command << "' is missing the key";
+            return;
+        }
+
+        std::string key_str;
+        json::Array const &arr_mod = mod_it->second;
+        for (std::string const &mod : arr_mod) {
+            key_str += mod;
+            key_str += '-';
+        }
+        key_str += static_cast<std::string const &>(key_it->second);
+
+        map.insert(make_pair(command, Combo(context, command, std::move(key_str))));
+        needs_backup = true;
+    }
+
+    void Visit(const json::Array &array) override { }
+    void Visit(int64_t number) override { }
+    void Visit(double number) override { }
+    void Visit(bool boolean) override { }
+    void Visit(const json::Null &null) override { }
 };
 }
 
-Hotkey::Hotkey(fs::path const& file, std::pair<const char *, size_t> default_config)
-: config_file(file)
+Hotkey::Hotkey(fs::path const &file, std::pair<const char *, size_t> default_config)
+    : config_file(file)
 {
-	LOG_D("hotkey/init") << "Generating hotkeys.";
+    LOG_D("hotkey/init") << "Generating hotkeys.";
 
-	auto root = json_util::file(config_file, default_config);
-	for (auto const& hotkey_context : static_cast<json::Object const&>(root))
-		BuildHotkey(hotkey_context.first, hotkey_context.second);
-	UpdateStrMap();
+    auto root = json_util::file(config_file, default_config);
+    for (auto const &hotkey_context : static_cast<json::Object const &>(root))
+        BuildHotkey(hotkey_context.first, hotkey_context.second);
+    UpdateStrMap();
 }
 
-void Hotkey::BuildHotkey(std::string const& context, json::Object const& hotkeys) {
-	for (auto const& command : hotkeys) {
-		json::Array const& command_hotkeys = command.second;
-
-		hotkey_visitor visitor{context, command.first, cmd_map};
-		for (auto const& hotkey : command_hotkeys)
-			hotkey.Accept(visitor);
-		backup_config_file |= visitor.needs_backup;
-	}
+void Hotkey::BuildHotkey(std::string const &context, json::Object const &hotkeys)
+{
+    for (auto const &command : hotkeys) {
+        json::Array const &command_hotkeys = command.second;
+
+        hotkey_visitor visitor{context, command.first, cmd_map};
+        for (auto const &hotkey : command_hotkeys)
+            hotkey.Accept(visitor);
+        backup_config_file |= visitor.needs_backup;
+    }
 }
 
-std::string Hotkey::Scan(std::string const& context, std::string const& str, bool always) const {
-	const std::string *local = nullptr, *dfault = nullptr;
-
-	std::vector<const Combo *>::const_iterator index, end;
-	for (std::tie(index, end) = boost::equal_range(str_map, str, combo_cmp()); index != end; ++index) {
-		std::string const& ctext = (*index)->Context();
-
-		if (always && ctext == "Always") {
-			LOG_D("agi/hotkey/found") << "Found: " << str << "  Context (req/found): " << context << "/Always   Command: " << (*index)->CmdName();
-			return (*index)->CmdName();
-		}
-		if (ctext == "Default")
-			dfault = &(*index)->CmdName();
-		else if (ctext == context)
-			local = &(*index)->CmdName();
-	}
-
-	if (local) {
-		LOG_D("agi/hotkey/found") << "Found: " << str << "  Context: " << context << "  Command: " << *local;
-		return *local;
-	}
-	if (dfault) {
-		LOG_D("agi/hotkey/found") << "Found: " << str << "  Context (req/found): " << context << "/Default   Command: " << *dfault;
-		return *dfault;
-	}
-
-	return "";
+std::string Hotkey::Scan(std::string const &context, std::string const &str, bool always) const
+{
+    const std::string *local = nullptr, *dfault = nullptr;
+
+    std::vector<const Combo *>::const_iterator index, end;
+    for (std::tie(index, end) = boost::equal_range(str_map, str, combo_cmp()); index != end; ++index) {
+        std::string const &ctext = (*index)->Context();
+
+        if (always && ctext == "Always") {
+            LOG_D("agi/hotkey/found") << "Found: " << str << "  Context (req/found): " << context << "/Always   Command: " << (*index)->CmdName();
+            return (*index)->CmdName();
+        }
+        if (ctext == "Default")
+            dfault = &(*index)->CmdName();
+        else if (ctext == context)
+            local = &(*index)->CmdName();
+    }
+
+    if (local) {
+        LOG_D("agi/hotkey/found") << "Found: " << str << "  Context: " << context << "  Command: " << *local;
+        return *local;
+    }
+    if (dfault) {
+        LOG_D("agi/hotkey/found") << "Found: " << str << "  Context (req/found): " << context << "/Default   Command: " << *dfault;
+        return *dfault;
+    }
+
+    return "";
 }
 
-bool Hotkey::HasHotkey(std::string const& context, std::string const& str) const {
-	std::vector<const Combo *>::const_iterator index, end;
-	for (std::tie(index, end) = boost::equal_range(str_map, str, combo_cmp()); index != end; ++index) {
-		if (context == (*index)->Context())
-			return true;
-	}
-	return false;
+bool Hotkey::HasHotkey(std::string const &context, std::string const &str) const
+{
+    std::vector<const Combo *>::const_iterator index, end;
+    for (std::tie(index, end) = boost::equal_range(str_map, str, combo_cmp()); index != end; ++index) {
+        if (context == (*index)->Context())
+            return true;
+    }
+    return false;
 }
 
-std::vector<std::string> Hotkey::GetHotkeys(std::string const& context, std::string const& command) const {
-	std::vector<std::string> ret;
+std::vector<std::string> Hotkey::GetHotkeys(std::string const &context, std::string const &command) const
+{
+    std::vector<std::string> ret;
 
-	HotkeyMap::const_iterator it, end;
-	for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
-		std::string const& ctext = it->second.Context();
-		if (ctext == "Always" || ctext == "Default" || ctext == context)
-			ret.emplace_back(it->second.Str());
-	}
+    HotkeyMap::const_iterator it, end;
+    for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
+        std::string const &ctext = it->second.Context();
+        if (ctext == "Always" || ctext == "Default" || ctext == context)
+            ret.emplace_back(it->second.Str());
+    }
 
-	sort(ret.begin(), ret.end());
-	ret.erase(unique(ret.begin(), ret.end()), ret.end());
+    sort(ret.begin(), ret.end());
+    ret.erase(unique(ret.begin(), ret.end()), ret.end());
 
-	return ret;
+    return ret;
 }
 
-std::string Hotkey::GetHotkey(std::string const& context, std::string const& command) const {
-	std::string ret;
-	HotkeyMap::const_iterator it, end;
-	for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
-		std::string const& ctext = it->second.Context();
-		if (ctext == context) return it->second.Str();
-		if (ctext == "Default")
-			ret = it->second.Str();
-		else if (ret.empty() && ctext == "Always")
-			ret = it->second.Str();
-	}
-	return ret;
+std::string Hotkey::GetHotkey(std::string const &context, std::string const &command) const
+{
+    std::string ret;
+    HotkeyMap::const_iterator it, end;
+    for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
+        std::string const &ctext = it->second.Context();
+        if (ctext == context) return it->second.Str();
+        if (ctext == "Default")
+            ret = it->second.Str();
+        else if (ret.empty() && ctext == "Always")
+            ret = it->second.Str();
+    }
+    return ret;
 }
 
-void Hotkey::Flush() {
-	json::Object root;
+void Hotkey::Flush()
+{
+    json::Object root;
 
-	for (auto const& combo : str_map) {
-		auto const& keys = combo->Str();
-		if (keys.empty()) continue;
+    for (auto const &combo : str_map) {
+        auto const &keys = combo->Str();
+        if (keys.empty()) continue;
 
-		json::Object& context = root[combo->Context()];
-		json::Array& combo_array = context[combo->CmdName()];
-		combo_array.push_back(keys);
-	}
+        json::Object &context = root[combo->Context()];
+        json::Array &combo_array = context[combo->CmdName()];
+        combo_array.push_back(keys);
+    }
 
-	if (backup_config_file && fs::FileExists(config_file) && !fs::FileExists(config_file.string() + ".3_1"))
-		fs::Copy(config_file, config_file.string() + ".3_1");
+    if (backup_config_file && fs::FileExists(config_file) && !fs::FileExists(config_file.string() + ".3_1"))
+        fs::Copy(config_file, config_file.string() + ".3_1");
 
-	io::Save file(config_file);
-	JsonWriter::Write(root, file.Get());
+    io::Save file(config_file);
+    JsonWriter::Write(root, file.Get());
 }
 
-void Hotkey::UpdateStrMap() {
-	str_map.clear();
-	str_map.reserve(cmd_map.size());
-	for (auto const& combo : cmd_map)
-		str_map.push_back(&combo.second);
+void Hotkey::UpdateStrMap()
+{
+    str_map.clear();
+    str_map.reserve(cmd_map.size());
+    for (auto const &combo : cmd_map)
+        str_map.push_back(&combo.second);
 
-	sort(begin(str_map), end(str_map), combo_cmp());
+    sort(begin(str_map), end(str_map), combo_cmp());
 }
 
-void Hotkey::SetHotkeyMap(HotkeyMap new_map) {
-	cmd_map = std::move(new_map);
-	UpdateStrMap();
-	Flush();
-	HotkeysChanged();
+void Hotkey::SetHotkeyMap(HotkeyMap new_map)
+{
+    cmd_map = std::move(new_map);
+    UpdateStrMap();
+    Flush();
+    HotkeysChanged();
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/io.cpp b/libaegisub/common/io.cpp
index e133a71abbd0ca7944f0b6d02f1895483ed97a93..de00c057867cd529675bacb5b76933b2bb229f76 100644
--- a/libaegisub/common/io.cpp
+++ b/libaegisub/common/io.cpp
@@ -28,49 +28,51 @@
 #include <boost/filesystem/operations.hpp>
 
 namespace agi {
-	namespace io {
+namespace io {
 
-std::unique_ptr<std::istream> Open(fs::path const& file, bool binary) {
-	LOG_D("agi/io/open/file") << file;
+std::unique_ptr<std::istream> Open(fs::path const &file, bool binary)
+{
+    LOG_D("agi/io/open/file") << file;
 
-	auto stream = agi::make_unique<boost::filesystem::ifstream>(file, (binary ? std::ios::binary : std::ios::in));
-	if (stream->fail()) {
-		acs::CheckFileRead(file);
-		throw IOFatal("Unknown fatal error occurred opening " + file.string());
-	}
+    auto stream = agi::make_unique<boost::filesystem::ifstream>(file, (binary ? std::ios::binary : std::ios::in));
+    if (stream->fail()) {
+        acs::CheckFileRead(file);
+        throw IOFatal("Unknown fatal error occurred opening " + file.string());
+    }
 
-	return std::unique_ptr<std::istream>(stream.release());
+    return std::unique_ptr<std::istream>(stream.release());
 }
 
-Save::Save(fs::path const& file, bool binary)
-: file_name(file)
-, tmp_name(unique_path(file.parent_path()/(file.stem().string() + "_tmp_%%%%" + file.extension().string())))
+Save::Save(fs::path const &file, bool binary)
+    : file_name(file)
+    , tmp_name(unique_path(file.parent_path() / (file.stem().string() + "_tmp_%%%%" + file.extension().string())))
 {
-	LOG_D("agi/io/save/file") << file;
+    LOG_D("agi/io/save/file") << file;
 
-	fp = agi::make_unique<boost::filesystem::ofstream>(tmp_name, binary ? std::ios::binary : std::ios::out);
-	if (!fp->good()) {
-		acs::CheckDirWrite(file.parent_path());
-		acs::CheckFileWrite(file);
-		throw fs::WriteDenied(tmp_name);
-	}
+    fp = agi::make_unique<boost::filesystem::ofstream>(tmp_name, binary ? std::ios::binary : std::ios::out);
+    if (!fp->good()) {
+        acs::CheckDirWrite(file.parent_path());
+        acs::CheckFileWrite(file);
+        throw fs::WriteDenied(tmp_name);
+    }
 }
 
-Save::~Save() noexcept(false) {
-	fp.reset(); // Need to close before rename on Windows to unlock the file
-	for (int i = 0; i < 10; ++i) {
-		try {
-			fs::Rename(tmp_name, file_name);
-			return;
-		}
-		catch (agi::fs::FileSystemError const&) {
-			// Retry up to ten times in case it's just locked by a poorly-written antivirus scanner
-			if (i == 9)
-				throw;
-			util::sleep_for(100);
-		}
-	}
+Save::~Save() noexcept(false)
+{
+    fp.reset(); // Need to close before rename on Windows to unlock the file
+    for (int i = 0; i < 10; ++i) {
+        try {
+            fs::Rename(tmp_name, file_name);
+            return;
+        }
+        catch (agi::fs::FileSystemError const &) {
+            // Retry up to ten times in case it's just locked by a poorly-written antivirus scanner
+            if (i == 9)
+                throw;
+            util::sleep_for(100);
+        }
+    }
 }
 
-	} // namespace io
+} // namespace io
 } // namespace agi
diff --git a/libaegisub/common/json.cpp b/libaegisub/common/json.cpp
index 493ca2f5d8d8e1e32337b32a6f7c3f45451d14f6..597280e1b3cb949900e145df857040e7d133d4b3 100644
--- a/libaegisub/common/json.cpp
+++ b/libaegisub/common/json.cpp
@@ -25,38 +25,44 @@
 
 #include <boost/interprocess/streams/bufferstream.hpp>
 
-namespace agi { namespace json_util {
-
-json::UnknownElement parse(std::istream &stream) {
-	try {
-		json::UnknownElement root;
-		json::Reader::Read(root, stream);
-		return root;
-	} catch (json::Reader::ParseException& e) {
-		LOG_E("json/parse") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
-		throw;
-	} catch (json::Exception& e) {
-		LOG_E("json/parse") << "json::Exception: " << e.what();
-		throw;
-	}
+namespace agi {
+namespace json_util {
+
+json::UnknownElement parse(std::istream &stream)
+{
+    try {
+        json::UnknownElement root;
+        json::Reader::Read(root, stream);
+        return root;
+    }
+    catch (json::Reader::ParseException &e) {
+        LOG_E("json/parse") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
+        throw;
+    }
+    catch (json::Exception &e) {
+        LOG_E("json/parse") << "json::Exception: " << e.what();
+        throw;
+    }
 }
 
-json::UnknownElement file(agi::fs::path const& file, std::pair<const char *, size_t> default_config) {
-	try {
-		if (fs::FileExists(file))
-			return parse(*io::Open(file));
-	}
-	catch (fs::FileNotFound const&) {
-		// Not an error
-	}
-	catch (json::Exception&) {
-		// Already logged in parse
-	}
-	catch (agi::Exception& e) {
-		LOG_E("json/file") << "Unexpected error when reading config file " << file << ": " << e.GetMessage();
-	}
-	boost::interprocess::ibufferstream stream(default_config.first, default_config.second);
-	return parse(stream);
+json::UnknownElement file(agi::fs::path const &file, std::pair<const char *, size_t> default_config)
+{
+    try {
+        if (fs::FileExists(file))
+            return parse(*io::Open(file));
+    }
+    catch (fs::FileNotFound const &) {
+        // Not an error
+    }
+    catch (json::Exception &) {
+        // Already logged in parse
+    }
+    catch (agi::Exception &e) {
+        LOG_E("json/file") << "Unexpected error when reading config file " << file << ": " << e.GetMessage();
+    }
+    boost::interprocess::ibufferstream stream(default_config.first, default_config.second);
+    return parse(stream);
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/kana_table.cpp b/libaegisub/common/kana_table.cpp
index 0c7c352287a412a06f7162c966c2dd5409166b43..cc1207deca093c01ad42d7a997984b374b175b65 100644
--- a/libaegisub/common/kana_table.cpp
+++ b/libaegisub/common/kana_table.cpp
@@ -20,601 +20,604 @@
 
 namespace {
 agi::kana_pair kana_to_romaji[] = {
-	{"\xE3\x81\x81", "a"},               // ぁ
-	{"\xE3\x81\x82", "a"},               // あ
-	{"\xE3\x81\x83", "i"},               // ぃ
-	{"\xE3\x81\x84", "i"},               // い
-	{"\xE3\x81\x85", "u"},               // ぅ
-	{"\xE3\x81\x86", "u"},               // う
-	{"\xE3\x81\x87", "e"},               // ぇ
-	{"\xE3\x81\x88", "e"},               // え
-	{"\xE3\x81\x89", "o"},               // ぉ
-	{"\xE3\x81\x8A", "o"},               // お
-	{"\xE3\x81\x8B", "ka"},              // か
-	{"\xE3\x81\x8C", "ga"},              // が
-	{"\xE3\x81\x8D", "ki"},              // き
-	{"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
-	{"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
-	{"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
-	{"\xE3\x81\x8E", "gi"},              // ぎ
-	{"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
-	{"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
-	{"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
-	{"\xE3\x81\x8F", "ku"},              // く
-	{"\xE3\x81\x90", "gu"},              // ぐ
-	{"\xE3\x81\x91", "ke"},              // け
-	{"\xE3\x81\x92", "ge"},              // げ
-	{"\xE3\x81\x93", "ko"},              // こ
-	{"\xE3\x81\x94", "go"},              // ご
-	{"\xE3\x81\x95", "sa"},              // さ
-	{"\xE3\x81\x96", "za"},              // ざ
-	{"\xE3\x81\x97", "shi"},             // し
-	{"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
-	{"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
-	{"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
-	{"\xE3\x81\x98", "ji"},              // じ
-	{"\xE3\x81\x98\xE3\x82\x83", "ja"},  // じゃ
-	{"\xE3\x81\x98\xE3\x82\x85", "ju"},  // じゅ
-	{"\xE3\x81\x98\xE3\x82\x87", "jo"},  // じょ
-	{"\xE3\x81\x99", "su"},              // す
-	{"\xE3\x81\x9A", "zu"},              // ず
-	{"\xE3\x81\x9B", "se"},              // せ
-	{"\xE3\x81\x9C", "ze"},              // ぜ
-	{"\xE3\x81\x9D", "so"},              // そ
-	{"\xE3\x81\x9E", "zo"},              // ぞ
-	{"\xE3\x81\x9F", "ta"},              // た
-	{"\xE3\x81\xA0", "da"},              // だ
-	{"\xE3\x81\xA1", "chi"},             // ち
-	{"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
-	{"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
-	{"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
-	{"\xE3\x81\xA2", "ji"},              // ぢ
-	{"\xE3\x81\xA2\xE3\x82\x83", "ja"},  // ぢゃ
-	{"\xE3\x81\xA2\xE3\x82\x85", "ju"},  // ぢゅ
-	{"\xE3\x81\xA2\xE3\x82\x87", "jo"},  // ぢょ
-	{"\xE3\x81\xA3", "c"},               // っ
-	{"\xE3\x81\xA3", "k"},               // っ
-	{"\xE3\x81\xA3", "p"},               // っ
-	{"\xE3\x81\xA3", "s"},               // っ
-	{"\xE3\x81\xA3", "t"},               // っ
-	{"\xE3\x81\xA4", "tsu"},             // つ
-	{"\xE3\x81\xA5", "zu"},              // づ
-	{"\xE3\x81\xA6", "te"},              // て
-	{"\xE3\x81\xA7", "de"},              // で
-	{"\xE3\x81\xA8", "to"},              // と
-	{"\xE3\x81\xA9", "do"},              // ど
-	{"\xE3\x81\xAA", "na"},              // な
-	{"\xE3\x81\xAB", "ni"},              // に
-	{"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
-	{"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
-	{"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
-	{"\xE3\x81\xAC", "nu"},              // ぬ
-	{"\xE3\x81\xAD", "ne"},              // ね
-	{"\xE3\x81\xAE", "no"},              // の
-	{"\xE3\x81\xAF", "ha"},              // は
-	{"\xE3\x81\xAF", "wa"},              // は
-	{"\xE3\x81\xB0", "ba"},              // ば
-	{"\xE3\x81\xB1", "pa"},              // ぱ
-	{"\xE3\x81\xB2", "hi"},              // ひ
-	{"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
-	{"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
-	{"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
-	{"\xE3\x81\xB3", "bi"},              // び
-	{"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
-	{"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
-	{"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
-	{"\xE3\x81\xB4", "pi"},              // ぴ
-	{"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
-	{"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
-	{"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
-	{"\xE3\x81\xB5", "fu"},              // ふ
-	{"\xE3\x81\xB6", "bu"},              // ぶ
-	{"\xE3\x81\xB7", "pu"},              // ぷ
-	{"\xE3\x81\xB8", "he"},              // へ
-	{"\xE3\x81\xB8", "e"},               // へ
-	{"\xE3\x81\xB9", "be"},              // べ
-	{"\xE3\x81\xBA", "pe"},              // ぺ
-	{"\xE3\x81\xBB", "ho"},              // ほ
-	{"\xE3\x81\xBC", "bo"},              // ぼ
-	{"\xE3\x81\xBD", "po"},              // ぽ
-	{"\xE3\x81\xBE", "ma"},              // ま
-	{"\xE3\x81\xBF", "mi"},              // み
-	{"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
-	{"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
-	{"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
-	{"\xE3\x82\x80", "mu"},              // む
-	{"\xE3\x82\x81", "me"},              // め
-	{"\xE3\x82\x82", "mo"},              // も
-	{"\xE3\x82\x84", "ya"},              // や
-	{"\xE3\x82\x86", "yu"},              // ゆ
-	{"\xE3\x82\x88", "yo"},              // よ
-	{"\xE3\x82\x89", "ra"},              // ら
-	{"\xE3\x82\x8A", "ri"},              // り
-	{"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
-	{"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
-	{"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
-	{"\xE3\x82\x8B", "ru"},              // る
-	{"\xE3\x82\x8C", "re"},              // れ
-	{"\xE3\x82\x8D", "ro"},              // ろ
-	{"\xE3\x82\x8F", "wa"},              // わ
-	{"\xE3\x82\x90", "wi"},              // ゐ
-	{"\xE3\x82\x91", "we"},              // ゑ
-	{"\xE3\x82\x92", "wo"},              // を
-	{"\xE3\x82\x93", "m"},               // ん
-	{"\xE3\x82\x93", "n"},               // ん
-	{"\xE3\x82\xA1", "a"},               // ァ
-	{"\xE3\x82\xA2", "a"},               // ア
-	{"\xE3\x82\xA3", "i"},               // ィ
-	{"\xE3\x82\xA4", "i"},               // イ
-	{"\xE3\x82\xA4\xE3\x82\xA7", "ye"},  // イェ
-	{"\xE3\x82\xA5", "u"},               // ゥ
-	{"\xE3\x82\xA6", "u"},               // ウ
-	{"\xE3\x82\xA6\xE3\x82\xA3", "wi"},  // ウィ
-	{"\xE3\x82\xA6\xE3\x82\xA7", "we"},  // ウェ
-	{"\xE3\x82\xA6\xE3\x82\xA9", "wo"},  // ウォ
-	{"\xE3\x82\xA7", "e"},               // ェ
-	{"\xE3\x82\xA8", "e"},               // エ
-	{"\xE3\x82\xA9", "o"},               // ォ
-	{"\xE3\x82\xAA", "o"},               // オ
-	{"\xE3\x82\xAB", "ka"},              // カ
-	{"\xE3\x82\xAC", "ga"},              // ガ
-	{"\xE3\x82\xAD", "ki"},              // キ
-	{"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
-	{"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
-	{"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
-	{"\xE3\x82\xAE", "gi"},              // ギ
-	{"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
-	{"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
-	{"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
-	{"\xE3\x82\xAF", "ku"},              // ク
-	{"\xE3\x82\xB0", "gu"},              // グ
-	{"\xE3\x82\xB1", "ke"},              // ケ
-	{"\xE3\x82\xB2", "ge"},              // ゲ
-	{"\xE3\x82\xB3", "ko"},              // コ
-	{"\xE3\x82\xB4", "go"},              // ゴ
-	{"\xE3\x82\xB5", "sa"},              // サ
-	{"\xE3\x82\xB6", "za"},              // ザ
-	{"\xE3\x82\xB7", "shi"},             // シ
-	{"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
-	{"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
-	{"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
-	{"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
-	{"\xE3\x82\xB8", "ji"},              // ジ
-	{"\xE3\x82\xB8\xE3\x82\xA7", "je"},  // ジェ
-	{"\xE3\x82\xB8\xE3\x83\xA3", "ja"},  // ジャ
-	{"\xE3\x82\xB8\xE3\x83\xA5", "ju"},  // ジュ
-	{"\xE3\x82\xB8\xE3\x83\xA7", "jo"},  // ジョ
-	{"\xE3\x82\xB9", "su"},              // ス
-	{"\xE3\x82\xBA", "zu"},              // ズ
-	{"\xE3\x82\xBB", "se"},              // セ
-	{"\xE3\x82\xBC", "ze"},              // ゼ
-	{"\xE3\x82\xBD", "so"},              // ソ
-	{"\xE3\x82\xBE", "zo"},              // ゾ
-	{"\xE3\x82\xBF", "ta"},              // タ
-	{"\xE3\x83\x80", "da"},              // ダ
-	{"\xE3\x83\x81", "chi"},             // チ
-	{"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
-	{"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
-	{"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
-	{"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
-	{"\xE3\x83\x82", "ji"},              // ヂ
-	{"\xE3\x83\x82\xE3\x83\xA3", "ja"},  // ヂャ
-	{"\xE3\x83\x82\xE3\x83\xA5", "ju"},  // ヂュ
-	{"\xE3\x83\x82\xE3\x83\xA7", "jo"},  // ヂョ
-	{"\xE3\x83\x83", "c"},               // ッ
-	{"\xE3\x83\x83", "k"},               // ッ
-	{"\xE3\x83\x83", "p"},               // ッ
-	{"\xE3\x83\x83", "s"},               // ッ
-	{"\xE3\x83\x83", "t"},               // ッ
-	{"\xE3\x83\x84", "tsu"},             // ツ
-	{"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
-	{"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
-	{"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
-	{"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
-	{"\xE3\x83\x85", "zu"},              // ヅ
-	{"\xE3\x83\x86", "te"},              // テ
-	{"\xE3\x83\x86\xE3\x82\xA3", "ti"},  // ティ
-	{"\xE3\x83\x86\xE3\x82\xA5", "tu"},  // テゥ
-	{"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
-	{"\xE3\x83\x87", "de"},              // デ
-	{"\xE3\x83\x87\xE3\x82\xA3", "di"},  // ディ
-	{"\xE3\x83\x87\xE3\x82\xA5", "du"},  // デゥ
-	{"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
-	{"\xE3\x83\x88", "to"},              // ト
-	{"\xE3\x83\x89", "do"},              // ド
-	{"\xE3\x83\x8A", "na"},              // ナ
-	{"\xE3\x83\x8B", "ni"},              // ニ
-	{"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
-	{"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
-	{"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
-	{"\xE3\x83\x8C", "nu"},              // ヌ
-	{"\xE3\x83\x8D", "ne"},              // ネ
-	{"\xE3\x83\x8E", "no"},              // ノ
-	{"\xE3\x83\x8F", "ha"},              // ハ
-	{"\xE3\x83\x90", "ba"},              // バ
-	{"\xE3\x83\x91", "pa"},              // パ
-	{"\xE3\x83\x92", "hi"},              // ヒ
-	{"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
-	{"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
-	{"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
-	{"\xE3\x83\x93", "bi"},              // ビ
-	{"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
-	{"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
-	{"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
-	{"\xE3\x83\x94", "pi"},              // ピ
-	{"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
-	{"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
-	{"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
-	{"\xE3\x83\x95", "fu"},              // フ
-	{"\xE3\x83\x95\xE3\x82\xA1", "fa"},  // ファ
-	{"\xE3\x83\x95\xE3\x82\xA3", "fi"},  // フィ
-	{"\xE3\x83\x95\xE3\x82\xA7", "fe"},  // フェ
-	{"\xE3\x83\x95\xE3\x82\xA9", "fo"},  // フォ
-	{"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
-	{"\xE3\x83\x96", "bu"},              // ブ
-	{"\xE3\x83\x97", "pu"},              // プ
-	{"\xE3\x83\x98", "he"},              // ヘ
-	{"\xE3\x83\x99", "be"},              // ベ
-	{"\xE3\x83\x9A", "pe"},              // ペ
-	{"\xE3\x83\x9B", "ho"},              // ホ
-	{"\xE3\x83\x9C", "bo"},              // ボ
-	{"\xE3\x83\x9D", "po"},              // ポ
-	{"\xE3\x83\x9E", "ma"},              // マ
-	{"\xE3\x83\x9F", "mi"},              // ミ
-	{"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
-	{"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
-	{"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
-	{"\xE3\x83\xA0", "mu"},              // ム
-	{"\xE3\x83\xA1", "me"},              // メ
-	{"\xE3\x83\xA2", "mo"},              // モ
-	{"\xE3\x83\xA4", "ya"},              // ヤ
-	{"\xE3\x83\xA6", "yu"},              // ユ
-	{"\xE3\x83\xA8", "yo"},              // ヨ
-	{"\xE3\x83\xA9", "ra"},              // ラ
-	{"\xE3\x83\xAA", "ri"},              // リ
-	{"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
-	{"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
-	{"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
-	{"\xE3\x83\xAB", "ru"},              // ル
-	{"\xE3\x83\xAC", "re"},              // レ
-	{"\xE3\x83\xAD", "ro"},              // ロ
-	{"\xE3\x83\xAF", "wa"},              // ワ
-	{"\xE3\x83\xB0", "wi"},              // ヰ
-	{"\xE3\x83\xB1", "we"},              // ヱ
-	{"\xE3\x83\xB2", "wo"},              // ヲ
-	{"\xE3\x83\xB3", "m"},               // ン
-	{"\xE3\x83\xB3", "n"},               // ン
-	{"\xE3\x83\xB4", "vu"},              // ヴ
-	{"\xE3\x83\xB4\xE3\x82\xA1", "va"},  // ヴァ
-	{"\xE3\x83\xB4\xE3\x82\xA3", "vi"},  // ヴィ
-	{"\xE3\x83\xB4\xE3\x82\xA7", "ve"},  // ヴェ
-	{"\xE3\x83\xB4\xE3\x82\xA9", "vo"},  // ヴォ
-	{"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
-	{"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
-	{"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
-	{"\xE3\x83\xBC", "a"},               // ー
-	{"\xE3\x83\xBC", "e"},               // ー
-	{"\xE3\x83\xBC", "i"},               // ー
-	{"\xE3\x83\xBC", "o"},               // ー
-	{"\xE3\x83\xBC", "u"},               // ー
+    {"\xE3\x81\x81", "a"},               // ぁ
+    {"\xE3\x81\x82", "a"},               // あ
+    {"\xE3\x81\x83", "i"},               // ぃ
+    {"\xE3\x81\x84", "i"},               // い
+    {"\xE3\x81\x85", "u"},               // ぅ
+    {"\xE3\x81\x86", "u"},               // う
+    {"\xE3\x81\x87", "e"},               // ぇ
+    {"\xE3\x81\x88", "e"},               // え
+    {"\xE3\x81\x89", "o"},               // ぉ
+    {"\xE3\x81\x8A", "o"},               // お
+    {"\xE3\x81\x8B", "ka"},              // か
+    {"\xE3\x81\x8C", "ga"},              // が
+    {"\xE3\x81\x8D", "ki"},              // き
+    {"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
+    {"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
+    {"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
+    {"\xE3\x81\x8E", "gi"},              // ぎ
+    {"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
+    {"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
+    {"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
+    {"\xE3\x81\x8F", "ku"},              // く
+    {"\xE3\x81\x90", "gu"},              // ぐ
+    {"\xE3\x81\x91", "ke"},              // け
+    {"\xE3\x81\x92", "ge"},              // げ
+    {"\xE3\x81\x93", "ko"},              // こ
+    {"\xE3\x81\x94", "go"},              // ご
+    {"\xE3\x81\x95", "sa"},              // さ
+    {"\xE3\x81\x96", "za"},              // ざ
+    {"\xE3\x81\x97", "shi"},             // し
+    {"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
+    {"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
+    {"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
+    {"\xE3\x81\x98", "ji"},              // じ
+    {"\xE3\x81\x98\xE3\x82\x83", "ja"},  // じゃ
+    {"\xE3\x81\x98\xE3\x82\x85", "ju"},  // じゅ
+    {"\xE3\x81\x98\xE3\x82\x87", "jo"},  // じょ
+    {"\xE3\x81\x99", "su"},              // す
+    {"\xE3\x81\x9A", "zu"},              // ず
+    {"\xE3\x81\x9B", "se"},              // せ
+    {"\xE3\x81\x9C", "ze"},              // ぜ
+    {"\xE3\x81\x9D", "so"},              // そ
+    {"\xE3\x81\x9E", "zo"},              // ぞ
+    {"\xE3\x81\x9F", "ta"},              // た
+    {"\xE3\x81\xA0", "da"},              // だ
+    {"\xE3\x81\xA1", "chi"},             // ち
+    {"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
+    {"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
+    {"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
+    {"\xE3\x81\xA2", "ji"},              // ぢ
+    {"\xE3\x81\xA2\xE3\x82\x83", "ja"},  // ぢゃ
+    {"\xE3\x81\xA2\xE3\x82\x85", "ju"},  // ぢゅ
+    {"\xE3\x81\xA2\xE3\x82\x87", "jo"},  // ぢょ
+    {"\xE3\x81\xA3", "c"},               // っ
+    {"\xE3\x81\xA3", "k"},               // っ
+    {"\xE3\x81\xA3", "p"},               // っ
+    {"\xE3\x81\xA3", "s"},               // っ
+    {"\xE3\x81\xA3", "t"},               // っ
+    {"\xE3\x81\xA4", "tsu"},             // つ
+    {"\xE3\x81\xA5", "zu"},              // づ
+    {"\xE3\x81\xA6", "te"},              // て
+    {"\xE3\x81\xA7", "de"},              // で
+    {"\xE3\x81\xA8", "to"},              // と
+    {"\xE3\x81\xA9", "do"},              // ど
+    {"\xE3\x81\xAA", "na"},              // な
+    {"\xE3\x81\xAB", "ni"},              // に
+    {"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
+    {"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
+    {"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
+    {"\xE3\x81\xAC", "nu"},              // ぬ
+    {"\xE3\x81\xAD", "ne"},              // ね
+    {"\xE3\x81\xAE", "no"},              // の
+    {"\xE3\x81\xAF", "ha"},              // は
+    {"\xE3\x81\xAF", "wa"},              // は
+    {"\xE3\x81\xB0", "ba"},              // ば
+    {"\xE3\x81\xB1", "pa"},              // ぱ
+    {"\xE3\x81\xB2", "hi"},              // ひ
+    {"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
+    {"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
+    {"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
+    {"\xE3\x81\xB3", "bi"},              // び
+    {"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
+    {"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
+    {"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
+    {"\xE3\x81\xB4", "pi"},              // ぴ
+    {"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
+    {"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
+    {"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
+    {"\xE3\x81\xB5", "fu"},              // ふ
+    {"\xE3\x81\xB6", "bu"},              // ぶ
+    {"\xE3\x81\xB7", "pu"},              // ぷ
+    {"\xE3\x81\xB8", "he"},              // へ
+    {"\xE3\x81\xB8", "e"},               // へ
+    {"\xE3\x81\xB9", "be"},              // べ
+    {"\xE3\x81\xBA", "pe"},              // ぺ
+    {"\xE3\x81\xBB", "ho"},              // ほ
+    {"\xE3\x81\xBC", "bo"},              // ぼ
+    {"\xE3\x81\xBD", "po"},              // ぽ
+    {"\xE3\x81\xBE", "ma"},              // ま
+    {"\xE3\x81\xBF", "mi"},              // み
+    {"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
+    {"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
+    {"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
+    {"\xE3\x82\x80", "mu"},              // む
+    {"\xE3\x82\x81", "me"},              // め
+    {"\xE3\x82\x82", "mo"},              // も
+    {"\xE3\x82\x84", "ya"},              // や
+    {"\xE3\x82\x86", "yu"},              // ゆ
+    {"\xE3\x82\x88", "yo"},              // よ
+    {"\xE3\x82\x89", "ra"},              // ら
+    {"\xE3\x82\x8A", "ri"},              // り
+    {"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
+    {"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
+    {"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
+    {"\xE3\x82\x8B", "ru"},              // る
+    {"\xE3\x82\x8C", "re"},              // れ
+    {"\xE3\x82\x8D", "ro"},              // ろ
+    {"\xE3\x82\x8F", "wa"},              // わ
+    {"\xE3\x82\x90", "wi"},              // ゐ
+    {"\xE3\x82\x91", "we"},              // ゑ
+    {"\xE3\x82\x92", "wo"},              // を
+    {"\xE3\x82\x93", "m"},               // ん
+    {"\xE3\x82\x93", "n"},               // ん
+    {"\xE3\x82\xA1", "a"},               // ァ
+    {"\xE3\x82\xA2", "a"},               // ア
+    {"\xE3\x82\xA3", "i"},               // ィ
+    {"\xE3\x82\xA4", "i"},               // イ
+    {"\xE3\x82\xA4\xE3\x82\xA7", "ye"},  // イェ
+    {"\xE3\x82\xA5", "u"},               // ゥ
+    {"\xE3\x82\xA6", "u"},               // ウ
+    {"\xE3\x82\xA6\xE3\x82\xA3", "wi"},  // ウィ
+    {"\xE3\x82\xA6\xE3\x82\xA7", "we"},  // ウェ
+    {"\xE3\x82\xA6\xE3\x82\xA9", "wo"},  // ウォ
+    {"\xE3\x82\xA7", "e"},               // ェ
+    {"\xE3\x82\xA8", "e"},               // エ
+    {"\xE3\x82\xA9", "o"},               // ォ
+    {"\xE3\x82\xAA", "o"},               // オ
+    {"\xE3\x82\xAB", "ka"},              // カ
+    {"\xE3\x82\xAC", "ga"},              // ガ
+    {"\xE3\x82\xAD", "ki"},              // キ
+    {"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
+    {"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
+    {"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
+    {"\xE3\x82\xAE", "gi"},              // ギ
+    {"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
+    {"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
+    {"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
+    {"\xE3\x82\xAF", "ku"},              // ク
+    {"\xE3\x82\xB0", "gu"},              // グ
+    {"\xE3\x82\xB1", "ke"},              // ケ
+    {"\xE3\x82\xB2", "ge"},              // ゲ
+    {"\xE3\x82\xB3", "ko"},              // コ
+    {"\xE3\x82\xB4", "go"},              // ゴ
+    {"\xE3\x82\xB5", "sa"},              // サ
+    {"\xE3\x82\xB6", "za"},              // ザ
+    {"\xE3\x82\xB7", "shi"},             // シ
+    {"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
+    {"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
+    {"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
+    {"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
+    {"\xE3\x82\xB8", "ji"},              // ジ
+    {"\xE3\x82\xB8\xE3\x82\xA7", "je"},  // ジェ
+    {"\xE3\x82\xB8\xE3\x83\xA3", "ja"},  // ジャ
+    {"\xE3\x82\xB8\xE3\x83\xA5", "ju"},  // ジュ
+    {"\xE3\x82\xB8\xE3\x83\xA7", "jo"},  // ジョ
+    {"\xE3\x82\xB9", "su"},              // ス
+    {"\xE3\x82\xBA", "zu"},              // ズ
+    {"\xE3\x82\xBB", "se"},              // セ
+    {"\xE3\x82\xBC", "ze"},              // ゼ
+    {"\xE3\x82\xBD", "so"},              // ソ
+    {"\xE3\x82\xBE", "zo"},              // ゾ
+    {"\xE3\x82\xBF", "ta"},              // タ
+    {"\xE3\x83\x80", "da"},              // ダ
+    {"\xE3\x83\x81", "chi"},             // チ
+    {"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
+    {"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
+    {"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
+    {"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
+    {"\xE3\x83\x82", "ji"},              // ヂ
+    {"\xE3\x83\x82\xE3\x83\xA3", "ja"},  // ヂャ
+    {"\xE3\x83\x82\xE3\x83\xA5", "ju"},  // ヂュ
+    {"\xE3\x83\x82\xE3\x83\xA7", "jo"},  // ヂョ
+    {"\xE3\x83\x83", "c"},               // ッ
+    {"\xE3\x83\x83", "k"},               // ッ
+    {"\xE3\x83\x83", "p"},               // ッ
+    {"\xE3\x83\x83", "s"},               // ッ
+    {"\xE3\x83\x83", "t"},               // ッ
+    {"\xE3\x83\x84", "tsu"},             // ツ
+    {"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
+    {"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
+    {"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
+    {"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
+    {"\xE3\x83\x85", "zu"},              // ヅ
+    {"\xE3\x83\x86", "te"},              // テ
+    {"\xE3\x83\x86\xE3\x82\xA3", "ti"},  // ティ
+    {"\xE3\x83\x86\xE3\x82\xA5", "tu"},  // テゥ
+    {"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
+    {"\xE3\x83\x87", "de"},              // デ
+    {"\xE3\x83\x87\xE3\x82\xA3", "di"},  // ディ
+    {"\xE3\x83\x87\xE3\x82\xA5", "du"},  // デゥ
+    {"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
+    {"\xE3\x83\x88", "to"},              // ト
+    {"\xE3\x83\x89", "do"},              // ド
+    {"\xE3\x83\x8A", "na"},              // ナ
+    {"\xE3\x83\x8B", "ni"},              // ニ
+    {"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
+    {"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
+    {"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
+    {"\xE3\x83\x8C", "nu"},              // ヌ
+    {"\xE3\x83\x8D", "ne"},              // ネ
+    {"\xE3\x83\x8E", "no"},              // ノ
+    {"\xE3\x83\x8F", "ha"},              // ハ
+    {"\xE3\x83\x90", "ba"},              // バ
+    {"\xE3\x83\x91", "pa"},              // パ
+    {"\xE3\x83\x92", "hi"},              // ヒ
+    {"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
+    {"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
+    {"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
+    {"\xE3\x83\x93", "bi"},              // ビ
+    {"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
+    {"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
+    {"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
+    {"\xE3\x83\x94", "pi"},              // ピ
+    {"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
+    {"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
+    {"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
+    {"\xE3\x83\x95", "fu"},              // フ
+    {"\xE3\x83\x95\xE3\x82\xA1", "fa"},  // ファ
+    {"\xE3\x83\x95\xE3\x82\xA3", "fi"},  // フィ
+    {"\xE3\x83\x95\xE3\x82\xA7", "fe"},  // フェ
+    {"\xE3\x83\x95\xE3\x82\xA9", "fo"},  // フォ
+    {"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
+    {"\xE3\x83\x96", "bu"},              // ブ
+    {"\xE3\x83\x97", "pu"},              // プ
+    {"\xE3\x83\x98", "he"},              // ヘ
+    {"\xE3\x83\x99", "be"},              // ベ
+    {"\xE3\x83\x9A", "pe"},              // ペ
+    {"\xE3\x83\x9B", "ho"},              // ホ
+    {"\xE3\x83\x9C", "bo"},              // ボ
+    {"\xE3\x83\x9D", "po"},              // ポ
+    {"\xE3\x83\x9E", "ma"},              // マ
+    {"\xE3\x83\x9F", "mi"},              // ミ
+    {"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
+    {"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
+    {"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
+    {"\xE3\x83\xA0", "mu"},              // ム
+    {"\xE3\x83\xA1", "me"},              // メ
+    {"\xE3\x83\xA2", "mo"},              // モ
+    {"\xE3\x83\xA4", "ya"},              // ヤ
+    {"\xE3\x83\xA6", "yu"},              // ユ
+    {"\xE3\x83\xA8", "yo"},              // ヨ
+    {"\xE3\x83\xA9", "ra"},              // ラ
+    {"\xE3\x83\xAA", "ri"},              // リ
+    {"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
+    {"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
+    {"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
+    {"\xE3\x83\xAB", "ru"},              // ル
+    {"\xE3\x83\xAC", "re"},              // レ
+    {"\xE3\x83\xAD", "ro"},              // ロ
+    {"\xE3\x83\xAF", "wa"},              // ワ
+    {"\xE3\x83\xB0", "wi"},              // ヰ
+    {"\xE3\x83\xB1", "we"},              // ヱ
+    {"\xE3\x83\xB2", "wo"},              // ヲ
+    {"\xE3\x83\xB3", "m"},               // ン
+    {"\xE3\x83\xB3", "n"},               // ン
+    {"\xE3\x83\xB4", "vu"},              // ヴ
+    {"\xE3\x83\xB4\xE3\x82\xA1", "va"},  // ヴァ
+    {"\xE3\x83\xB4\xE3\x82\xA3", "vi"},  // ヴィ
+    {"\xE3\x83\xB4\xE3\x82\xA7", "ve"},  // ヴェ
+    {"\xE3\x83\xB4\xE3\x82\xA9", "vo"},  // ヴォ
+    {"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
+    {"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
+    {"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
+    {"\xE3\x83\xBC", "a"},               // ー
+    {"\xE3\x83\xBC", "e"},               // ー
+    {"\xE3\x83\xBC", "i"},               // ー
+    {"\xE3\x83\xBC", "o"},               // ー
+    {"\xE3\x83\xBC", "u"},               // ー
 };
 
 agi::kana_pair romaji_to_kana[] = {
-	{"\xE3\x81\x81", "a"},               // ぁ
-	{"\xE3\x81\x82", "a"},               // あ
-	{"\xE3\x82\xA1", "a"},               // ァ
-	{"\xE3\x82\xA2", "a"},               // ア
-	{"\xE3\x83\xBC", "a"},               // ー
-	{"\xE3\x81\xB0", "ba"},              // ば
-	{"\xE3\x83\x90", "ba"},              // バ
-	{"\xE3\x81\xB9", "be"},              // べ
-	{"\xE3\x83\x99", "be"},              // ベ
-	{"\xE3\x81\xB3", "bi"},              // び
-	{"\xE3\x83\x93", "bi"},              // ビ
-	{"\xE3\x81\xBC", "bo"},              // ぼ
-	{"\xE3\x83\x9C", "bo"},              // ボ
-	{"\xE3\x81\xB6", "bu"},              // ぶ
-	{"\xE3\x83\x96", "bu"},              // ブ
-	{"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
-	{"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
-	{"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
-	{"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
-	{"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
-	{"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
-	{"\xE3\x81\xA3", "c"},               // っ
-	{"\xE3\x83\x83", "c"},               // ッ
-	{"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
-	{"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
-	{"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
-	{"\xE3\x81\xA1", "chi"},             // ち
-	{"\xE3\x83\x81", "chi"},             // チ
-	{"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
-	{"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
-	{"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
-	{"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
-	{"\xE3\x81\xA0", "da"},              // だ
-	{"\xE3\x83\x80", "da"},              // ダ
-	{"\xE3\x81\xA7", "de"},              // で
-	{"\xE3\x83\x87", "de"},              // デ
-	{"\xE3\x83\x87\xE3\x82\xA3", "di"},  // ディ
-	{"\xE3\x81\xA9", "do"},              // ど
-	{"\xE3\x83\x89", "do"},              // ド
-	{"\xE3\x83\x87\xE3\x82\xA5", "du"},  // デゥ
-	{"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
-	{"\xE3\x81\x87", "e"},               // ぇ
-	{"\xE3\x81\x88", "e"},               // え
-	{"\xE3\x82\xA7", "e"},               // ェ
-	{"\xE3\x82\xA8", "e"},               // エ
-	{"\xE3\x83\xBC", "e"},               // ー
-	{"\xE3\x83\x95\xE3\x82\xA1", "fa"},  // ファ
-	{"\xE3\x83\x95\xE3\x82\xA7", "fe"},  // フェ
-	{"\xE3\x83\x95\xE3\x82\xA3", "fi"},  // フィ
-	{"\xE3\x83\x95\xE3\x82\xA9", "fo"},  // フォ
-	{"\xE3\x81\xB5", "fu"},              // ふ
-	{"\xE3\x83\x95", "fu"},              // フ
-	{"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
-	{"\xE3\x81\x8C", "ga"},              // が
-	{"\xE3\x82\xAC", "ga"},              // ガ
-	{"\xE3\x81\x92", "ge"},              // げ
-	{"\xE3\x82\xB2", "ge"},              // ゲ
-	{"\xE3\x81\x8E", "gi"},              // ぎ
-	{"\xE3\x82\xAE", "gi"},              // ギ
-	{"\xE3\x81\x94", "go"},              // ご
-	{"\xE3\x82\xB4", "go"},              // ゴ
-	{"\xE3\x81\x90", "gu"},              // ぐ
-	{"\xE3\x82\xB0", "gu"},              // グ
-	{"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
-	{"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
-	{"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
-	{"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
-	{"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
-	{"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
-	{"\xE3\x81\xAF", "ha"},              // は
-	{"\xE3\x83\x8F", "ha"},              // ハ
-	{"\xE3\x81\xB8", "he"},              // へ
-	{"\xE3\x83\x98", "he"},              // ヘ
-	{"\xE3\x81\xB2", "hi"},              // ひ
-	{"\xE3\x83\x92", "hi"},              // ヒ
-	{"\xE3\x81\xBB", "ho"},              // ほ
-	{"\xE3\x83\x9B", "ho"},              // ホ
-	{"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
-	{"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
-	{"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
-	{"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
-	{"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
-	{"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
-	{"\xE3\x81\x83", "i"},               // ぃ
-	{"\xE3\x81\x84", "i"},               // い
-	{"\xE3\x82\xA3", "i"},               // ィ
-	{"\xE3\x82\xA4", "i"},               // イ
-	{"\xE3\x83\xBC", "i"},               // ー
-	{"\xE3\x81\x98\xE3\x82\x83", "ja"},  // じゃ
-	{"\xE3\x81\xA2\xE3\x82\x83", "ja"},  // ぢゃ
-	{"\xE3\x82\xB8\xE3\x83\xA3", "ja"},  // ジャ
-	{"\xE3\x83\x82\xE3\x83\xA3", "ja"},  // ヂャ
-	{"\xE3\x82\xB8\xE3\x82\xA7", "je"},  // ジェ
-	{"\xE3\x81\x98", "ji"},              // じ
-	{"\xE3\x81\xA2", "ji"},              // ぢ
-	{"\xE3\x82\xB8", "ji"},              // ジ
-	{"\xE3\x83\x82", "ji"},              // ヂ
-	{"\xE3\x81\x98\xE3\x82\x87", "jo"},  // じょ
-	{"\xE3\x81\xA2\xE3\x82\x87", "jo"},  // ぢょ
-	{"\xE3\x82\xB8\xE3\x83\xA7", "jo"},  // ジョ
-	{"\xE3\x83\x82\xE3\x83\xA7", "jo"},  // ヂョ
-	{"\xE3\x81\x98\xE3\x82\x85", "ju"},  // じゅ
-	{"\xE3\x81\xA2\xE3\x82\x85", "ju"},  // ぢゅ
-	{"\xE3\x82\xB8\xE3\x83\xA5", "ju"},  // ジュ
-	{"\xE3\x83\x82\xE3\x83\xA5", "ju"},  // ヂュ
-	{"\xE3\x81\xA3", "k"},               // っ
-	{"\xE3\x83\x83", "k"},               // ッ
-	{"\xE3\x81\x8B", "ka"},              // か
-	{"\xE3\x82\xAB", "ka"},              // カ
-	{"\xE3\x81\x91", "ke"},              // け
-	{"\xE3\x82\xB1", "ke"},              // ケ
-	{"\xE3\x81\x8D", "ki"},              // き
-	{"\xE3\x82\xAD", "ki"},              // キ
-	{"\xE3\x81\x93", "ko"},              // こ
-	{"\xE3\x82\xB3", "ko"},              // コ
-	{"\xE3\x81\x8F", "ku"},              // く
-	{"\xE3\x82\xAF", "ku"},              // ク
-	{"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
-	{"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
-	{"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
-	{"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
-	{"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
-	{"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
-	{"\xE3\x82\x93", "m"},               // ん
-	{"\xE3\x83\xB3", "m"},               // ン
-	{"\xE3\x81\xBE", "ma"},              // ま
-	{"\xE3\x83\x9E", "ma"},              // マ
-	{"\xE3\x82\x81", "me"},              // め
-	{"\xE3\x83\xA1", "me"},              // メ
-	{"\xE3\x81\xBF", "mi"},              // み
-	{"\xE3\x83\x9F", "mi"},              // ミ
-	{"\xE3\x82\x82", "mo"},              // も
-	{"\xE3\x83\xA2", "mo"},              // モ
-	{"\xE3\x82\x80", "mu"},              // む
-	{"\xE3\x83\xA0", "mu"},              // ム
-	{"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
-	{"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
-	{"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
-	{"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
-	{"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
-	{"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
-	{"\xE3\x82\x93", "n"},               // ん
-	{"\xE3\x83\xB3", "n"},               // ン
-	{"\xE3\x81\xAA", "na"},              // な
-	{"\xE3\x83\x8A", "na"},              // ナ
-	{"\xE3\x81\xAD", "ne"},              // ね
-	{"\xE3\x83\x8D", "ne"},              // ネ
-	{"\xE3\x81\xAB", "ni"},              // に
-	{"\xE3\x83\x8B", "ni"},              // ニ
-	{"\xE3\x81\xAE", "no"},              // の
-	{"\xE3\x83\x8E", "no"},              // ノ
-	{"\xE3\x81\xAC", "nu"},              // ぬ
-	{"\xE3\x83\x8C", "nu"},              // ヌ
-	{"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
-	{"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
-	{"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
-	{"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
-	{"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
-	{"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
-	{"\xE3\x81\x89", "o"},               // ぉ
-	{"\xE3\x81\x8A", "o"},               // お
-	{"\xE3\x82\xA9", "o"},               // ォ
-	{"\xE3\x82\xAA", "o"},               // オ
-	{"\xE3\x83\xBC", "o"},               // ー
-	{"\xE3\x81\xA3", "p"},               // っ
-	{"\xE3\x83\x83", "p"},               // ッ
-	{"\xE3\x81\xB1", "pa"},              // ぱ
-	{"\xE3\x83\x91", "pa"},              // パ
-	{"\xE3\x81\xBA", "pe"},              // ぺ
-	{"\xE3\x83\x9A", "pe"},              // ペ
-	{"\xE3\x81\xB4", "pi"},              // ぴ
-	{"\xE3\x83\x94", "pi"},              // ピ
-	{"\xE3\x81\xBD", "po"},              // ぽ
-	{"\xE3\x83\x9D", "po"},              // ポ
-	{"\xE3\x81\xB7", "pu"},              // ぷ
-	{"\xE3\x83\x97", "pu"},              // プ
-	{"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
-	{"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
-	{"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
-	{"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
-	{"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
-	{"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
-	{"\xE3\x82\x89", "ra"},              // ら
-	{"\xE3\x83\xA9", "ra"},              // ラ
-	{"\xE3\x82\x8C", "re"},              // れ
-	{"\xE3\x83\xAC", "re"},              // レ
-	{"\xE3\x82\x8A", "ri"},              // り
-	{"\xE3\x83\xAA", "ri"},              // リ
-	{"\xE3\x82\x8D", "ro"},              // ろ
-	{"\xE3\x83\xAD", "ro"},              // ロ
-	{"\xE3\x82\x8B", "ru"},              // る
-	{"\xE3\x83\xAB", "ru"},              // ル
-	{"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
-	{"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
-	{"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
-	{"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
-	{"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
-	{"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
-	{"\xE3\x81\xA3", "s"},               // っ
-	{"\xE3\x83\x83", "s"},               // ッ
-	{"\xE3\x81\x95", "sa"},              // さ
-	{"\xE3\x82\xB5", "sa"},              // サ
-	{"\xE3\x81\x9B", "se"},              // せ
-	{"\xE3\x82\xBB", "se"},              // セ
-	{"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
-	{"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
-	{"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
-	{"\xE3\x81\x97", "shi"},             // し
-	{"\xE3\x82\xB7", "shi"},             // シ
-	{"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
-	{"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
-	{"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
-	{"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
-	{"\xE3\x81\x9D", "so"},              // そ
-	{"\xE3\x82\xBD", "so"},              // ソ
-	{"\xE3\x81\x99", "su"},              // す
-	{"\xE3\x82\xB9", "su"},              // ス
-	{"\xE3\x81\xA3", "t"},               // っ
-	{"\xE3\x83\x83", "t"},               // ッ
-	{"\xE3\x81\x9F", "ta"},              // た
-	{"\xE3\x82\xBF", "ta"},              // タ
-	{"\xE3\x81\xA6", "te"},              // て
-	{"\xE3\x83\x86", "te"},              // テ
-	{"\xE3\x83\x86\xE3\x82\xA3", "ti"},  // ティ
-	{"\xE3\x81\xA8", "to"},              // と
-	{"\xE3\x83\x88", "to"},              // ト
-	{"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
-	{"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
-	{"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
-	{"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
-	{"\xE3\x81\xA4", "tsu"},             // つ
-	{"\xE3\x83\x84", "tsu"},             // ツ
-	{"\xE3\x83\x86\xE3\x82\xA5", "tu"},  // テゥ
-	{"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
-	{"\xE3\x81\x85", "u"},               // ぅ
-	{"\xE3\x81\x86", "u"},               // う
-	{"\xE3\x82\xA5", "u"},               // ゥ
-	{"\xE3\x82\xA6", "u"},               // ウ
-	{"\xE3\x83\xBC", "u"},               // ー
-	{"\xE3\x83\xB4\xE3\x82\xA1", "va"},  // ヴァ
-	{"\xE3\x83\xB4\xE3\x82\xA7", "ve"},  // ヴェ
-	{"\xE3\x83\xB4\xE3\x82\xA3", "vi"},  // ヴィ
-	{"\xE3\x83\xB4\xE3\x82\xA9", "vo"},  // ヴォ
-	{"\xE3\x83\xB4", "vu"},              // ヴ
-	{"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
-	{"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
-	{"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
-	{"\xE3\x81\xAF", "wa"},              // は
-	{"\xE3\x82\x8F", "wa"},              // わ
-	{"\xE3\x83\xAF", "wa"},              // ワ
-	{"\xE3\x82\x91", "we"},              // ゑ
-	{"\xE3\x82\xA6\xE3\x82\xA7", "we"},  // ウェ
-	{"\xE3\x83\xB1", "we"},              // ヱ
-	{"\xE3\x82\x90", "wi"},              // ゐ
-	{"\xE3\x82\xA6\xE3\x82\xA3", "wi"},  // ウィ
-	{"\xE3\x83\xB0", "wi"},              // ヰ
-	{"\xE3\x82\x92", "wo"},              // を
-	{"\xE3\x82\xA6\xE3\x82\xA9", "wo"},  // ウォ
-	{"\xE3\x83\xB2", "wo"},              // ヲ
-	{"\xE3\x82\x84", "ya"},              // や
-	{"\xE3\x83\xA4", "ya"},              // ヤ
-	{"\xE3\x82\xA4\xE3\x82\xA7", "ye"},  // イェ
-	{"\xE3\x82\x88", "yo"},              // よ
-	{"\xE3\x83\xA8", "yo"},              // ヨ
-	{"\xE3\x82\x86", "yu"},              // ゆ
-	{"\xE3\x83\xA6", "yu"},              // ユ
-	{"\xE3\x81\x96", "za"},              // ざ
-	{"\xE3\x82\xB6", "za"},              // ザ
-	{"\xE3\x81\x9C", "ze"},              // ぜ
-	{"\xE3\x82\xBC", "ze"},              // ゼ
-	{"\xE3\x81\x9E", "zo"},              // ぞ
-	{"\xE3\x82\xBE", "zo"},              // ゾ
-	{"\xE3\x81\x9A", "zu"},              // ず
-	{"\xE3\x81\xA5", "zu"},              // づ
-	{"\xE3\x82\xBA", "zu"},              // ズ
-	{"\xE3\x83\x85", "zu"},              // ヅ
+    {"\xE3\x81\x81", "a"},               // ぁ
+    {"\xE3\x81\x82", "a"},               // あ
+    {"\xE3\x82\xA1", "a"},               // ァ
+    {"\xE3\x82\xA2", "a"},               // ア
+    {"\xE3\x83\xBC", "a"},               // ー
+    {"\xE3\x81\xB0", "ba"},              // ば
+    {"\xE3\x83\x90", "ba"},              // バ
+    {"\xE3\x81\xB9", "be"},              // べ
+    {"\xE3\x83\x99", "be"},              // ベ
+    {"\xE3\x81\xB3", "bi"},              // び
+    {"\xE3\x83\x93", "bi"},              // ビ
+    {"\xE3\x81\xBC", "bo"},              // ぼ
+    {"\xE3\x83\x9C", "bo"},              // ボ
+    {"\xE3\x81\xB6", "bu"},              // ぶ
+    {"\xE3\x83\x96", "bu"},              // ブ
+    {"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
+    {"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
+    {"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
+    {"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
+    {"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
+    {"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
+    {"\xE3\x81\xA3", "c"},               // っ
+    {"\xE3\x83\x83", "c"},               // ッ
+    {"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
+    {"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
+    {"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
+    {"\xE3\x81\xA1", "chi"},             // ち
+    {"\xE3\x83\x81", "chi"},             // チ
+    {"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
+    {"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
+    {"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
+    {"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
+    {"\xE3\x81\xA0", "da"},              // だ
+    {"\xE3\x83\x80", "da"},              // ダ
+    {"\xE3\x81\xA7", "de"},              // で
+    {"\xE3\x83\x87", "de"},              // デ
+    {"\xE3\x83\x87\xE3\x82\xA3", "di"},  // ディ
+    {"\xE3\x81\xA9", "do"},              // ど
+    {"\xE3\x83\x89", "do"},              // ド
+    {"\xE3\x83\x87\xE3\x82\xA5", "du"},  // デゥ
+    {"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
+    {"\xE3\x81\x87", "e"},               // ぇ
+    {"\xE3\x81\x88", "e"},               // え
+    {"\xE3\x82\xA7", "e"},               // ェ
+    {"\xE3\x82\xA8", "e"},               // エ
+    {"\xE3\x83\xBC", "e"},               // ー
+    {"\xE3\x83\x95\xE3\x82\xA1", "fa"},  // ファ
+    {"\xE3\x83\x95\xE3\x82\xA7", "fe"},  // フェ
+    {"\xE3\x83\x95\xE3\x82\xA3", "fi"},  // フィ
+    {"\xE3\x83\x95\xE3\x82\xA9", "fo"},  // フォ
+    {"\xE3\x81\xB5", "fu"},              // ふ
+    {"\xE3\x83\x95", "fu"},              // フ
+    {"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
+    {"\xE3\x81\x8C", "ga"},              // が
+    {"\xE3\x82\xAC", "ga"},              // ガ
+    {"\xE3\x81\x92", "ge"},              // げ
+    {"\xE3\x82\xB2", "ge"},              // ゲ
+    {"\xE3\x81\x8E", "gi"},              // ぎ
+    {"\xE3\x82\xAE", "gi"},              // ギ
+    {"\xE3\x81\x94", "go"},              // ご
+    {"\xE3\x82\xB4", "go"},              // ゴ
+    {"\xE3\x81\x90", "gu"},              // ぐ
+    {"\xE3\x82\xB0", "gu"},              // グ
+    {"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
+    {"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
+    {"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
+    {"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
+    {"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
+    {"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
+    {"\xE3\x81\xAF", "ha"},              // は
+    {"\xE3\x83\x8F", "ha"},              // ハ
+    {"\xE3\x81\xB8", "he"},              // へ
+    {"\xE3\x83\x98", "he"},              // ヘ
+    {"\xE3\x81\xB2", "hi"},              // ひ
+    {"\xE3\x83\x92", "hi"},              // ヒ
+    {"\xE3\x81\xBB", "ho"},              // ほ
+    {"\xE3\x83\x9B", "ho"},              // ホ
+    {"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
+    {"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
+    {"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
+    {"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
+    {"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
+    {"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
+    {"\xE3\x81\x83", "i"},               // ぃ
+    {"\xE3\x81\x84", "i"},               // い
+    {"\xE3\x82\xA3", "i"},               // ィ
+    {"\xE3\x82\xA4", "i"},               // イ
+    {"\xE3\x83\xBC", "i"},               // ー
+    {"\xE3\x81\x98\xE3\x82\x83", "ja"},  // じゃ
+    {"\xE3\x81\xA2\xE3\x82\x83", "ja"},  // ぢゃ
+    {"\xE3\x82\xB8\xE3\x83\xA3", "ja"},  // ジャ
+    {"\xE3\x83\x82\xE3\x83\xA3", "ja"},  // ヂャ
+    {"\xE3\x82\xB8\xE3\x82\xA7", "je"},  // ジェ
+    {"\xE3\x81\x98", "ji"},              // じ
+    {"\xE3\x81\xA2", "ji"},              // ぢ
+    {"\xE3\x82\xB8", "ji"},              // ジ
+    {"\xE3\x83\x82", "ji"},              // ヂ
+    {"\xE3\x81\x98\xE3\x82\x87", "jo"},  // じょ
+    {"\xE3\x81\xA2\xE3\x82\x87", "jo"},  // ぢょ
+    {"\xE3\x82\xB8\xE3\x83\xA7", "jo"},  // ジョ
+    {"\xE3\x83\x82\xE3\x83\xA7", "jo"},  // ヂョ
+    {"\xE3\x81\x98\xE3\x82\x85", "ju"},  // じゅ
+    {"\xE3\x81\xA2\xE3\x82\x85", "ju"},  // ぢゅ
+    {"\xE3\x82\xB8\xE3\x83\xA5", "ju"},  // ジュ
+    {"\xE3\x83\x82\xE3\x83\xA5", "ju"},  // ヂュ
+    {"\xE3\x81\xA3", "k"},               // っ
+    {"\xE3\x83\x83", "k"},               // ッ
+    {"\xE3\x81\x8B", "ka"},              // か
+    {"\xE3\x82\xAB", "ka"},              // カ
+    {"\xE3\x81\x91", "ke"},              // け
+    {"\xE3\x82\xB1", "ke"},              // ケ
+    {"\xE3\x81\x8D", "ki"},              // き
+    {"\xE3\x82\xAD", "ki"},              // キ
+    {"\xE3\x81\x93", "ko"},              // こ
+    {"\xE3\x82\xB3", "ko"},              // コ
+    {"\xE3\x81\x8F", "ku"},              // く
+    {"\xE3\x82\xAF", "ku"},              // ク
+    {"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
+    {"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
+    {"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
+    {"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
+    {"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
+    {"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
+    {"\xE3\x82\x93", "m"},               // ん
+    {"\xE3\x83\xB3", "m"},               // ン
+    {"\xE3\x81\xBE", "ma"},              // ま
+    {"\xE3\x83\x9E", "ma"},              // マ
+    {"\xE3\x82\x81", "me"},              // め
+    {"\xE3\x83\xA1", "me"},              // メ
+    {"\xE3\x81\xBF", "mi"},              // み
+    {"\xE3\x83\x9F", "mi"},              // ミ
+    {"\xE3\x82\x82", "mo"},              // も
+    {"\xE3\x83\xA2", "mo"},              // モ
+    {"\xE3\x82\x80", "mu"},              // む
+    {"\xE3\x83\xA0", "mu"},              // ム
+    {"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
+    {"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
+    {"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
+    {"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
+    {"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
+    {"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
+    {"\xE3\x82\x93", "n"},               // ん
+    {"\xE3\x83\xB3", "n"},               // ン
+    {"\xE3\x81\xAA", "na"},              // な
+    {"\xE3\x83\x8A", "na"},              // ナ
+    {"\xE3\x81\xAD", "ne"},              // ね
+    {"\xE3\x83\x8D", "ne"},              // ネ
+    {"\xE3\x81\xAB", "ni"},              // に
+    {"\xE3\x83\x8B", "ni"},              // ニ
+    {"\xE3\x81\xAE", "no"},              // の
+    {"\xE3\x83\x8E", "no"},              // ノ
+    {"\xE3\x81\xAC", "nu"},              // ぬ
+    {"\xE3\x83\x8C", "nu"},              // ヌ
+    {"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
+    {"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
+    {"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
+    {"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
+    {"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
+    {"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
+    {"\xE3\x81\x89", "o"},               // ぉ
+    {"\xE3\x81\x8A", "o"},               // お
+    {"\xE3\x82\xA9", "o"},               // ォ
+    {"\xE3\x82\xAA", "o"},               // オ
+    {"\xE3\x83\xBC", "o"},               // ー
+    {"\xE3\x81\xA3", "p"},               // っ
+    {"\xE3\x83\x83", "p"},               // ッ
+    {"\xE3\x81\xB1", "pa"},              // ぱ
+    {"\xE3\x83\x91", "pa"},              // パ
+    {"\xE3\x81\xBA", "pe"},              // ぺ
+    {"\xE3\x83\x9A", "pe"},              // ペ
+    {"\xE3\x81\xB4", "pi"},              // ぴ
+    {"\xE3\x83\x94", "pi"},              // ピ
+    {"\xE3\x81\xBD", "po"},              // ぽ
+    {"\xE3\x83\x9D", "po"},              // ポ
+    {"\xE3\x81\xB7", "pu"},              // ぷ
+    {"\xE3\x83\x97", "pu"},              // プ
+    {"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
+    {"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
+    {"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
+    {"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
+    {"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
+    {"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
+    {"\xE3\x82\x89", "ra"},              // ら
+    {"\xE3\x83\xA9", "ra"},              // ラ
+    {"\xE3\x82\x8C", "re"},              // れ
+    {"\xE3\x83\xAC", "re"},              // レ
+    {"\xE3\x82\x8A", "ri"},              // り
+    {"\xE3\x83\xAA", "ri"},              // リ
+    {"\xE3\x82\x8D", "ro"},              // ろ
+    {"\xE3\x83\xAD", "ro"},              // ロ
+    {"\xE3\x82\x8B", "ru"},              // る
+    {"\xE3\x83\xAB", "ru"},              // ル
+    {"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
+    {"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
+    {"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
+    {"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
+    {"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
+    {"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
+    {"\xE3\x81\xA3", "s"},               // っ
+    {"\xE3\x83\x83", "s"},               // ッ
+    {"\xE3\x81\x95", "sa"},              // さ
+    {"\xE3\x82\xB5", "sa"},              // サ
+    {"\xE3\x81\x9B", "se"},              // せ
+    {"\xE3\x82\xBB", "se"},              // セ
+    {"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
+    {"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
+    {"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
+    {"\xE3\x81\x97", "shi"},             // し
+    {"\xE3\x82\xB7", "shi"},             // シ
+    {"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
+    {"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
+    {"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
+    {"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
+    {"\xE3\x81\x9D", "so"},              // そ
+    {"\xE3\x82\xBD", "so"},              // ソ
+    {"\xE3\x81\x99", "su"},              // す
+    {"\xE3\x82\xB9", "su"},              // ス
+    {"\xE3\x81\xA3", "t"},               // っ
+    {"\xE3\x83\x83", "t"},               // ッ
+    {"\xE3\x81\x9F", "ta"},              // た
+    {"\xE3\x82\xBF", "ta"},              // タ
+    {"\xE3\x81\xA6", "te"},              // て
+    {"\xE3\x83\x86", "te"},              // テ
+    {"\xE3\x83\x86\xE3\x82\xA3", "ti"},  // ティ
+    {"\xE3\x81\xA8", "to"},              // と
+    {"\xE3\x83\x88", "to"},              // ト
+    {"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
+    {"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
+    {"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
+    {"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
+    {"\xE3\x81\xA4", "tsu"},             // つ
+    {"\xE3\x83\x84", "tsu"},             // ツ
+    {"\xE3\x83\x86\xE3\x82\xA5", "tu"},  // テゥ
+    {"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
+    {"\xE3\x81\x85", "u"},               // ぅ
+    {"\xE3\x81\x86", "u"},               // う
+    {"\xE3\x82\xA5", "u"},               // ゥ
+    {"\xE3\x82\xA6", "u"},               // ウ
+    {"\xE3\x83\xBC", "u"},               // ー
+    {"\xE3\x83\xB4\xE3\x82\xA1", "va"},  // ヴァ
+    {"\xE3\x83\xB4\xE3\x82\xA7", "ve"},  // ヴェ
+    {"\xE3\x83\xB4\xE3\x82\xA3", "vi"},  // ヴィ
+    {"\xE3\x83\xB4\xE3\x82\xA9", "vo"},  // ヴォ
+    {"\xE3\x83\xB4", "vu"},              // ヴ
+    {"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
+    {"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
+    {"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
+    {"\xE3\x81\xAF", "wa"},              // は
+    {"\xE3\x82\x8F", "wa"},              // わ
+    {"\xE3\x83\xAF", "wa"},              // ワ
+    {"\xE3\x82\x91", "we"},              // ゑ
+    {"\xE3\x82\xA6\xE3\x82\xA7", "we"},  // ウェ
+    {"\xE3\x83\xB1", "we"},              // ヱ
+    {"\xE3\x82\x90", "wi"},              // ゐ
+    {"\xE3\x82\xA6\xE3\x82\xA3", "wi"},  // ウィ
+    {"\xE3\x83\xB0", "wi"},              // ヰ
+    {"\xE3\x82\x92", "wo"},              // を
+    {"\xE3\x82\xA6\xE3\x82\xA9", "wo"},  // ウォ
+    {"\xE3\x83\xB2", "wo"},              // ヲ
+    {"\xE3\x82\x84", "ya"},              // や
+    {"\xE3\x83\xA4", "ya"},              // ヤ
+    {"\xE3\x82\xA4\xE3\x82\xA7", "ye"},  // イェ
+    {"\xE3\x82\x88", "yo"},              // よ
+    {"\xE3\x83\xA8", "yo"},              // ヨ
+    {"\xE3\x82\x86", "yu"},              // ゆ
+    {"\xE3\x83\xA6", "yu"},              // ユ
+    {"\xE3\x81\x96", "za"},              // ざ
+    {"\xE3\x82\xB6", "za"},              // ザ
+    {"\xE3\x81\x9C", "ze"},              // ぜ
+    {"\xE3\x82\xBC", "ze"},              // ゼ
+    {"\xE3\x81\x9E", "zo"},              // ぞ
+    {"\xE3\x82\xBE", "zo"},              // ゾ
+    {"\xE3\x81\x9A", "zu"},              // ず
+    {"\xE3\x81\xA5", "zu"},              // づ
+    {"\xE3\x82\xBA", "zu"},              // ズ
+    {"\xE3\x83\x85", "zu"},              // ヅ
 };
 
-bool cmp_kana(agi::kana_pair const& kp, std::string const& kana) {
-	return strcmp(kp.kana, kana.c_str()) < 0;
+bool cmp_kana(agi::kana_pair const &kp, std::string const &kana)
+{
+    return strcmp(kp.kana, kana.c_str()) < 0;
 }
 
 struct cmp_romaji {
-	bool operator()(agi::kana_pair const& kp, std::string const& romaji) const {
-		return strcmp(kp.romaji, romaji.c_str()) < 0;
-	}
-	bool operator()(std::string const& romaji, agi::kana_pair const& kp) const {
-		return strcmp(kp.romaji, romaji.c_str()) > 0;
-	}
+    bool operator()(agi::kana_pair const &kp, std::string const &romaji) const {
+        return strcmp(kp.romaji, romaji.c_str()) < 0;
+    }
+    bool operator()(std::string const &romaji, agi::kana_pair const &kp) const {
+        return strcmp(kp.romaji, romaji.c_str()) > 0;
+    }
 
 #ifdef _MSC_VER // debug iterator stuff needs this overload
-	bool operator()(agi::kana_pair const& a, agi::kana_pair const& b) const {
-		return strcmp(a.romaji, b.romaji) < 0;
-	}
+    bool operator()(agi::kana_pair const &a, agi::kana_pair const &b) const {
+        return strcmp(a.romaji, b.romaji) < 0;
+    }
 #endif
 };
 
 }
 
 namespace agi {
-std::vector<const char *> kana_to_romaji(std::string const& kana) {
-	std::vector<const char *> ret;
-	for (auto pair = boost::lower_bound(::kana_to_romaji, kana, cmp_kana);
-		pair != std::end(::kana_to_romaji) && !strcmp(pair->kana, kana.c_str());
-		++pair)
-		ret.push_back(pair->romaji);
-	return ret;
+std::vector<const char *> kana_to_romaji(std::string const &kana)
+{
+    std::vector<const char *> ret;
+    for (auto pair = boost::lower_bound(::kana_to_romaji, kana, cmp_kana);
+         pair != std::end(::kana_to_romaji) && !strcmp(pair->kana, kana.c_str());
+         ++pair)
+        ret.push_back(pair->romaji);
+    return ret;
 }
 
-boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const& romaji) {
-	for (size_t len = std::min<size_t>(3, romaji.size()); len > 0; --len) {
-		auto pair = boost::equal_range(::romaji_to_kana, romaji.substr(0, len).c_str(), cmp_romaji());
-		if (pair.first != pair.second)
-			return boost::make_iterator_range(pair.first, pair.second);
-	}
-	return boost::make_iterator_range(::romaji_to_kana, ::romaji_to_kana);
+boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const &romaji)
+{
+    for (size_t len = std::min<size_t>(3, romaji.size()); len > 0; --len) {
+        auto pair = boost::equal_range(::romaji_to_kana, romaji.substr(0, len).c_str(), cmp_romaji());
+        if (pair.first != pair.second)
+            return boost::make_iterator_range(pair.first, pair.second);
+    }
+    return boost::make_iterator_range(::romaji_to_kana, ::romaji_to_kana);
 }
 }
diff --git a/libaegisub/common/karaoke_matcher.cpp b/libaegisub/common/karaoke_matcher.cpp
index 562b0273fe972cc6a5d6f841741b1e29bbd19321..3283e870ad0d4df06114539181c8f4221a9ca043 100644
--- a/libaegisub/common/karaoke_matcher.cpp
+++ b/libaegisub/common/karaoke_matcher.cpp
@@ -28,180 +28,188 @@
 #include <unicode/utf8.h>
 
 namespace {
-int32_t next_codepoint(const char *str, size_t *i) {
-	UChar32 c;
-	U8_NEXT_UNSAFE(str, *i, c);
-	return c;
+int32_t next_codepoint(const char *str, size_t *i)
+{
+    UChar32 c;
+    U8_NEXT_UNSAFE(str, *i, c);
+    return c;
 }
 
-bool is_whitespace(int32_t c) {
-	return !!u_isUWhiteSpace(c);
+bool is_whitespace(int32_t c)
+{
+    return !!u_isUWhiteSpace(c);
 }
 
-bool is_whitespace(std::string const& str) {
-	size_t i = 0;
-	while (auto c = next_codepoint(str.c_str(), &i)) {
-		if (!u_isUWhiteSpace(c))
-			return false;
-	}
-	return true;
+bool is_whitespace(std::string const &str)
+{
+    size_t i = 0;
+    while (auto c = next_codepoint(str.c_str(), &i)) {
+        if (!u_isUWhiteSpace(c))
+            return false;
+    }
+    return true;
 }
 
 // strcmp but ignoring case and accents
-int compare(std::string const& a, std::string const& b) {
-	using namespace boost::locale;
-	return std::use_facet<collator<char>>(std::locale()).compare(collator_base::primary, a, b);
+int compare(std::string const &a, std::string const &b)
+{
+    using namespace boost::locale;
+    return std::use_facet<collator<char>>(std::locale()).compare(collator_base::primary, a, b);
 }
 
 }
 
 namespace agi {
 
-karaoke_match_result auto_match_karaoke(std::vector<std::string> const& source_strings, std::string const& dest_string) {
-	karaoke_match_result result = { 0, 0 };
-	if (source_strings.empty()) return result;
-
-	using namespace boost::locale::boundary;
-	using boost::starts_with;
-
-	result.source_length = 1;
-	ssegment_index destination_characters(character, begin(dest_string), end(dest_string));
-	auto src = boost::to_lower_copy(source_strings[0]);
-	auto dst = destination_characters.begin();
-	auto dst_end = destination_characters.end();
-
-	// Eat all the whitespace at the beginning of the source and destination
-	// syllables and exit if either ran out.
-	auto eat_whitespace = [&]() -> bool {
-		size_t i = 0, first_non_whitespace = 0;
-		while (is_whitespace(next_codepoint(src.c_str(), &i)))
-			first_non_whitespace = i;
-		if (first_non_whitespace)
-			src = src.substr(first_non_whitespace);
-
-		while (dst != dst_end && is_whitespace(dst->str())) {
-			++dst;
-			++result.destination_length;
-		}
-
-		// If we ran out of dest then this needs to match the rest of the
-		// source syllables (this probably means the user did something wrong)
-		if (dst == dst_end) {
-			result.source_length = source_strings.size();
-			return true;
-		}
-
-		return src.empty();
-	};
-
-	if (eat_whitespace()) return result;
-
-	// We now have a non-whitespace character at the beginning of both source
-	// and destination. Check if the source starts with a romanized kana, and
-	// if it does then check if the destination also has the appropriate
-	// character. If it does, match them and repeat.
-	while (!src.empty()) {
-		// First check for a basic match of the first character of the source and dest
-		auto first_src_char = ssegment_index(character, begin(src), end(src)).begin()->str();
-		if (compare(first_src_char, dst->str()) == 0) {
-			++dst;
-			++result.destination_length;
-			src.erase(0, first_src_char.size());
-			if (eat_whitespace()) return result;
-			continue;
-		}
-
-		auto check = [&](kana_pair const& kp) -> bool {
-			if (!starts_with(&*dst->begin(), kp.kana)) return false;
-
-			src = src.substr(strlen(kp.romaji));
-			for (size_t i = 0; kp.kana[i]; ) {
-				i += dst->length();
-				++result.destination_length;
-				++dst;
-			}
-			return true;
-		};
-
-		bool matched = false;
-		for (auto const& match : romaji_to_kana(src)) {
-			if (check(match)) {
-				if (eat_whitespace()) return result;
-				matched = true;
-				break;
-			}
-		}
-		if (!matched) break;
-	}
-
-	// Source and dest are now non-empty and start with non-whitespace.
-	// If there's only one character left in the dest, it obviously needs to
-	// match all of the source syllables left.
-	if (std::distance(dst, dst_end) == 1) {
-		result.source_length = source_strings.size();
-		++result.destination_length;
-		return result;
-	}
-
-	// We couldn't match the current character, but if we can match the *next*
-	// syllable then we know that everything in between must belong to the
-	// current syllable. Do this by looking up to KANA_SEARCH_DISTANCE
-	// characters ahead in destination and seeing if we can match them against
-	// the beginning of a syllable after this syllable.
-	// 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.
-
-	// The longest kanji are 'uketamawa.ru' and 'kokorozashi', each with a
-	// reading consisting of five kana. This means each each character from
-	// the destination can match at most five syllables from the source.
-	static const int max_character_length = 5;
-
-	// Arbitrarily chosen limit on the number of dest characters to try
-	// skipping. Higher numbers probably increase false-positives.
-	static const int dst_lookahead_max = 3;
-
-	for (size_t lookahead = 0; lookahead < dst_lookahead_max; ++lookahead) {
-		if (++dst == dst_end) break;
-
-		// Transliterate this character if it's a known hiragana or katakana character
-		std::vector<const char *> translit;
-		auto next = std::next(dst);
-		if (next != dst_end)
-			boost::copy(kana_to_romaji(dst->str() + next->str()), back_inserter(translit));
-		boost::copy(kana_to_romaji(dst->str()), back_inserter(translit));
-
-		// Search for it and the transliterated version in the source
-		int src_lookahead_max = (lookahead + 1) * max_character_length;
-		int src_lookahead_pos = 0;
-		for (auto const& syl : source_strings) {
-			// Don't count blank syllables in the max search distance
-			if (is_whitespace(syl)) continue;
-			if (++src_lookahead_pos == 1) continue;
-			if (src_lookahead_pos > src_lookahead_max) break;
-
-			std::string lsyl = boost::to_lower_copy(syl);
-			if (!(starts_with(syl, dst->str()) || util::any_of(translit, [&](const char *str) { return starts_with(lsyl, str); })))
-				continue;
-
-			// The syllable immediately after the current one matched, so
-			// everything up to the match must go with the current syllable.
-			if (src_lookahead_pos == 2) {
-				result.destination_length += lookahead + 1;
-				return result;
-			}
-
-			// The match was multiple syllables ahead, so just divide the
-			// destination characters evenly between the source syllables
-			result.destination_length += 1;
-			result.source_length = static_cast<size_t>((src_lookahead_pos - 1.0) / (lookahead + 1.0) + .5);
-			return result;
-		}
-	}
-
-	// We wouldn't have gotten here if the dest was empty, so make sure at
-	// least one character is selected
-	result.destination_length = std::max<size_t>(result.destination_length, 1u);
-
-	return result;
+karaoke_match_result auto_match_karaoke(std::vector<std::string> const &source_strings, std::string const &dest_string)
+{
+    karaoke_match_result result = { 0, 0 };
+    if (source_strings.empty()) return result;
+
+    using namespace boost::locale::boundary;
+    using boost::starts_with;
+
+    result.source_length = 1;
+    ssegment_index destination_characters(character, begin(dest_string), end(dest_string));
+    auto src = boost::to_lower_copy(source_strings[0]);
+    auto dst = destination_characters.begin();
+    auto dst_end = destination_characters.end();
+
+    // Eat all the whitespace at the beginning of the source and destination
+    // syllables and exit if either ran out.
+    auto eat_whitespace = [&]() -> bool {
+        size_t i = 0, first_non_whitespace = 0;
+        while (is_whitespace(next_codepoint(src.c_str(), &i)))
+            first_non_whitespace = i;
+        if (first_non_whitespace)
+            src = src.substr(first_non_whitespace);
+
+        while (dst != dst_end && is_whitespace(dst->str()))
+        {
+            ++dst;
+            ++result.destination_length;
+        }
+
+        // If we ran out of dest then this needs to match the rest of the
+        // source syllables (this probably means the user did something wrong)
+        if (dst == dst_end)
+        {
+            result.source_length = source_strings.size();
+            return true;
+        }
+
+        return src.empty();
+    };
+
+    if (eat_whitespace()) return result;
+
+    // We now have a non-whitespace character at the beginning of both source
+    // and destination. Check if the source starts with a romanized kana, and
+    // if it does then check if the destination also has the appropriate
+    // character. If it does, match them and repeat.
+    while (!src.empty()) {
+        // First check for a basic match of the first character of the source and dest
+        auto first_src_char = ssegment_index(character, begin(src), end(src)).begin()->str();
+        if (compare(first_src_char, dst->str()) == 0) {
+            ++dst;
+            ++result.destination_length;
+            src.erase(0, first_src_char.size());
+            if (eat_whitespace()) return result;
+            continue;
+        }
+
+        auto check = [&](kana_pair const & kp) -> bool {
+            if (!starts_with(&*dst->begin(), kp.kana)) return false;
+
+            src = src.substr(strlen(kp.romaji));
+            for (size_t i = 0; kp.kana[i]; )
+            {
+                i += dst->length();
+                ++result.destination_length;
+                ++dst;
+            }
+            return true;
+        };
+
+        bool matched = false;
+        for (auto const &match : romaji_to_kana(src)) {
+            if (check(match)) {
+                if (eat_whitespace()) return result;
+                matched = true;
+                break;
+            }
+        }
+        if (!matched) break;
+    }
+
+    // Source and dest are now non-empty and start with non-whitespace.
+    // If there's only one character left in the dest, it obviously needs to
+    // match all of the source syllables left.
+    if (std::distance(dst, dst_end) == 1) {
+        result.source_length = source_strings.size();
+        ++result.destination_length;
+        return result;
+    }
+
+    // We couldn't match the current character, but if we can match the *next*
+    // syllable then we know that everything in between must belong to the
+    // current syllable. Do this by looking up to KANA_SEARCH_DISTANCE
+    // characters ahead in destination and seeing if we can match them against
+    // the beginning of a syllable after this syllable.
+    // 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.
+
+    // The longest kanji are 'uketamawa.ru' and 'kokorozashi', each with a
+    // reading consisting of five kana. This means each each character from
+    // the destination can match at most five syllables from the source.
+    static const int max_character_length = 5;
+
+    // Arbitrarily chosen limit on the number of dest characters to try
+    // skipping. Higher numbers probably increase false-positives.
+    static const int dst_lookahead_max = 3;
+
+    for (size_t lookahead = 0; lookahead < dst_lookahead_max; ++lookahead) {
+        if (++dst == dst_end) break;
+
+        // Transliterate this character if it's a known hiragana or katakana character
+        std::vector<const char *> translit;
+        auto next = std::next(dst);
+        if (next != dst_end)
+            boost::copy(kana_to_romaji(dst->str() + next->str()), back_inserter(translit));
+        boost::copy(kana_to_romaji(dst->str()), back_inserter(translit));
+
+        // Search for it and the transliterated version in the source
+        int src_lookahead_max = (lookahead + 1) * max_character_length;
+        int src_lookahead_pos = 0;
+        for (auto const &syl : source_strings) {
+            // Don't count blank syllables in the max search distance
+            if (is_whitespace(syl)) continue;
+            if (++src_lookahead_pos == 1) continue;
+            if (src_lookahead_pos > src_lookahead_max) break;
+
+            std::string lsyl = boost::to_lower_copy(syl);
+            if (!(starts_with(syl, dst->str()) || util::any_of(translit, [&](const char *str) { return starts_with(lsyl, str); })))
+            continue;
+
+            // The syllable immediately after the current one matched, so
+            // everything up to the match must go with the current syllable.
+            if (src_lookahead_pos == 2) {
+                result.destination_length += lookahead + 1;
+                return result;
+            }
+
+            // The match was multiple syllables ahead, so just divide the
+            // destination characters evenly between the source syllables
+            result.destination_length += 1;
+            result.source_length = static_cast<size_t>((src_lookahead_pos - 1.0) / (lookahead + 1.0) + .5);
+            return result;
+        }
+    }
+
+    // We wouldn't have gotten here if the dest was empty, so make sure at
+    // least one character is selected
+    result.destination_length = std::max<size_t>(result.destination_length, 1u);
+
+    return result;
 }
 }
diff --git a/libaegisub/common/keyframe.cpp b/libaegisub/common/keyframe.cpp
index b2855bd11fd168e192ad125c3e30e239f8f888f8..5ae9acda32dd156789fd6a9e954509c3add5965d 100644
--- a/libaegisub/common/keyframe.cpp
+++ b/libaegisub/common/keyframe.cpp
@@ -28,98 +28,109 @@
 #include <boost/range/algorithm/copy.hpp>
 
 namespace {
-std::vector<int> agi_keyframes(std::istream &file) {
-	double fps;
-	std::string fps_str;
-	file >> fps_str;
-	file >> fps;
-
-	return std::vector<int>(agi::line_iterator<int>(file), agi::line_iterator<int>());
+std::vector<int> agi_keyframes(std::istream &file)
+{
+    double fps;
+    std::string fps_str;
+    file >> fps_str;
+    file >> fps;
+
+    return std::vector<int>(agi::line_iterator<int>(file), agi::line_iterator<int>());
 }
 
-std::vector<int> enumerated_keyframes(std::istream &file, char (*func)(std::string const&)) {
-	int count = 0;
-	std::vector<int> ret;
-	for (auto line : agi::line_iterator<std::string>(file)) {
-		char c = tolower(func(line));
-		if (c == 'i')
-			ret.push_back(count++);
-		else if (c == 'p' || c == 'b')
-			++count;
-	}
-	return ret;
+std::vector<int> enumerated_keyframes(std::istream &file, char (*func)(std::string const &))
+{
+    int count = 0;
+    std::vector<int> ret;
+    for (auto line : agi::line_iterator<std::string>(file)) {
+        char c = tolower(func(line));
+        if (c == 'i')
+            ret.push_back(count++);
+        else if (c == 'p' || c == 'b')
+            ++count;
+    }
+    return ret;
 }
 
-std::vector<int> indexed_keyframes(std::istream &file, int (*func)(std::string const&)) {
-	std::vector<int> ret;
-	for (auto line : agi::line_iterator<std::string>(file)) {
-		int frame_no = func(line);
-		if (frame_no >= 0)
-			ret.push_back(frame_no);
-	}
-	return ret;
+std::vector<int> indexed_keyframes(std::istream &file, int (*func)(std::string const &))
+{
+    std::vector<int> ret;
+    for (auto line : agi::line_iterator<std::string>(file)) {
+        int frame_no = func(line);
+        if (frame_no >= 0)
+            ret.push_back(frame_no);
+    }
+    return ret;
 }
 
-char xvid(std::string const& line) {
-	return line.empty() ? 0 : line[0];
+char xvid(std::string const &line)
+{
+    return line.empty() ? 0 : line[0];
 }
 
-char divx(std::string const& line) {
-	char chrs[] = "IPB";
-	for (int i = 0; i < 3; ++i) {
-		std::string::size_type pos = line.find(chrs[i]);
-		if (pos != line.npos)
-			return line[pos];
-	}
-	return 0;
+char divx(std::string const &line)
+{
+    char chrs[] = "IPB";
+    for (int i = 0; i < 3; ++i) {
+        std::string::size_type pos = line.find(chrs[i]);
+        if (pos != line.npos)
+            return line[pos];
+    }
+    return 0;
 }
 
-char x264(std::string const& line) {
-	std::string::size_type pos = line.find("type:");
-	if (pos == line.npos || pos + 5 >= line.size()) return 0;
-	return line[pos + 5];
+char x264(std::string const &line)
+{
+    std::string::size_type pos = line.find("type:");
+    if (pos == line.npos || pos + 5 >= line.size()) return 0;
+    return line[pos + 5];
 }
 
-int wwxd(std::string const& line) {
-	if (line.empty() || line[0] == '#')
-		return -1;
-	std::istringstream ss(line);
-	int frame_no;
-	char frame_type;
-	ss >> frame_no >> frame_type;
-	if (ss.fail())
-		throw agi::keyframe::KeyframeFormatParseError("WWXD keyframe file not in qpfile format");
-	if (frame_type == 'I')
-		return frame_no;
-	return -1;
+int wwxd(std::string const &line)
+{
+    if (line.empty() || line[0] == '#')
+        return -1;
+    std::istringstream ss(line);
+    int frame_no;
+    char frame_type;
+    ss >> frame_no >> frame_type;
+    if (ss.fail())
+        throw agi::keyframe::KeyframeFormatParseError("WWXD keyframe file not in qpfile format");
+    if (frame_type == 'I')
+        return frame_no;
+    return -1;
 }
 }
 
-namespace agi { namespace keyframe {
-void Save(agi::fs::path const& filename, std::vector<int> const& keyframes) {
-	io::Save file(filename);
-	std::ostream& of = file.Get();
-	of << "# keyframe format v1" << std::endl;
-	of << "fps " << 0 << std::endl;
-	boost::copy(keyframes, std::ostream_iterator<int>(of, "\n"));
+namespace agi {
+namespace keyframe {
+void Save(agi::fs::path const &filename, std::vector<int> const &keyframes)
+{
+    io::Save file(filename);
+    std::ostream &of = file.Get();
+    of << "# keyframe format v1" << std::endl;
+    of << "fps " << 0 << std::endl;
+    boost::copy(keyframes, std::ostream_iterator<int>(of, "\n"));
 }
 
-std::vector<int> Load(agi::fs::path const& filename) {
-	auto file = io::Open(filename);
-	std::istream &is(*file);
+std::vector<int> Load(agi::fs::path const &filename)
+{
+    auto file = io::Open(filename);
+    std::istream &is(*file);
 
-	std::string header;
-	getline(is, header);
+    std::string header;
+    getline(is, header);
 
-	if (header == "# keyframe format v1") return agi_keyframes(is);
-	if (boost::starts_with(header, "# XviD 2pass stat file")) return enumerated_keyframes(is, xvid);
-	if (boost::starts_with(header, "# ffmpeg 2-pass log file, using xvid codec")) return enumerated_keyframes(is, xvid);
-	if (boost::starts_with(header, "# avconv 2-pass log file, using xvid codec")) return enumerated_keyframes(is, xvid);
-	if (boost::starts_with(header, "##map version")) return enumerated_keyframes(is, divx);
-	if (boost::starts_with(header, "#options:")) return enumerated_keyframes(is, x264);
-	if (boost::starts_with(header, "# WWXD log file, using qpfile format")) return indexed_keyframes(is, wwxd);
+    if (header == "# keyframe format v1") return agi_keyframes(is);
+    if (boost::starts_with(header, "# XviD 2pass stat file")) return enumerated_keyframes(is, xvid);
+    if (boost::starts_with(header, "# ffmpeg 2-pass log file, using xvid codec")) return enumerated_keyframes(is, xvid);
+    if (boost::starts_with(header, "# avconv 2-pass log file, using xvid codec")) return enumerated_keyframes(is, xvid);
+    if (boost::starts_with(header, "##map version")) return enumerated_keyframes(is, divx);
+    if (boost::starts_with(header, "#options:")) return enumerated_keyframes(is, x264);
+    if (boost::starts_with(header, "# WWXD log file, using qpfile format")) return indexed_keyframes(is, wwxd);
 
-	throw UnknownKeyframeFormatError("File header does not match any known formats");
+    throw UnknownKeyframeFormatError("File header does not match any known formats");
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/line_iterator.cpp b/libaegisub/common/line_iterator.cpp
index 21754a41c609dfc6b7f320af5b053ae5b1cdbdca..f0cad35db2deb695fee057a1b48598b44a4f912f 100644
--- a/libaegisub/common/line_iterator.cpp
+++ b/libaegisub/common/line_iterator.cpp
@@ -20,59 +20,60 @@
 namespace agi {
 
 line_iterator_base::line_iterator_base(std::istream &stream, std::string encoding)
-: stream(&stream)
+    : stream(&stream)
 {
-	if (encoding != "utf-8") {
-		agi::charset::IconvWrapper c("utf-8", encoding.c_str());
-		c.Convert("\r", 1, reinterpret_cast<char *>(&cr), sizeof(int));
-		c.Convert("\n", 1, reinterpret_cast<char *>(&lf), sizeof(int));
-		width = c.RequiredBufferSize("\n");
-		conv = std::make_shared<agi::charset::IconvWrapper>(encoding.c_str(), "utf-8");
-	}
+    if (encoding != "utf-8") {
+        agi::charset::IconvWrapper c("utf-8", encoding.c_str());
+        c.Convert("\r", 1, reinterpret_cast<char *>(&cr), sizeof(int));
+        c.Convert("\n", 1, reinterpret_cast<char *>(&lf), sizeof(int));
+        width = c.RequiredBufferSize("\n");
+        conv = std::make_shared<agi::charset::IconvWrapper>(encoding.c_str(), "utf-8");
+    }
 }
 
-bool line_iterator_base::getline(std::string &str) {
-	if (!stream) return false;
-	if (!stream->good()) {
-		stream = nullptr;
-		return false;
-	}
+bool line_iterator_base::getline(std::string &str)
+{
+    if (!stream) return false;
+    if (!stream->good()) {
+        stream = nullptr;
+        return false;
+    }
 
-	if (width == 1) {
-		std::getline(*stream, str);
-		if (str.size() && str.back() == '\r')
-			str.pop_back();
-	}
-	else {
-		union {
-			int32_t chr;
-			char buf[4];
-		} u;
+    if (width == 1) {
+        std::getline(*stream, str);
+        if (str.size() && str.back() == '\r')
+            str.pop_back();
+    }
+    else {
+        union {
+            int32_t chr;
+            char buf[4];
+        } u;
 
-		for (;;) {
-			u.chr = 0;
-			std::streamsize read = stream->rdbuf()->sgetn(u.buf, width);
-			if (read < (std::streamsize)width) {
-				for (int i = 0; i < read; i++) {
-					str += u.buf[i];
-				}
-				stream->setstate(std::ios::eofbit);
-				break;
-			}
-			if (u.chr == cr) continue;
-			if (u.chr == lf) break;
-			for (int i = 0; i < read; i++) {
-				str += u.buf[i];
-			}
-		}
-	}
+        for (;;) {
+            u.chr = 0;
+            std::streamsize read = stream->rdbuf()->sgetn(u.buf, width);
+            if (read < (std::streamsize)width) {
+                for (int i = 0; i < read; i++) {
+                    str += u.buf[i];
+                }
+                stream->setstate(std::ios::eofbit);
+                break;
+            }
+            if (u.chr == cr) continue;
+            if (u.chr == lf) break;
+            for (int i = 0; i < read; i++) {
+                str += u.buf[i];
+            }
+        }
+    }
 
-	if (conv.get()) {
-		std::string tmp;
-		conv->Convert(str, tmp);
-		str = std::move(tmp);
-	}
+    if (conv.get()) {
+        std::string tmp;
+        conv->Convert(str, tmp);
+        str = std::move(tmp);
+    }
 
-	return true;
+    return true;
 }
 }
diff --git a/libaegisub/common/log.cpp b/libaegisub/common/log.cpp
index 57503302186d1f065d21b5735a0cc308cf6a28c1..ce7ceb1df8669029975fe14edf3a3b9c006c3ef6 100644
--- a/libaegisub/common/log.cpp
+++ b/libaegisub/common/log.cpp
@@ -25,7 +25,8 @@
 #include <boost/range/algorithm/remove_if.hpp>
 #include <chrono>
 
-namespace agi { namespace log {
+namespace agi {
+namespace log {
 
 /// Global log sink.
 LogSink *log;
@@ -36,85 +37,94 @@ const char *Severity_ID = "EAWID";
 
 LogSink::LogSink() : queue(dispatch::Create()) { }
 
-LogSink::~LogSink() {
-	// The destructor for emitters may try to log messages, so disable all the
-	// emitters before destructing any
-	decltype(emitters) emitters_temp;
-	queue->Sync([&]{ swap(emitters_temp, emitters); });
+LogSink::~LogSink()
+{
+    // The destructor for emitters may try to log messages, so disable all the
+    // emitters before destructing any
+    decltype(emitters) emitters_temp;
+    queue->Sync([&] { swap(emitters_temp, emitters); });
 }
 
-void LogSink::Log(SinkMessage const& sm) {
-	queue->Async([=] {
-		if (messages.size() < 250)
-			messages.push_back(sm);
-		else {
-			messages[next_idx] = sm;
-			if (++next_idx == 250)
-				next_idx = 0;
-		}
-		for (auto& em : emitters) em->log(sm);
-	});
+void LogSink::Log(SinkMessage const &sm)
+{
+    queue->Async([ = ] {
+        if (messages.size() < 250)
+            messages.push_back(sm);
+        else
+        {
+            messages[next_idx] = sm;
+            if (++next_idx == 250)
+                next_idx = 0;
+        }
+        for (auto &em : emitters) em->log(sm);
+    });
 }
 
-void LogSink::Subscribe(std::unique_ptr<Emitter> em) {
-	LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this;
-	auto tmp = em.release();
-	queue->Sync([=] { emitters.emplace_back(tmp); });
+void LogSink::Subscribe(std::unique_ptr<Emitter> em)
+{
+    LOG_D("agi/log/emitter/subscribe") << "Subscribe: " << this;
+    auto tmp = em.release();
+    queue->Sync([ = ] { emitters.emplace_back(tmp); });
 }
 
-void LogSink::Unsubscribe(Emitter *em) {
-	queue->Sync([=] {
-		emitters.erase(
-			boost::remove_if(emitters, [=](std::unique_ptr<Emitter> const& e) { return e.get() == em; }),
-			emitters.end());
-	});
-	LOG_D("agi/log/emitter/unsubscribe") << "Un-Subscribe: " << this;
+void LogSink::Unsubscribe(Emitter *em)
+{
+    queue->Sync([ = ] {
+        emitters.erase(
+        boost::remove_if(emitters, [ = ](std::unique_ptr<Emitter> const & e) { return e.get() == em; }),
+        emitters.end());
+    });
+    LOG_D("agi/log/emitter/unsubscribe") << "Un-Subscribe: " << this;
 }
 
-decltype(LogSink::messages) LogSink::GetMessages() const {
-	decltype(messages) ret;
-	queue->Sync([&] {
-		ret.reserve(messages.size());
-		ret.insert(ret.end(), messages.begin() + next_idx, messages.end());
-		ret.insert(ret.end(), messages.begin(), messages.begin() + next_idx);
-	});
-	return ret;
+decltype(LogSink::messages) LogSink::GetMessages() const
+{
+    decltype(messages) ret;
+    queue->Sync([&] {
+        ret.reserve(messages.size());
+        ret.insert(ret.end(), messages.begin() + next_idx, messages.end());
+        ret.insert(ret.end(), messages.begin(), messages.begin() + next_idx);
+    });
+    return ret;
 }
 
 Message::Message(const char *section, Severity severity, const char *file, const char *func, int line)
-: msg(buffer, sizeof buffer)
+    : msg(buffer, sizeof buffer)
 {
-	using namespace std::chrono;
-	sm.section = section;
-	sm.severity = severity;
-	sm.file = file;
-	sm.func = func;
-	sm.line = line;
-	sm.time = duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
+    using namespace std::chrono;
+    sm.section = section;
+    sm.severity = severity;
+    sm.file = file;
+    sm.func = func;
+    sm.line = line;
+    sm.time = duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count();
 }
 
-Message::~Message() {
-	sm.message = std::string(buffer, (std::string::size_type)msg.tellp());
-	agi::log::log->Log(sm);
+Message::~Message()
+{
+    sm.message = std::string(buffer, (std::string::size_type)msg.tellp());
+    agi::log::log->Log(sm);
 }
 
-JsonEmitter::JsonEmitter(fs::path const& directory)
-: fp(new boost::filesystem::ofstream(unique_path(directory/util::strftime("%Y-%m-%d-%H-%M-%S-%%%%%%%%.json"))))
+JsonEmitter::JsonEmitter(fs::path const &directory)
+    : fp(new boost::filesystem::ofstream(unique_path(directory / util::strftime("%Y-%m-%d-%H-%M-%S-%%%%%%%%.json"))))
 {
 }
 
-void JsonEmitter::log(SinkMessage const& sm) {
-	json::Object entry;
-	entry["sec"]      = sm.time / 1000000000;
-	entry["usec"]     = sm.time % 1000000000;
-	entry["severity"] = sm.severity;
-	entry["section"]  = sm.section;
-	entry["file"]     = sm.file;
-	entry["func"]     = sm.func;
-	entry["line"]     = sm.line;
-	entry["message"]  = sm.message;
-	agi::JsonWriter::Write(entry, *fp);
-	fp->flush();
+void JsonEmitter::log(SinkMessage const &sm)
+{
+    json::Object entry;
+    entry["sec"]      = sm.time / 1000000000;
+    entry["usec"]     = sm.time % 1000000000;
+    entry["severity"] = sm.severity;
+    entry["section"]  = sm.section;
+    entry["file"]     = sm.file;
+    entry["func"]     = sm.func;
+    entry["line"]     = sm.line;
+    entry["message"]  = sm.message;
+    agi::JsonWriter::Write(entry, *fp);
+    fp->flush();
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/mru.cpp b/libaegisub/common/mru.cpp
index dd7cfde92b116d5d21de617f2fdd7e613dfc663b..597f8a4a6fa99989c161e8b1d52e753fb54a4a2e 100644
--- a/libaegisub/common/mru.cpp
+++ b/libaegisub/common/mru.cpp
@@ -24,128 +24,137 @@
 
 namespace {
 const char *mru_names[] = {
-	"Audio",
-	"Find",
-	"Keyframes",
-	"Replace",
-	"Subtitle",
-	"Timecodes",
-	"Video",
+    "Audio",
+    "Find",
+    "Keyframes",
+    "Replace",
+    "Subtitle",
+    "Timecodes",
+    "Video",
 };
 
 const char *option_names[] = {
-	"Limits/MRU",
-	"Limits/Find Replace",
-	"Limits/MRU",
-	"Limits/Find Replace",
-	"Limits/MRU",
-	"Limits/MRU",
-	"Limits/MRU",
+    "Limits/MRU",
+    "Limits/Find Replace",
+    "Limits/MRU",
+    "Limits/Find Replace",
+    "Limits/MRU",
+    "Limits/MRU",
+    "Limits/MRU",
 };
 
-int mru_index(const char *key) {
-	int i;
-	switch (*key) {
-	case 'A': i = 0; break;
-	case 'F': i = 1; break;
-	case 'K': i = 2; break;
-	case 'R': i = 3; break;
-	case 'S': i = 4; break;
-	case 'T': i = 5; break;
-	case 'V': i = 6; break;
-	default: return -1;
-	}
-	return strcmp(key, mru_names[i]) == 0 ? i : -1;
+int mru_index(const char *key)
+{
+    int i;
+    switch (*key) {
+    case 'A': i = 0; break;
+    case 'F': i = 1; break;
+    case 'K': i = 2; break;
+    case 'R': i = 3; break;
+    case 'S': i = 4; break;
+    case 'T': i = 5; break;
+    case 'V': i = 6; break;
+    default: return -1;
+    }
+    return strcmp(key, mru_names[i]) == 0 ? i : -1;
 }
 }
 
 namespace agi {
-MRUManager::MRUManager(agi::fs::path const& config, std::pair<const char *, size_t> default_config, agi::Options *options)
-: config_name(config)
-, options(options)
+MRUManager::MRUManager(agi::fs::path const &config, std::pair<const char *, size_t> default_config, agi::Options *options)
+    : config_name(config)
+    , options(options)
 {
-	LOG_D("agi/mru") << "Loading MRU List";
+    LOG_D("agi/mru") << "Loading MRU List";
 
-	auto root = json_util::file(config, default_config);
-	for (auto const& it : static_cast<json::Object const&>(root))
-		Load(it.first.c_str(), it.second);
+    auto root = json_util::file(config, default_config);
+    for (auto const &it : static_cast<json::Object const &>(root))
+        Load(it.first.c_str(), it.second);
 }
 
-MRUManager::MRUListMap &MRUManager::Find(const char *key) {
-	auto index = mru_index(key);
-	if (index == -1)
-		throw MRUError("Invalid key value");
-	return mru[index];
+MRUManager::MRUListMap &MRUManager::Find(const char *key)
+{
+    auto index = mru_index(key);
+    if (index == -1)
+        throw MRUError("Invalid key value");
+    return mru[index];
 }
 
-void MRUManager::Add(const char *key, agi::fs::path const& entry) {
-	MRUListMap &map = Find(key);
-	auto it = find(begin(map), end(map), entry);
-	if (it == begin(map) && it != end(map))
-		return;
-	if (it != end(map))
-		rotate(begin(map), it, it + 1);
-	else {
-		map.insert(begin(map), entry);
-		Prune(key, map);
-	}
-
-	Flush();
+void MRUManager::Add(const char *key, agi::fs::path const &entry)
+{
+    MRUListMap &map = Find(key);
+    auto it = find(begin(map), end(map), entry);
+    if (it == begin(map) && it != end(map))
+        return;
+    if (it != end(map))
+        rotate(begin(map), it, it + 1);
+    else {
+        map.insert(begin(map), entry);
+        Prune(key, map);
+    }
+
+    Flush();
 }
 
-void MRUManager::Remove(const char *key, agi::fs::path const& entry) {
-	auto& map = Find(key);
-	map.erase(remove(begin(map), end(map), entry), end(map));
-	Flush();
+void MRUManager::Remove(const char *key, agi::fs::path const &entry)
+{
+    auto &map = Find(key);
+    map.erase(remove(begin(map), end(map), entry), end(map));
+    Flush();
 }
 
-const MRUManager::MRUListMap* MRUManager::Get(const char *key) {
-	return &Find(key);
+const MRUManager::MRUListMap *MRUManager::Get(const char *key)
+{
+    return &Find(key);
 }
 
-agi::fs::path const& MRUManager::GetEntry(const char *key, const size_t entry) {
-	const auto map = Get(key);
-	if (entry >= map->size())
-		throw MRUError("Requested element index is out of range.");
+agi::fs::path const &MRUManager::GetEntry(const char *key, const size_t entry)
+{
+    const auto map = Get(key);
+    if (entry >= map->size())
+        throw MRUError("Requested element index is out of range.");
 
-	return *next(map->begin(), entry);
+    return *next(map->begin(), entry);
 }
 
-void MRUManager::Flush() {
-	json::Object out;
+void MRUManager::Flush()
+{
+    json::Object out;
 
-	for (size_t i = 0; i < mru.size(); ++i) {
-		json::Array &array = out[mru_names[i]];
-		for (auto const& p : mru[i])
-			array.push_back(p.string());
-	}
+    for (size_t i = 0; i < mru.size(); ++i) {
+        json::Array &array = out[mru_names[i]];
+        for (auto const &p : mru[i])
+            array.push_back(p.string());
+    }
 
-	agi::JsonWriter::Write(out, io::Save(config_name).Get());
+    agi::JsonWriter::Write(out, io::Save(config_name).Get());
 }
 
-void MRUManager::Prune(const char *key, MRUListMap& map) const {
-	size_t limit = 16u;
-	if (options) {
-		int idx = mru_index(key);
-		if (idx != -1)
-			limit = (size_t)options->Get(option_names[idx])->GetInt();
-	}
-	map.resize(std::min(limit, map.size()));
+void MRUManager::Prune(const char *key, MRUListMap &map) const
+{
+    size_t limit = 16u;
+    if (options) {
+        int idx = mru_index(key);
+        if (idx != -1)
+            limit = (size_t)options->Get(option_names[idx])->GetInt();
+    }
+    map.resize(std::min(limit, map.size()));
 }
 
-void MRUManager::Load(const char *key, const json::Array& array) {
-	int idx = mru_index(key);
-	if (idx == -1) return;
-
-	try {
-		mru[idx].reserve(array.size());
-		for (std::string const& str : array)
-			mru[idx].push_back(str);
-	}
-	catch (json::Exception const&) {
-		// Out of date MRU file; just discard the data and skip it
-	}
-	Prune(key, mru[idx]);
+void MRUManager::Load(const char *key, const json::Array &array)
+{
+    int idx = mru_index(key);
+    if (idx == -1) return;
+
+    try {
+        mru[idx].reserve(array.size());
+        for (std::string const &str : array)
+            mru[idx].push_back(str);
+    }
+    catch (json::Exception const &) {
+        // Out of date MRU file; just discard the data and skip it
+    }
+    Prune(key, mru[idx]);
 }
 
 }
diff --git a/libaegisub/common/option.cpp b/libaegisub/common/option.cpp
index cdf5a07b89734ff9f65be109ad06e81d3139241f..96173d87b19038c6ecbb715fa34244b0e4a31f0b 100644
--- a/libaegisub/common/option.cpp
+++ b/libaegisub/common/option.cpp
@@ -41,280 +41,289 @@ using namespace agi;
 DEFINE_EXCEPTION(OptionJsonValueError, Exception);
 
 class ConfigVisitor final : public json::ConstVisitor {
-	std::vector<std::unique_ptr<OptionValue>> values;
-
-	/// Option name prefix to add to read names
-	std::string name;
-
-	/// Log errors rather than throwing them, for when loading user config files
-	/// (as a bad user config file shouldn't make the program fail to start)
-	bool ignore_errors;
-
-	void Error(const char *message) {
-		if (ignore_errors)
-			LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << message;
-		else
-			throw OptionJsonValueError(message);
-	}
-
-	template<class OptionValueType>
-	void ReadArray(json::Array const& src, std::string const& array_type) {
-		typename OptionValueType::value_type arr;
-		arr.reserve(src.size());
-		for (json::Object const& obj : src)
-			arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
-
-		values.push_back(agi::make_unique<OptionValueType>(name, std::move(arr)));
-	}
-
-	void Visit(const json::Object& object) {
-		auto old_name = name;
-		for (auto const& obj : object) {
-			name = old_name + (old_name.empty() ? "" : "/") + obj.first;
-			obj.second.Accept(*this);
-		}
-		name = old_name;
-	}
-
-	void Visit(const json::Array& array) {
-		if (array.empty())
-			return Error("Cannot infer the type of an empty array");
-
-		json::Object const& front = array.front();
-		if (front.size() != 1)
-			return Error("Invalid array member");
-
-		auto const& array_type = front.begin()->first;
-		for (json::Object const& obj : array) {
-			if (obj.size() != 1)
-				return Error("Invalid array member");
-			if (obj.begin()->first != array_type)
-				return Error("Attempt to insert value into array of wrong type");
-		}
-
-		if (array_type == "string")
-			ReadArray<OptionValueListString>(array, array_type);
-		else if (array_type == "int")
-			ReadArray<OptionValueListInt>(array, array_type);
-		else if (array_type == "double")
-			ReadArray<OptionValueListDouble>(array, array_type);
-		else if (array_type == "bool")
-			ReadArray<OptionValueListBool>(array, array_type);
-		else if (array_type == "color")
-			ReadArray<OptionValueListColor>(array, array_type);
-		else
-			Error("Array type not handled");
-	}
-
-	void Visit(int64_t number) {
-		values.push_back(agi::make_unique<OptionValueInt>(name, number));
-	}
-
-	void Visit(double number) {
-		values.push_back(agi::make_unique<OptionValueDouble>(name, number));
-	}
-
-	void Visit(const json::String& string) {
-		size_t size = string.size();
-		if ((size == 4 && string[0] == '#') ||
-			(size == 7 && string[0] == '#') ||
-			(size >= 10 && boost::starts_with(string, "rgb(")) ||
-			((size == 9 || size == 10) && boost::starts_with(string, "&H")))
-		{
-			values.push_back(agi::make_unique<OptionValueColor>(name, string));
-		} else {
-			values.push_back(agi::make_unique<OptionValueString>(name, string));
-		}
-	}
-
-	void Visit(bool boolean) {
-		values.push_back(agi::make_unique<OptionValueBool>(name, boolean));
-	}
-
-	void Visit(const json::Null& null) {
-		Error("Attempt to read null value");
-	}
+    std::vector<std::unique_ptr<OptionValue>> values;
+
+    /// Option name prefix to add to read names
+    std::string name;
+
+    /// Log errors rather than throwing them, for when loading user config files
+    /// (as a bad user config file shouldn't make the program fail to start)
+    bool ignore_errors;
+
+    void Error(const char *message) {
+        if (ignore_errors)
+            LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << message;
+        else
+            throw OptionJsonValueError(message);
+    }
+
+    template<class OptionValueType>
+    void ReadArray(json::Array const &src, std::string const &array_type) {
+        typename OptionValueType::value_type arr;
+        arr.reserve(src.size());
+        for (json::Object const &obj : src)
+            arr.push_back((typename OptionValueType::value_type::value_type)(obj.begin()->second));
+
+        values.push_back(agi::make_unique<OptionValueType>(name, std::move(arr)));
+    }
+
+    void Visit(const json::Object &object) {
+        auto old_name = name;
+        for (auto const &obj : object) {
+            name = old_name + (old_name.empty() ? "" : "/") + obj.first;
+            obj.second.Accept(*this);
+        }
+        name = old_name;
+    }
+
+    void Visit(const json::Array &array) {
+        if (array.empty())
+            return Error("Cannot infer the type of an empty array");
+
+        json::Object const &front = array.front();
+        if (front.size() != 1)
+            return Error("Invalid array member");
+
+        auto const &array_type = front.begin()->first;
+        for (json::Object const &obj : array) {
+            if (obj.size() != 1)
+                return Error("Invalid array member");
+            if (obj.begin()->first != array_type)
+                return Error("Attempt to insert value into array of wrong type");
+        }
+
+        if (array_type == "string")
+            ReadArray<OptionValueListString>(array, array_type);
+        else if (array_type == "int")
+            ReadArray<OptionValueListInt>(array, array_type);
+        else if (array_type == "double")
+            ReadArray<OptionValueListDouble>(array, array_type);
+        else if (array_type == "bool")
+            ReadArray<OptionValueListBool>(array, array_type);
+        else if (array_type == "color")
+            ReadArray<OptionValueListColor>(array, array_type);
+        else
+            Error("Array type not handled");
+    }
+
+    void Visit(int64_t number) {
+        values.push_back(agi::make_unique<OptionValueInt>(name, number));
+    }
+
+    void Visit(double number) {
+        values.push_back(agi::make_unique<OptionValueDouble>(name, number));
+    }
+
+    void Visit(const json::String &string) {
+        size_t size = string.size();
+        if ((size == 4 && string[0] == '#') ||
+            (size == 7 && string[0] == '#') ||
+            (size >= 10 && boost::starts_with(string, "rgb(")) ||
+            ((size == 9 || size == 10) && boost::starts_with(string, "&H"))) {
+            values.push_back(agi::make_unique<OptionValueColor>(name, string));
+        }
+        else {
+            values.push_back(agi::make_unique<OptionValueString>(name, string));
+        }
+    }
+
+    void Visit(bool boolean) {
+        values.push_back(agi::make_unique<OptionValueBool>(name, boolean));
+    }
+
+    void Visit(const json::Null &null) {
+        Error("Attempt to read null value");
+    }
 
 public:
-	ConfigVisitor(bool ignore_errors) : ignore_errors(ignore_errors) { }
-	std::vector<std::unique_ptr<OptionValue>> Values() { return std::move(values); }
+    ConfigVisitor(bool ignore_errors) : ignore_errors(ignore_errors) { }
+    std::vector<std::unique_ptr<OptionValue>> Values() { return std::move(values); }
 };
 
 /// @brief Write an option to a json object
 /// @param[out] obj  Parent object
 /// @param[in] path  Path option should be stored in.
 /// @param[in] value Value to write.
-void put_option(json::Object &obj, const std::string &path, json::UnknownElement value) {
-	std::string::size_type pos = path.find('/');
-	// Not having a '/' denotes it is a leaf.
-	if (pos == std::string::npos) {
-		assert(obj.find(path) == obj.end());
-		obj[path] = std::move(value);
-	}
-	else
-		put_option(obj[path.substr(0, pos)], path.substr(pos + 1), std::move(value));
+void put_option(json::Object &obj, const std::string &path, json::UnknownElement value)
+{
+    std::string::size_type pos = path.find('/');
+    // Not having a '/' denotes it is a leaf.
+    if (pos == std::string::npos) {
+        assert(obj.find(path) == obj.end());
+        obj[path] = std::move(value);
+    }
+    else
+        put_option(obj[path.substr(0, pos)], path.substr(pos + 1), std::move(value));
 }
 
 template<class T>
-void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const& value) {
-	json::Array array;
-	array.resize(value.size());
-	for (size_t i = 0, size = value.size(); i < size; ++i)
-		static_cast<json::Object&>(array[i])[element_key] = (json::UnknownElement)value[i];
-	put_option(obj, path, std::move(array));
+void put_array(json::Object &obj, const std::string &path, const char *element_key, std::vector<T> const &value)
+{
+    json::Array array;
+    array.resize(value.size());
+    for (size_t i = 0, size = value.size(); i < size; ++i)
+        static_cast<json::Object &>(array[i])[element_key] = (json::UnknownElement)value[i];
+    put_option(obj, path, std::move(array));
 }
 
 struct option_name_cmp {
-	bool operator()(std::unique_ptr<OptionValue> const& a, std::unique_ptr<OptionValue> const& b) const {
-		return a->GetName() < b->GetName();
-	}
+    bool operator()(std::unique_ptr<OptionValue> const &a, std::unique_ptr<OptionValue> const &b) const {
+        return a->GetName() < b->GetName();
+    }
 
-	bool operator()(std::unique_ptr<OptionValue> const& a, std::string const& b) const {
-		return a->GetName() < b;
-	}
+    bool operator()(std::unique_ptr<OptionValue> const &a, std::string const &b) const {
+        return a->GetName() < b;
+    }
 
-	bool operator()(std::unique_ptr<OptionValue> const& a, const char *b) const {
-		return a->GetName() < b;
-	}
+    bool operator()(std::unique_ptr<OptionValue> const &a, const char *b) const {
+        return a->GetName() < b;
+    }
 };
 
 }
 
 namespace agi {
 
-Options::Options(agi::fs::path const& file, std::pair<const char *, size_t> default_config, const OptionSetting setting)
-: config_file(file)
-, setting(setting)
+Options::Options(agi::fs::path const &file, std::pair<const char *, size_t> default_config, const OptionSetting setting)
+    : config_file(file)
+    , setting(setting)
 {
-	LOG_D("agi/options") << "New Options object";
-	boost::interprocess::ibufferstream stream(default_config.first, default_config.second);
-	LoadConfig(stream);
+    LOG_D("agi/options") << "New Options object";
+    boost::interprocess::ibufferstream stream(default_config.first, default_config.second);
+    LoadConfig(stream);
 }
 
-Options::~Options() {
-	if ((setting & FLUSH_SKIP) != FLUSH_SKIP)
-		Flush();
+Options::~Options()
+{
+    if ((setting & FLUSH_SKIP) != FLUSH_SKIP)
+        Flush();
 }
 
-void Options::ConfigUser() {
-	try {
-		LoadConfig(*io::Open(config_file), true);
-	}
-	catch (fs::FileNotFound const&) {
-		return;
-	}
+void Options::ConfigUser()
+{
+    try {
+        LoadConfig(*io::Open(config_file), true);
+    }
+    catch (fs::FileNotFound const &) {
+        return;
+    }
 }
 
-void Options::LoadConfig(std::istream& stream, bool ignore_errors) {
-	json::UnknownElement config_root;
-
-	try {
-		json::Reader::Read(config_root, stream);
-	} catch (json::Reader::ParseException& e) {
-		LOG_E("option/load") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
-		return;
-	} catch (json::Exception& e) {
-		LOG_E("option/load") << "json::Exception: " << e.what();
-		return;
-	}
-
-	ConfigVisitor config_visitor(ignore_errors);
-	config_root.Accept(config_visitor);
-
-	auto new_values = config_visitor.Values();
-	if (new_values.empty()) return;
-
-	sort(begin(new_values), end(new_values), option_name_cmp());
-
-	if (values.empty()) {
-		values = std::move(new_values);
-		return;
-	}
-
-	auto src_it = begin(new_values), src_end = end(new_values);
-	auto dst_it = begin(values), dst_end = end(values);
-
-	while (src_it != src_end && dst_it != dst_end) {
-		int cmp = (*src_it)->GetName().compare((*dst_it)->GetName());
-		if (cmp < 0) // Option doesn't exist in defaults so skip
-			++src_it;
-		else if (cmp > 0)
-			++dst_it;
-		else {
-			if (ignore_errors) {
-				try {
-					(*dst_it)->Set((*src_it).get());
-				}
-				catch (agi::InternalError const& e) {
-					LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << e.GetMessage();
-				}
-			}
-			else {
-				*dst_it = std::move(*src_it);
-			}
-			++src_it;
-			++dst_it;
-		}
-	}
+void Options::LoadConfig(std::istream &stream, bool ignore_errors)
+{
+    json::UnknownElement config_root;
+
+    try {
+        json::Reader::Read(config_root, stream);
+    }
+    catch (json::Reader::ParseException &e) {
+        LOG_E("option/load") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
+        return;
+    }
+    catch (json::Exception &e) {
+        LOG_E("option/load") << "json::Exception: " << e.what();
+        return;
+    }
+
+    ConfigVisitor config_visitor(ignore_errors);
+    config_root.Accept(config_visitor);
+
+    auto new_values = config_visitor.Values();
+    if (new_values.empty()) return;
+
+    sort(begin(new_values), end(new_values), option_name_cmp());
+
+    if (values.empty()) {
+        values = std::move(new_values);
+        return;
+    }
+
+    auto src_it = begin(new_values), src_end = end(new_values);
+    auto dst_it = begin(values), dst_end = end(values);
+
+    while (src_it != src_end && dst_it != dst_end) {
+        int cmp = (*src_it)->GetName().compare((*dst_it)->GetName());
+        if (cmp < 0) // Option doesn't exist in defaults so skip
+            ++src_it;
+        else if (cmp > 0)
+            ++dst_it;
+        else {
+            if (ignore_errors) {
+                try {
+                    (*dst_it)->Set((*src_it).get());
+                }
+                catch (agi::InternalError const &e) {
+                    LOG_E("option/load/config_visitor") << "Error loading option from user configuration: " << e.GetMessage();
+                }
+            }
+            else {
+                *dst_it = std::move(*src_it);
+            }
+            ++src_it;
+            ++dst_it;
+        }
+    }
 }
 
-OptionValue *Options::Get(const char *name) {
-	auto index = lower_bound(begin(values), end(values), name, option_name_cmp());
-	if (index != end(values) && (*index)->GetName() == name)
-		return index->get();
+OptionValue *Options::Get(const char *name)
+{
+    auto index = lower_bound(begin(values), end(values), name, option_name_cmp());
+    if (index != end(values) && (*index)->GetName() == name)
+        return index->get();
 
-	LOG_E("option/get") << "agi::Options::Get Option not found: (" << name << ")";
-	throw agi::InternalError("Option value not found: " + std::string(name));
+    LOG_E("option/get") << "agi::Options::Get Option not found: (" << name << ")";
+    throw agi::InternalError("Option value not found: " + std::string(name));
 }
 
-void Options::Flush() const {
-	json::Object obj_out;
+void Options::Flush() const
+{
+    json::Object obj_out;
 
-	for (auto const& ov : values) {
-		switch (ov->GetType()) {
-			case OptionType::String:
-				put_option(obj_out, ov->GetName(), ov->GetString());
-				break;
+    for (auto const &ov : values) {
+        switch (ov->GetType()) {
+        case OptionType::String:
+            put_option(obj_out, ov->GetName(), ov->GetString());
+            break;
 
-			case OptionType::Int:
-				put_option(obj_out, ov->GetName(), ov->GetInt());
-				break;
+        case OptionType::Int:
+            put_option(obj_out, ov->GetName(), ov->GetInt());
+            break;
 
-			case OptionType::Double:
-				put_option(obj_out, ov->GetName(), ov->GetDouble());
-				break;
+        case OptionType::Double:
+            put_option(obj_out, ov->GetName(), ov->GetDouble());
+            break;
 
-			case OptionType::Color:
-				put_option(obj_out, ov->GetName(), ov->GetColor().GetRgbFormatted());
-				break;
+        case OptionType::Color:
+            put_option(obj_out, ov->GetName(), ov->GetColor().GetRgbFormatted());
+            break;
 
-			case OptionType::Bool:
-				put_option(obj_out, ov->GetName(), ov->GetBool());
-				break;
+        case OptionType::Bool:
+            put_option(obj_out, ov->GetName(), ov->GetBool());
+            break;
 
-			case OptionType::ListString:
-				put_array(obj_out, ov->GetName(), "string", ov->GetListString());
-				break;
+        case OptionType::ListString:
+            put_array(obj_out, ov->GetName(), "string", ov->GetListString());
+            break;
 
-			case OptionType::ListInt:
-				put_array(obj_out, ov->GetName(), "int", ov->GetListInt());
-				break;
+        case OptionType::ListInt:
+            put_array(obj_out, ov->GetName(), "int", ov->GetListInt());
+            break;
 
-			case OptionType::ListDouble:
-				put_array(obj_out, ov->GetName(), "double", ov->GetListDouble());
-				break;
+        case OptionType::ListDouble:
+            put_array(obj_out, ov->GetName(), "double", ov->GetListDouble());
+            break;
 
-			case OptionType::ListColor:
-				put_array(obj_out, ov->GetName(), "color", ov->GetListColor());
-				break;
+        case OptionType::ListColor:
+            put_array(obj_out, ov->GetName(), "color", ov->GetListColor());
+            break;
 
-			case OptionType::ListBool:
-				put_array(obj_out, ov->GetName(), "bool", ov->GetListBool());
-				break;
-		}
-	}
+        case OptionType::ListBool:
+            put_array(obj_out, ov->GetName(), "bool", ov->GetListBool());
+            break;
+        }
+    }
 
-	agi::JsonWriter::Write(obj_out, io::Save(config_file).Get());
+    agi::JsonWriter::Write(obj_out, io::Save(config_file).Get());
 }
 
 } // namespace agi
diff --git a/libaegisub/common/option_value.cpp b/libaegisub/common/option_value.cpp
index 46aff51a1db74e131c04c7a7ab199f85640d34c7..915e8a7e72156574cd447ff79064ed084b21b55b 100644
--- a/libaegisub/common/option_value.cpp
+++ b/libaegisub/common/option_value.cpp
@@ -17,26 +17,28 @@
 #include <libaegisub/option_value.h>
 
 namespace agi {
-	std::string OptionValue::TypeToString(OptionType type) const {
-		switch (type) {
-			case OptionType::String:     return "String";
-			case OptionType::Int:        return "Integer";
-			case OptionType::Double:     return "Double";
-			case OptionType::Color:      return "Color";
-			case OptionType::Bool:       return "Bool";
-			case OptionType::ListString: return "List of Strings";
-			case OptionType::ListInt:    return "List of Integers";
-			case OptionType::ListDouble: return "List of Doubles";
-			case OptionType::ListColor:  return "List of Colors";
-			case OptionType::ListBool:   return "List of Bools";
-		}
-		throw agi::InternalError("Invalid option type");
-	}
+std::string OptionValue::TypeToString(OptionType type) const
+{
+    switch (type) {
+    case OptionType::String:     return "String";
+    case OptionType::Int:        return "Integer";
+    case OptionType::Double:     return "Double";
+    case OptionType::Color:      return "Color";
+    case OptionType::Bool:       return "Bool";
+    case OptionType::ListString: return "List of Strings";
+    case OptionType::ListInt:    return "List of Integers";
+    case OptionType::ListDouble: return "List of Doubles";
+    case OptionType::ListColor:  return "List of Colors";
+    case OptionType::ListBool:   return "List of Bools";
+    }
+    throw agi::InternalError("Invalid option type");
+}
 
-	InternalError OptionValue::TypeError(OptionType type) const {
-		return InternalError("Invalid type for option " + name + ": expected " +
-			TypeToString(type) + ", got " + TypeToString(GetType()));
-	}
+InternalError OptionValue::TypeError(OptionType type) const
+{
+    return InternalError("Invalid type for option " + name + ": expected " +
+                         TypeToString(type) + ", got " + TypeToString(GetType()));
+}
 
 #define CONFIG_DEFINE_SET(type_name, type) \
 	void OptionValue##type_name::Set(const OptionValue *nv) { SetValue(nv->Get##type_name()); } \
diff --git a/libaegisub/common/parser.cpp b/libaegisub/common/parser.cpp
index ef53ed0d20a91da684784cb2431404eae733ce91..d76e3bee7af5dfb9c011f3de4ff8d784181d13f0 100644
--- a/libaegisub/common/parser.cpp
+++ b/libaegisub/common/parser.cpp
@@ -35,11 +35,11 @@
 #endif
 
 BOOST_FUSION_ADAPT_STRUCT(
-	agi::Color,
-	(unsigned char, r)
-	(unsigned char, g)
-	(unsigned char, b)
-	(unsigned char, a)
+    agi::Color,
+    (unsigned char, r)
+    (unsigned char, g)
+    (unsigned char, b)
+    (unsigned char, a)
 )
 
 namespace {
@@ -47,196 +47,201 @@ using namespace boost::spirit;
 
 /// Convert a abgr value in an int or unsigned int to an agi::Color
 struct unpack_colors : public boost::static_visitor<agi::Color> {
-	template<typename T> struct result { typedef agi::Color type; };
+    template<typename T> struct result { typedef agi::Color type; };
 
-	template<class T> agi::Color operator()(T arg) const {
-		return boost::apply_visitor(*this, arg);
-	}
+    template<class T> agi::Color operator()(T arg) const {
+        return boost::apply_visitor(*this, arg);
+    }
 
-	agi::Color operator()(int abgr) const { return (*this)((unsigned)abgr); }
-	agi::Color operator()(unsigned int abgr) const {
-		return agi::Color(abgr & 0xFF, (abgr >> 8) & 0xFF, (abgr >> 16) & 0xFF, (abgr >> 24) & 0xFF);
-	}
+    agi::Color operator()(int abgr) const { return (*this)((unsigned)abgr); }
+    agi::Color operator()(unsigned int abgr) const {
+        return agi::Color(abgr & 0xFF, (abgr >> 8) & 0xFF, (abgr >> 16) & 0xFF, (abgr >> 24) & 0xFF);
+    }
 };
 
 template<typename Iterator>
 struct color_grammar : qi::grammar<Iterator, agi::Color()> {
-	qi::rule<Iterator, agi::Color()> color;
+    qi::rule<Iterator, agi::Color()> color;
 
-	qi::rule<Iterator, agi::Color()> css_color;
-	qi::rule<Iterator, agi::Color()> ass_color;
+    qi::rule<Iterator, agi::Color()> css_color;
+    qi::rule<Iterator, agi::Color()> ass_color;
 
-	qi::rule<Iterator, unsigned char()> rgb_component;
-	qi::rule<Iterator, unsigned char()> rgb_percent;
-	qi::rule<Iterator, unsigned char()> hex_byte;
-	qi::rule<Iterator, unsigned char()> hex_char;
+    qi::rule<Iterator, unsigned char()> rgb_component;
+    qi::rule<Iterator, unsigned char()> rgb_percent;
+    qi::rule<Iterator, unsigned char()> hex_byte;
+    qi::rule<Iterator, unsigned char()> hex_char;
 
-	qi::rule<Iterator> comma;
-	qi::rule<Iterator> blank;
+    qi::rule<Iterator> comma;
+    qi::rule<Iterator> blank;
 
 #define HEX_PARSER(type, len) qi::uint_parser<unsigned type, 16, len, len>()
 
-	color_grammar() : color_grammar::base_type(color) {
-		color = css_color | ass_color;
-
-		boost::phoenix::function<unpack_colors> unpack;
-
-		// Order is important here; int_ (for SSA) needs to come before the ASS
-		// option as decimal numbers of the appropriate length are also valid
-		// hex numbers
-		// ASS with alpha needs to come before ASS without alpha for the same
-		// reason
-		ass_color = (
-			int_
-			| -lit('&') >> -(lit('H') | lit('h')) >> (HEX_PARSER(int, 8) | HEX_PARSER(int, 6)) >> -lit('&')
-		)[_val = unpack(_1)] >> blank >> qi::eoi;
-
-		css_color
-			= "rgb(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')'
-			| '#' >> hex_byte >> hex_byte >> hex_byte
-			| '#' >> hex_char >> hex_char >> hex_char
-			;
-
-		hex_char = HEX_PARSER(int, 1)[_val = _1 * 16 + _1];
-		hex_byte = HEX_PARSER(int, 2);
-		rgb_component = qi::uint_parser<unsigned char, 10, 1, 3>();
-
-		comma = *qi::blank >> "," >> *qi::blank;
-		blank = *qi::blank;
-	}
+    color_grammar() : color_grammar::base_type(color) {
+        color = css_color | ass_color;
+
+        boost::phoenix::function<unpack_colors> unpack;
+
+        // Order is important here; int_ (for SSA) needs to come before the ASS
+        // option as decimal numbers of the appropriate length are also valid
+        // hex numbers
+        // ASS with alpha needs to come before ASS without alpha for the same
+        // reason
+        ass_color = (
+                        int_
+                        | -lit('&') >> -(lit('H') | lit('h')) >> (HEX_PARSER(int, 8) | HEX_PARSER(int, 6)) >> -lit('&')
+                    )[_val = unpack(_1)] >> blank >> qi::eoi;
+
+        css_color
+            = "rgb(" >> blank >> rgb_component >> comma >> rgb_component >> comma >> rgb_component >> blank >> ')'
+              | '#' >> hex_byte >> hex_byte >> hex_byte
+              | '#' >> hex_char >> hex_char >> hex_char
+              ;
+
+        hex_char = HEX_PARSER(int, 1)[_val = _1 * 16 + _1];
+        hex_byte = HEX_PARSER(int, 2);
+        rgb_component = qi::uint_parser<unsigned char, 10, 1, 3>();
+
+        comma = *qi::blank >> "," >> *qi::blank;
+        blank = *qi::blank;
+    }
 };
 
 template <typename Lexer>
 struct dialogue_tokens final : lex::lexer<Lexer> {
-	int paren_depth;
-
-	template<typename KT>
-	void init(KT &&kara_templater) {
-		using lex::_state;
-		using lex::char_;
-		using lex::string;
-		using namespace boost::phoenix;
-		using namespace agi::ass::DialogueTokenType;
-
-		this->self
-			= string("\\\\[nNh]", LINE_BREAK)
-			| char_('{', OVR_BEGIN)[ref(paren_depth) = 0, _state = "OVR"]
-			| kara_templater
-			| string(".", TEXT)
-			;
-
-		this->self("OVR")
-			= char_('{', ERROR)
-			| char_('}', OVR_END)[_state = "INITIAL"]
-			| char_('\\', TAG_START)[_state = "TAGSTART"]
-			| string("\\s+", WHITESPACE)
-			| kara_templater
-			| string(".", COMMENT)
-			;
-
-		this->self("ARG")
-			= char_('{', ERROR)
-			| char_('}', OVR_END)[_state = "INITIAL"]
-			| char_('(', OPEN_PAREN)[++ref(paren_depth)]
-			| char_(')', CLOSE_PAREN)[--ref(paren_depth), if_(ref(paren_depth) == 0)[_state = "OVR"]]
-			| char_('\\', TAG_START)[_state = "TAGSTART"]
-			| char_(',', ARG_SEP)
-			| string("\\s+", WHITESPACE)
-			| string(".", ARG)
-			| kara_templater
-			;
-
-		this->self("TAGSTART")
-			= string("\\s+", WHITESPACE)
-			| string("r|fn", TAG_NAME)[_state = "ARG"]
-			| char_('\\', TAG_START)
-			| char_('}', OVR_END)[_state = "INITIAL"]
-			| string("[a-z0-9]", TAG_NAME)[_state = "TAGNAME"]
-			| string(".", COMMENT)[_state = "OVR"]
-			| kara_templater
-			;
-
-		this->self("TAGNAME")
-			= string("[a-z]+", TAG_NAME)[_state = "ARG"]
-			| char_('(', OPEN_PAREN)[++ref(paren_depth), _state = "ARG"]
-			| char_(')', CLOSE_PAREN)[--ref(paren_depth), if_(ref(paren_depth) == 0)[_state = "OVR"]]
-			| char_('}', OVR_END)[_state = "INITIAL"]
-			| char_('\\', TAG_START)[_state = "TAGSTART"]
-			| string(".", ARG)[_state = "ARG"]
-			| kara_templater
-			;
-	}
-
-	dialogue_tokens(bool karaoke_templater) : paren_depth(0) {
-		using lex::string;
-		using namespace agi::ass::DialogueTokenType;
-
-		if (karaoke_templater)
-			init(string("![^!]*!", KARAOKE_TEMPLATE) | string("\\$[A-Za-z_]+", KARAOKE_VARIABLE));
-		else
-			init(lex::char_('\1'));
-	}
+    int paren_depth;
+
+    template<typename KT>
+    void init(KT &&kara_templater) {
+        using lex::_state;
+        using lex::char_;
+        using lex::string;
+        using namespace boost::phoenix;
+        using namespace agi::ass::DialogueTokenType;
+
+        this->self
+            = string("\\\\[nNh]", LINE_BREAK)
+              | char_('{', OVR_BEGIN)[ref(paren_depth) = 0, _state = "OVR"]
+              | kara_templater
+              | string(".", TEXT)
+              ;
+
+        this->self("OVR")
+            = char_('{', ERROR)
+              | char_('}', OVR_END)[_state = "INITIAL"]
+              | char_('\\', TAG_START)[_state = "TAGSTART"]
+              | string("\\s+", WHITESPACE)
+              | kara_templater
+              | string(".", COMMENT)
+              ;
+
+        this->self("ARG")
+            = char_('{', ERROR)
+              | char_('}', OVR_END)[_state = "INITIAL"]
+              | char_('(', OPEN_PAREN)[++ref(paren_depth)]
+              | char_(')', CLOSE_PAREN)[--ref(paren_depth), if_(ref(paren_depth) == 0)[_state = "OVR"]]
+              | char_('\\', TAG_START)[_state = "TAGSTART"]
+              | char_(',', ARG_SEP)
+              | string("\\s+", WHITESPACE)
+              | string(".", ARG)
+              | kara_templater
+              ;
+
+        this->self("TAGSTART")
+            = string("\\s+", WHITESPACE)
+              | string("r|fn", TAG_NAME)[_state = "ARG"]
+              | char_('\\', TAG_START)
+              | char_('}', OVR_END)[_state = "INITIAL"]
+              | string("[a-z0-9]", TAG_NAME)[_state = "TAGNAME"]
+              | string(".", COMMENT)[_state = "OVR"]
+              | kara_templater
+              ;
+
+        this->self("TAGNAME")
+            = string("[a-z]+", TAG_NAME)[_state = "ARG"]
+              | char_('(', OPEN_PAREN)[++ref(paren_depth), _state = "ARG"]
+              | char_(')', CLOSE_PAREN)[--ref(paren_depth), if_(ref(paren_depth) == 0)[_state = "OVR"]]
+              | char_('}', OVR_END)[_state = "INITIAL"]
+              | char_('\\', TAG_START)[_state = "TAGSTART"]
+              | string(".", ARG)[_state = "ARG"]
+              | kara_templater
+              ;
+    }
+
+    dialogue_tokens(bool karaoke_templater) : paren_depth(0) {
+        using lex::string;
+        using namespace agi::ass::DialogueTokenType;
+
+        if (karaoke_templater)
+            init(string("![^!]*!", KARAOKE_TEMPLATE) | string("\\$[A-Za-z_]+", KARAOKE_VARIABLE));
+        else
+            init(lex::char_('\1'));
+    }
 };
 
 template<typename Parser, typename T>
-bool do_try_parse(std::string const& str, Parser parser, T *out) {
-	using namespace boost::spirit::qi;
-	T res;
-	char const* cstr = str.c_str();
-
-	bool parsed = parse(cstr, cstr + str.size(), parser, res);
-	if (parsed && cstr == &str[str.size()]) {
-		*out = res;
-		return true;
-	}
-
-	return false;
+bool do_try_parse(std::string const &str, Parser parser, T *out)
+{
+    using namespace boost::spirit::qi;
+    T res;
+    char const *cstr = str.c_str();
+
+    bool parsed = parse(cstr, cstr + str.size(), parser, res);
+    if (parsed && cstr == &str[str.size()]) {
+        *out = res;
+        return true;
+    }
+
+    return false;
 }
 
 }
 
 namespace agi {
 namespace parser {
-	bool parse(Color &dst, std::string const& str) {
-		std::string::const_iterator begin = str.begin();
-		bool parsed = parse(begin, str.end(), color_grammar<std::string::const_iterator>(), dst);
-		return parsed && begin == str.end();
-	}
+bool parse(Color &dst, std::string const &str)
+{
+    std::string::const_iterator begin = str.begin();
+    bool parsed = parse(begin, str.end(), color_grammar<std::string::const_iterator>(), dst);
+    return parsed && begin == str.end();
+}
 }
 
 namespace ass {
-	std::vector<DialogueToken> TokenizeDialogueBody(std::string const& str, bool karaoke_templater) {
-		static const dialogue_tokens<lex::lexertl::actor_lexer<>> kt(true);
-		static const dialogue_tokens<lex::lexertl::actor_lexer<>> not_kt(false);
-		auto const& tokenizer = karaoke_templater ? kt : not_kt;
-
-		char const *first = str.c_str();
-		char const *last = first + str.size();
-		std::vector<DialogueToken> data;
-		auto it = tokenizer.begin(first, last), end = tokenizer.end();
-
-		for (; it != end && token_is_valid(*it); ++it) {
-			int id = it->id();
-			ptrdiff_t len = it->value().end() - it->value().begin();
-			assert(len > 0);
-			if (data.empty() || data.back().type != id)
-				data.push_back(DialogueToken{id, static_cast<size_t>(len)});
-			else
-				data.back().length += len;
-		}
-
-		return data;
-	}
+std::vector<DialogueToken> TokenizeDialogueBody(std::string const &str, bool karaoke_templater)
+{
+    static const dialogue_tokens<lex::lexertl::actor_lexer<>> kt(true);
+    static const dialogue_tokens<lex::lexertl::actor_lexer<>> not_kt(false);
+    auto const &tokenizer = karaoke_templater ? kt : not_kt;
+
+    char const *first = str.c_str();
+    char const *last = first + str.size();
+    std::vector<DialogueToken> data;
+    auto it = tokenizer.begin(first, last), end = tokenizer.end();
+
+    for (; it != end && token_is_valid(*it); ++it) {
+        int id = it->id();
+        ptrdiff_t len = it->value().end() - it->value().begin();
+        assert(len > 0);
+        if (data.empty() || data.back().type != id)
+            data.push_back(DialogueToken{id, static_cast<size_t>(len)});
+        else
+            data.back().length += len;
+    }
+
+    return data;
+}
 }
 
 namespace util {
-	// from util.h
-	bool try_parse(std::string const& str, double *out) {
-		return do_try_parse(str, boost::spirit::qi::double_, out);
-	}
-
-	bool try_parse(std::string const& str, int *out) {
-		return do_try_parse(str, boost::spirit::qi::int_, out);
-	}
+// from util.h
+bool try_parse(std::string const &str, double *out)
+{
+    return do_try_parse(str, boost::spirit::qi::double_, out);
+}
+
+bool try_parse(std::string const &str, int *out)
+{
+    return do_try_parse(str, boost::spirit::qi::int_, out);
+}
 }
 }
diff --git a/libaegisub/common/parser.h b/libaegisub/common/parser.h
index d83ffb0bd639c8f59e7d21b4f4b975b7e8d7bb13..18cc027ce477040e0d94cf9fc84f189e26b4ee3f 100644
--- a/libaegisub/common/parser.h
+++ b/libaegisub/common/parser.h
@@ -15,25 +15,25 @@
 #include <string>
 
 namespace agi {
-	struct Color;
+struct Color;
 
-	namespace parser {
-		/// Try to parse a string as a color
-		/// @param[out] dst Color struct to populate with the parsed result
-		/// @param str String to parse
-		/// @return Was the string successfully parsed as a color?
-		///
-		/// If this function returns false, the contents of dst is undefined. It
-		/// may contain partially-parsed garbage.
-		///
-		/// This function supports the following formats:
-		///  * SSA colors (i.e. a decimal number representing a bgr value)
-		///  * ASS override and style formats (a (a)bgr hex number possibly with
-		///    some ampersands and an H somewhere)
-		///  * CSS-style #rrggbb and #rgb
-		///  * CSS-style rgb(r,g,b)
-		///
-		/// CSS's rgb(r%,g%,b%) format is not currently supported.
-		bool parse(Color &dst, std::string const& str);
-	}
+namespace parser {
+/// Try to parse a string as a color
+/// @param[out] dst Color struct to populate with the parsed result
+/// @param str String to parse
+/// @return Was the string successfully parsed as a color?
+///
+/// If this function returns false, the contents of dst is undefined. It
+/// may contain partially-parsed garbage.
+///
+/// This function supports the following formats:
+///  * SSA colors (i.e. a decimal number representing a bgr value)
+///  * ASS override and style formats (a (a)bgr hex number possibly with
+///    some ampersands and an H somewhere)
+///  * CSS-style #rrggbb and #rgb
+///  * CSS-style rgb(r,g,b)
+///
+/// CSS's rgb(r%,g%,b%) format is not currently supported.
+bool parse(Color &dst, std::string const &str);
+}
 }
diff --git a/libaegisub/common/path.cpp b/libaegisub/common/path.cpp
index cca7302cc5781b8700aa82ac5299f40c9448509c..57d90cc42ae448672c2ad6a5f60ec10de534f104 100644
--- a/libaegisub/common/path.cpp
+++ b/libaegisub/common/path.cpp
@@ -23,125 +23,133 @@
 
 namespace {
 static const char *tokens[] = {
-	"?audio",
-	"?data",
-	"?dictionary",
-	"?local",
-	"?script",
-	"?temp",
-	"?user",
-	"?video"
+    "?audio",
+    "?data",
+    "?dictionary",
+    "?local",
+    "?script",
+    "?temp",
+    "?user",
+    "?video"
 };
 
-int find_token(const char *str, size_t len) {
-	if (len < 5 || str[0] != '?') return -1;
-	int idx;
-	switch (str[1] + str[4]) {
-	case 'a' + 'i': idx = 0; break;
-	case 'd' + 'a': idx = 1; break;
-	case 'd' + 't': idx = 2; break;
-	case 'l' + 'a': idx = 3; break;
-	case 's' + 'i': idx = 4; break;
-	case 't' + 'p': idx = 5; break;
-	case 'u' + 'r': idx = 6; break;
-	case 'v' + 'e': idx = 7; break;
-	default: return -1;
-	}
-
-	return strncmp(str, tokens[idx], strlen(tokens[idx])) == 0 ? idx : -1;
+int find_token(const char *str, size_t len)
+{
+    if (len < 5 || str[0] != '?') return -1;
+    int idx;
+    switch (str[1] + str[4]) {
+    case 'a' + 'i': idx = 0; break;
+    case 'd' + 'a': idx = 1; break;
+    case 'd' + 't': idx = 2; break;
+    case 'l' + 'a': idx = 3; break;
+    case 's' + 'i': idx = 4; break;
+    case 't' + 'p': idx = 5; break;
+    case 'u' + 'r': idx = 6; break;
+    case 'v' + 'e': idx = 7; break;
+    default: return -1;
+    }
+
+    return strncmp(str, tokens[idx], strlen(tokens[idx])) == 0 ? idx : -1;
 }
 }
 
 namespace agi {
 
-Path::Path() {
-	static_assert(sizeof(paths) / sizeof(paths[0]) == sizeof(tokens) / sizeof(tokens[0]),
-		"Token and path arrays need to be the same size");
-	FillPlatformSpecificPaths();
+Path::Path()
+{
+    static_assert(sizeof(paths) / sizeof(paths[0]) == sizeof(tokens) / sizeof(tokens[0]),
+                  "Token and path arrays need to be the same size");
+    FillPlatformSpecificPaths();
 }
 
-fs::path Path::Decode(std::string const& path) const {
-	int idx = find_token(path.c_str(), path.size());
-	if (idx == -1 || paths[idx].empty())
-		return fs::path(path).make_preferred();
-	return (paths[idx]/path.substr(strlen(tokens[idx]))).make_preferred();
+fs::path Path::Decode(std::string const &path) const
+{
+    int idx = find_token(path.c_str(), path.size());
+    if (idx == -1 || paths[idx].empty())
+        return fs::path(path).make_preferred();
+    return (paths[idx] / path.substr(strlen(tokens[idx]))).make_preferred();
 }
 
-fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const {
-	int idx = find_token(token.c_str(), token.size());
-	if (idx == -1) throw agi::InternalError("Bad token: " + token);
+fs::path Path::MakeRelative(fs::path const &path, std::string const &token) const
+{
+    int idx = find_token(token.c_str(), token.size());
+    if (idx == -1) throw agi::InternalError("Bad token: " + token);
 
-	return MakeRelative(path, paths[idx]);
+    return MakeRelative(path, paths[idx]);
 }
 
-fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const {
-	if (path.empty() || base.empty()) return path;
+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;
+    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();
+    // 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) ;
+    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;
+    agi::fs::path result;
+    for (; ref_it != base.end(); ++ref_it)
+        result /= "..";
+    for (; path_it != path.end(); ++path_it)
+        result /= *path_it;
 
-	return result;
+    return result;
 }
 
-fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const {
-	if (path.empty()) return path;
-	int idx = find_token(token.c_str(), token.size());
-	if (idx == -1) throw agi::InternalError("Bad token: " + token);
-
-	path.make_preferred();
-	const auto str = path.string();
-	if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
-		return path;
-	return (paths[idx].empty() || path.is_absolute()) ? path : paths[idx]/path;
+fs::path Path::MakeAbsolute(fs::path path, std::string const &token) const
+{
+    if (path.empty()) return path;
+    int idx = find_token(token.c_str(), token.size());
+    if (idx == -1) throw agi::InternalError("Bad token: " + token);
+
+    path.make_preferred();
+    const auto str = path.string();
+    if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
+        return path;
+    return (paths[idx].empty() || path.is_absolute()) ? path : paths[idx] / 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 (size_t i = 0; i < paths.size(); ++i) {
-		if (paths[i].empty()) continue;
-
-		const auto p = MakeRelative(path, tokens[i]);
-		const size_t d = boost::distance(p);
-		if (d < length) {
-			length = d;
-			shortest = (tokens[i]/p).string();
-		}
-	}
-
-	return shortest;
+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 (size_t i = 0; i < paths.size(); ++i) {
+        if (paths[i].empty()) continue;
+
+        const auto p = MakeRelative(path, tokens[i]);
+        const size_t d = boost::distance(p);
+        if (d < length) {
+            length = d;
+            shortest = (tokens[i] / p).string();
+        }
+    }
+
+    return shortest;
 }
 
-void Path::SetToken(const char *token_name, fs::path const& token_value) {
-	int idx = find_token(token_name, strlen(token_name));
-	if (idx == -1) throw agi::InternalError("Bad token: " + std::string(token_name));
-
-	if (token_value.empty())
-		paths[idx] = token_value;
-	else if (!token_value.is_absolute())
-		paths[idx].clear();
-	else {
-		paths[idx] = token_value;
-		paths[idx].make_preferred();
-		if (fs::FileExists(paths[idx]))
-			paths[idx] = paths[idx].parent_path();
-	}
+void Path::SetToken(const char *token_name, fs::path const &token_value)
+{
+    int idx = find_token(token_name, strlen(token_name));
+    if (idx == -1) throw agi::InternalError("Bad token: " + std::string(token_name));
+
+    if (token_value.empty())
+        paths[idx] = token_value;
+    else if (!token_value.is_absolute())
+        paths[idx].clear();
+    else {
+        paths[idx] = token_value;
+        paths[idx].make_preferred();
+        if (fs::FileExists(paths[idx]))
+            paths[idx] = paths[idx].parent_path();
+    }
 }
 
 }
diff --git a/libaegisub/common/thesaurus.cpp b/libaegisub/common/thesaurus.cpp
index 9d914be773596b913c05d34e7621cb8be5dc1c45..61037b40ab75b11aef202a1f9e76ed28f59acbdd 100644
--- a/libaegisub/common/thesaurus.cpp
+++ b/libaegisub/common/thesaurus.cpp
@@ -28,82 +28,83 @@
 
 namespace agi {
 
-Thesaurus::Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path)
-: dat(make_unique<read_file_mapping>(dat_path))
+Thesaurus::Thesaurus(agi::fs::path const &dat_path, agi::fs::path const &idx_path)
+    : dat(make_unique<read_file_mapping>(dat_path))
 {
-	read_file_mapping idx_file(idx_path);
-	boost::interprocess::ibufferstream idx(idx_file.read(), static_cast<size_t>(idx_file.size()));
-
-	std::string encoding_name;
-	getline(idx, encoding_name);
-	std::string unused_entry_count;
-	getline(idx, unused_entry_count);
-
-	conv = make_unique<charset::IconvWrapper>(encoding_name.c_str(), "utf-8");
-
-	// Read the list of words and file offsets for those words
-	for (auto const& line : line_iterator<std::string>(idx, encoding_name)) {
-		auto pos = line.find('|');
-		if (pos != line.npos && line.find('|', pos + 1) == line.npos)
-			offsets[line.substr(0, pos)] = static_cast<size_t>(atoi(line.c_str() + pos + 1));
-	}
+    read_file_mapping idx_file(idx_path);
+    boost::interprocess::ibufferstream idx(idx_file.read(), static_cast<size_t>(idx_file.size()));
+
+    std::string encoding_name;
+    getline(idx, encoding_name);
+    std::string unused_entry_count;
+    getline(idx, unused_entry_count);
+
+    conv = make_unique<charset::IconvWrapper>(encoding_name.c_str(), "utf-8");
+
+    // Read the list of words and file offsets for those words
+    for (auto const &line : line_iterator<std::string>(idx, encoding_name)) {
+        auto pos = line.find('|');
+        if (pos != line.npos && line.find('|', pos + 1) == line.npos)
+            offsets[line.substr(0, pos)] = static_cast<size_t>(atoi(line.c_str() + pos + 1));
+    }
 }
 
 Thesaurus::~Thesaurus() { }
 
-std::vector<Thesaurus::Entry> Thesaurus::Lookup(std::string const& word) {
-	std::vector<Entry> out;
-	if (!dat) return out;
-
-	auto it = offsets.find(word);
-	if (it == offsets.end()) return out;
-	if (it->second >= dat->size()) return out;
-
-	auto len = dat->size() - it->second;
-	auto buff = dat->read(it->second, len);
-	auto buff_end = buff + len;
-
-	std::string temp;
-	auto read_line = [&] (std::string& temp) -> std::string * {
-		auto start = buff;
-		auto end = std::find(buff, buff_end, '\n');
-		buff = end < buff_end ? end + 1 : buff_end;
-		if (end > start && end[-1] == '\r') --end;
-		temp.clear();
-		conv->Convert(start, end - start, temp);
-		return &temp;
-	};
-
-	// First line is the word and meaning count
-	std::vector<std::string> header;
-	agi::Split(header, *read_line(temp), '|');
-	if (header.size() != 2) return out;
-	int meanings = atoi(header[1].c_str());
-
-	out.reserve(meanings);
-	std::vector<std::string> line;
-	for (int i = 0; i < meanings; ++i) {
-		agi::Split(line, *read_line(temp), '|');
-		if (line.size() < 2)
-			continue;
-
-		Entry e;
-		// The "definition" is just the part of speech (which may be empty)
-		// plus the word it's giving synonyms for (which may not be the passed word)
-		if (!line[0].empty())
-			e.first = line[0] + ' ';
-		e.first += line[1];
-		e.second.reserve(line.size() - 2);
-
-		for (size_t i = 2; i < line.size(); ++i) {
-			if (line[i].size())
-				e.second.emplace_back(std::move(line[i]));
-		}
-
-		out.emplace_back(std::move(e));
-	}
-
-	return out;
+std::vector<Thesaurus::Entry> Thesaurus::Lookup(std::string const &word)
+{
+    std::vector<Entry> out;
+    if (!dat) return out;
+
+    auto it = offsets.find(word);
+    if (it == offsets.end()) return out;
+    if (it->second >= dat->size()) return out;
+
+    auto len = dat->size() - it->second;
+    auto buff = dat->read(it->second, len);
+    auto buff_end = buff + len;
+
+    std::string temp;
+    auto read_line = [&] (std::string & temp) -> std::string * {
+        auto start = buff;
+        auto end = std::find(buff, buff_end, '\n');
+        buff = end < buff_end ? end + 1 : buff_end;
+        if (end > start && end[-1] == '\r') --end;
+        temp.clear();
+        conv->Convert(start, end - start, temp);
+        return &temp;
+    };
+
+    // First line is the word and meaning count
+    std::vector<std::string> header;
+    agi::Split(header, *read_line(temp), '|');
+    if (header.size() != 2) return out;
+    int meanings = atoi(header[1].c_str());
+
+    out.reserve(meanings);
+    std::vector<std::string> line;
+    for (int i = 0; i < meanings; ++i) {
+        agi::Split(line, *read_line(temp), '|');
+        if (line.size() < 2)
+            continue;
+
+        Entry e;
+        // The "definition" is just the part of speech (which may be empty)
+        // plus the word it's giving synonyms for (which may not be the passed word)
+        if (!line[0].empty())
+            e.first = line[0] + ' ';
+        e.first += line[1];
+        e.second.reserve(line.size() - 2);
+
+        for (size_t i = 2; i < line.size(); ++i) {
+            if (line[i].size())
+                e.second.emplace_back(std::move(line[i]));
+        }
+
+        out.emplace_back(std::move(e));
+    }
+
+    return out;
 }
 
 }
diff --git a/libaegisub/common/util.cpp b/libaegisub/common/util.cpp
index e92d1477640052dc90ecfece3ec38873ad379439..ab4598c4b8b9535a77e2f2067924528d097700bf 100644
--- a/libaegisub/common/util.cpp
+++ b/libaegisub/common/util.cpp
@@ -23,175 +23,183 @@
 #include <ctime>
 
 namespace {
-const size_t bad_pos = (size_t)-1;
+const size_t bad_pos = (size_t) -1;
 const std::pair<size_t, size_t> bad_match(bad_pos, bad_pos);
 
 template<typename Iterator>
-size_t advance_both(Iterator& folded, Iterator& raw) {
-	size_t len;
-	if (*folded == *raw) {
-		len = folded->length();
-		++folded;
-	}
-	else {
-		// This character was changed by case folding, so refold it and eat the
-		// appropriate number of characters from folded
-		len = boost::locale::fold_case(raw->str()).size();
-		for (size_t folded_consumed = 0; folded_consumed < len; ++folded)
-			folded_consumed += folded->length();
-	}
-
-	++raw;
-	return len;
+size_t advance_both(Iterator &folded, Iterator &raw)
+{
+    size_t len;
+    if (*folded == *raw) {
+        len = folded->length();
+        ++folded;
+    }
+    else {
+        // This character was changed by case folding, so refold it and eat the
+        // appropriate number of characters from folded
+        len = boost::locale::fold_case(raw->str()).size();
+        for (size_t folded_consumed = 0; folded_consumed < len; ++folded)
+            folded_consumed += folded->length();
+    }
+
+    ++raw;
+    return len;
 }
 
-std::pair<size_t, size_t> find_range(std::string const& haystack, std::string const& needle, size_t start = 0) {
-	const size_t match_start = haystack.find(needle, start);
-	if (match_start == std::string::npos)
-		return bad_match;
-	return {match_start, match_start + needle.size()};
+std::pair<size_t, size_t> find_range(std::string const &haystack, std::string const &needle, size_t start = 0)
+{
+    const size_t match_start = haystack.find(needle, start);
+    if (match_start == std::string::npos)
+        return bad_match;
+    return {match_start, match_start + needle.size()};
 }
 
-void parse_blocks(std::vector<std::pair<size_t, size_t>>& blocks, std::string const& str) {
-	blocks.clear();
-
-	size_t ovr_start = bad_pos;
-	size_t i = 0;
-	for (auto const& c : str) {
-		if (c == '{' && ovr_start == bad_pos)
-			ovr_start = i;
-		else if (c == '}' && ovr_start != bad_pos) {
-			blocks.emplace_back(ovr_start, i + 1);
-			ovr_start = bad_pos;
-		}
-		++i;
-	}
+void parse_blocks(std::vector<std::pair<size_t, size_t>> &blocks, std::string const &str)
+{
+    blocks.clear();
+
+    size_t ovr_start = bad_pos;
+    size_t i = 0;
+    for (auto const &c : str) {
+        if (c == '{' && ovr_start == bad_pos)
+            ovr_start = i;
+        else if (c == '}' && ovr_start != bad_pos) {
+            blocks.emplace_back(ovr_start, i + 1);
+            ovr_start = bad_pos;
+        }
+        ++i;
+    }
 }
 
 } // anonymous namespace
 
-namespace agi { namespace util {
+namespace agi {
+namespace util {
 
-std::string strftime(const char *fmt, const tm *tmptr) {
-	if (!tmptr) {
-		time_t t = time(nullptr);
-		tmptr = localtime(&t);
-	}
+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;
+    char buff[65536];
+    ::strftime(buff, sizeof buff, fmt, tmptr);
+    return buff;
 }
 
-std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const& needle) {
-	const auto folded_hs = boost::locale::fold_case(haystack);
-	const auto folded_n = boost::locale::fold_case(needle);
-	auto match = find_range(folded_hs, folded_n);
-	if (match == bad_match || folded_hs == haystack)
-		return match;
-
-	// We have a match, but the position is an index into the folded string
-	// and we want an index into the unfolded string.
-
-	using namespace boost::locale::boundary;
-	const ssegment_index haystack_characters(character, begin(haystack), end(haystack));
-	const ssegment_index folded_characters(character, begin(folded_hs), end(folded_hs));
-	const size_t haystack_char_count = boost::distance(haystack_characters);
-	const size_t folded_char_count = boost::distance(folded_characters);
-
-	// As of Unicode 6.2, case folding can never reduce the number of
-	// characters, and can only reduce the number of bytes with UTF-8 when
-	// increasing the number of characters. As a result, iff the bytes and
-	// characters are unchanged, no folds changed the size of any characters
-	// and our indices are correct.
-	if (haystack.size() == folded_hs.size() && haystack_char_count == folded_char_count)
-		return match;
-
-	const auto map_folded_to_raw = [&]() -> std::pair<size_t, size_t> {
-		size_t start = -1;
-
-		// Iterate over each pair of characters and refold each character which was
-		// changed by folding, so that we can find the corresponding positions in
-		// the unfolded string
-		auto folded_it = begin(folded_characters);
-		auto haystack_it = begin(haystack_characters);
-		size_t folded_pos = 0;
-
-		while (folded_pos < match.first)
-			folded_pos += advance_both(folded_it, haystack_it);
-		// If we overshot the start then the match started in the middle of a
-		// character which was folded to multiple characters
-		if (folded_pos > match.first)
-			return bad_match;
-
-		start = distance(begin(haystack), begin(*haystack_it));
-
-		while (folded_pos < match.second)
-			folded_pos += advance_both(folded_it, haystack_it);
-		if (folded_pos > match.second)
-			return bad_match;
-
-		return {start, distance(begin(haystack), begin(*haystack_it))};
-	};
-
-	auto ret = map_folded_to_raw();
-	while (ret == bad_match) {
-		// Found something, but it was an invalid match so retry from the next character
-		match = find_range(folded_hs, folded_n, match.first + 1);
-		if (match == bad_match) return match;
-		ret = map_folded_to_raw();
-	}
-
-	return ret;
+std::pair<size_t, size_t> ifind(std::string const &haystack, std::string const &needle)
+{
+    const auto folded_hs = boost::locale::fold_case(haystack);
+    const auto folded_n = boost::locale::fold_case(needle);
+    auto match = find_range(folded_hs, folded_n);
+    if (match == bad_match || folded_hs == haystack)
+        return match;
+
+    // We have a match, but the position is an index into the folded string
+    // and we want an index into the unfolded string.
+
+    using namespace boost::locale::boundary;
+    const ssegment_index haystack_characters(character, begin(haystack), end(haystack));
+    const ssegment_index folded_characters(character, begin(folded_hs), end(folded_hs));
+    const size_t haystack_char_count = boost::distance(haystack_characters);
+    const size_t folded_char_count = boost::distance(folded_characters);
+
+    // As of Unicode 6.2, case folding can never reduce the number of
+    // characters, and can only reduce the number of bytes with UTF-8 when
+    // increasing the number of characters. As a result, iff the bytes and
+    // characters are unchanged, no folds changed the size of any characters
+    // and our indices are correct.
+    if (haystack.size() == folded_hs.size() && haystack_char_count == folded_char_count)
+        return match;
+
+    const auto map_folded_to_raw = [&]() -> std::pair<size_t, size_t> {
+        size_t start = -1;
+
+        // Iterate over each pair of characters and refold each character which was
+        // changed by folding, so that we can find the corresponding positions in
+        // the unfolded string
+        auto folded_it = begin(folded_characters);
+        auto haystack_it = begin(haystack_characters);
+        size_t folded_pos = 0;
+
+        while (folded_pos < match.first)
+            folded_pos += advance_both(folded_it, haystack_it);
+        // If we overshot the start then the match started in the middle of a
+        // character which was folded to multiple characters
+        if (folded_pos > match.first)
+            return bad_match;
+
+        start = distance(begin(haystack), begin(*haystack_it));
+
+        while (folded_pos < match.second)
+            folded_pos += advance_both(folded_it, haystack_it);
+        if (folded_pos > match.second)
+            return bad_match;
+
+        return {start, distance(begin(haystack), begin(*haystack_it))};
+    };
+
+    auto ret = map_folded_to_raw();
+    while (ret == bad_match) {
+        // Found something, but it was an invalid match so retry from the next character
+        match = find_range(folded_hs, folded_n, match.first + 1);
+        if (match == bad_match) return match;
+        ret = map_folded_to_raw();
+    }
+
+    return ret;
 }
 
-std::string tagless_find_helper::strip_tags(std::string const& str, size_t s) {
-	parse_blocks(blocks, str);
+std::string tagless_find_helper::strip_tags(std::string const &str, size_t s)
+{
+    parse_blocks(blocks, str);
 
-	std::string out;
+    std::string out;
 
-	size_t last = s;
-	for (auto const& block : blocks) {
-		if (block.second <= s) continue;
-		if (block.first > last)
-			out.append(str.begin() + last, str.begin() + block.first);
-		last = block.second;
-	}
+    size_t last = s;
+    for (auto const &block : blocks) {
+        if (block.second <= s) continue;
+        if (block.first > last)
+            out.append(str.begin() + last, str.begin() + block.first);
+        last = block.second;
+    }
 
-	if (last < str.size())
-		out.append(str.begin() + last, str.end());
+    if (last < str.size())
+        out.append(str.begin() + last, str.end());
 
-	start = s;
-	return out;
+    start = s;
+    return out;
 }
 
-void tagless_find_helper::map_range(size_t &s, size_t &e) {
-	s += start;
-	e += start;
-
-	// Shift the start and end of the match to be relative to the unstripped
-	// match
-	for (auto const& block : blocks) {
-		// Any blocks before start are irrelevant as they're included in `start`
-		if (block.second < s) continue;
-		// Skip over blocks at the very beginning of the match
-		// < should only happen if the cursor was within an override block
-		// when the user started a search
-		if (block.first <= s) {
-			size_t len = block.second - std::max(block.first, start);
-			s += len;
-			e += len;
-			continue;
-		}
-
-		assert(block.first > s);
-		// Blocks after the match are irrelevant
-		if (block.first >= e) break;
-
-		// Extend the match to include blocks within the match
-		// Note that blocks cannot be partially within the match
-		e += block.second - block.first;
-	}
+void tagless_find_helper::map_range(size_t &s, size_t &e)
+{
+    s += start;
+    e += start;
+
+    // Shift the start and end of the match to be relative to the unstripped
+    // match
+    for (auto const &block : blocks) {
+        // Any blocks before start are irrelevant as they're included in `start`
+        if (block.second < s) continue;
+        // Skip over blocks at the very beginning of the match
+        // < should only happen if the cursor was within an override block
+        // when the user started a search
+        if (block.first <= s) {
+            size_t len = block.second - std::max(block.first, start);
+            s += len;
+            e += len;
+            continue;
+        }
+
+        assert(block.first > s);
+        // Blocks after the match are irrelevant
+        if (block.first >= e) break;
+
+        // Extend the match to include blocks within the match
+        // Note that blocks cannot be partially within the match
+        e += block.second - block.first;
+    }
 }
 } // namespace util
 
diff --git a/libaegisub/common/vfr.cpp b/libaegisub/common/vfr.cpp
index c82fcdbeedf659e29026ace3d644a7a1cbc96989..82c192de82507e514cbf431e4cdbc6162a22772a 100644
--- a/libaegisub/common/vfr.cpp
+++ b/libaegisub/common/vfr.cpp
@@ -36,53 +36,56 @@ using namespace agi::vfr;
 
 /// @brief Verify that timecodes monotonically increase
 /// @param timecodes List of timecodes to check
-void validate_timecodes(std::vector<int> const& timecodes) {
-	if (timecodes.size() <= 1)
-		throw InvalidFramerate("Must have at least two timecodes to do anything useful");
-	if (!is_sorted(timecodes.begin(), timecodes.end()))
-		throw InvalidFramerate("Timecodes are out of order");
-	if (timecodes.front() == timecodes.back())
-		throw InvalidFramerate("Timecodes are all identical");
+void validate_timecodes(std::vector<int> const &timecodes)
+{
+    if (timecodes.size() <= 1)
+        throw InvalidFramerate("Must have at least two timecodes to do anything useful");
+    if (!is_sorted(timecodes.begin(), timecodes.end()))
+        throw InvalidFramerate("Timecodes are out of order");
+    if (timecodes.front() == timecodes.back())
+        throw InvalidFramerate("Timecodes are all identical");
 }
 
 /// @brief Shift timecodes so that frame 0 starts at time 0
 /// @param timecodes List of timecodes to normalize
-void normalize_timecodes(std::vector<int> &timecodes) {
-	if (int front = timecodes.front())
-		boost::for_each(timecodes, [=](int &tc) { tc -= front; });
+void normalize_timecodes(std::vector<int> &timecodes)
+{
+    if (int front = timecodes.front())
+        boost::for_each(timecodes, [ = ](int &tc) { tc -= front; });
 }
 
 // A "start,end,fps" line in a v1 timecode file
 struct TimecodeRange {
-	int start;
-	int end;
-	double fps;
-	bool operator<(TimecodeRange const& cmp) const { return start < cmp.start; }
+    int start;
+    int end;
+    double fps;
+    bool operator<(TimecodeRange const &cmp) const { return start < cmp.start; }
 };
 
 /// @brief Parse a single line of a v1 timecode file
 /// @param str Line to parse
 /// @return The line in TimecodeRange form, or TimecodeRange() if it's a comment
-TimecodeRange v1_parse_line(std::string const& str) {
-	if (str.empty() || str[0] == '#') return TimecodeRange();
-
-	boost::interprocess::ibufferstream ss(str.data(), str.size());
-	TimecodeRange range;
-	char comma1 = 0, comma2 = 0;
-	ss >> range.start >> comma1 >> range.end >> comma2 >> range.fps;
-	if (ss.fail() || comma1 != ',' || comma2 != ',' || !ss.eof())
-		throw MalformedLine(str);
-	if (range.start < 0 || range.end < 0)
-		throw InvalidFramerate("Cannot specify frame rate for negative frames.");
-	if (range.end < range.start)
-		throw InvalidFramerate("End frame must be greater than or equal to start frame");
-	if (range.fps <= 0.)
-		throw InvalidFramerate("FPS must be greater than zero");
-	if (range.fps > 1000.)
-		// This is our limitation, not mkvmerge's
-		// mkvmerge uses nanoseconds internally
-		throw InvalidFramerate("FPS must be at most 1000");
-	return range;
+TimecodeRange v1_parse_line(std::string const &str)
+{
+    if (str.empty() || str[0] == '#') return TimecodeRange();
+
+    boost::interprocess::ibufferstream ss(str.data(), str.size());
+    TimecodeRange range;
+    char comma1 = 0, comma2 = 0;
+    ss >> range.start >> comma1 >> range.end >> comma2 >> range.fps;
+    if (ss.fail() || comma1 != ',' || comma2 != ',' || !ss.eof())
+        throw MalformedLine(str);
+    if (range.start < 0 || range.end < 0)
+        throw InvalidFramerate("Cannot specify frame rate for negative frames.");
+    if (range.end < range.start)
+        throw InvalidFramerate("End frame must be greater than or equal to start frame");
+    if (range.fps <= 0.)
+        throw InvalidFramerate("FPS must be greater than zero");
+    if (range.fps > 1000.)
+        // This is our limitation, not mkvmerge's
+        // mkvmerge uses nanoseconds internally
+        throw InvalidFramerate("FPS must be at most 1000");
+    return range;
 }
 
 /// @brief Parse a v1 timecode file
@@ -91,234 +94,245 @@ TimecodeRange v1_parse_line(std::string const& str) {
 /// @param[out] timecodes Vector filled with frame start times
 /// @param[out] last      Unrounded time of the last frame
 /// @return Assumed fps times one million
-int64_t v1_parse(line_iterator<std::string> file, std::string line, std::vector<int> &timecodes, int64_t &last) {
-	double fps = atof(line.substr(7).c_str());
-	if (fps <= 0.) throw InvalidFramerate("Assumed FPS must be greater than zero");
-	if (fps > 1000.) throw InvalidFramerate("Assumed FPS must not be greater than 1000");
-
-	std::vector<TimecodeRange> ranges;
-	for (auto const& line : file) {
-		auto range = v1_parse_line(line);
-		if (range.fps != 0)
-			ranges.push_back(range);
-	}
-
-	std::sort(begin(ranges), end(ranges));
-
-	if (!ranges.empty())
-		timecodes.reserve(ranges.back().end + 2);
-	double time = 0.;
-	int frame = 0;
-	for (auto const& range : ranges) {
-		if (frame > range.start) {
-			// mkvmerge allows overlapping timecode ranges, but does completely
-			// broken things with them
-			throw InvalidFramerate("Override ranges must not overlap");
-		}
-		for (; frame < range.start; ++frame) {
-			timecodes.push_back(int(time + .5));
-			time += 1000. / fps;
-		}
-		for (; frame <= range.end; ++frame) {
-			timecodes.push_back(int(time + .5));
-			time += 1000. / range.fps;
-		}
-	}
-	timecodes.push_back(int(time + .5));
-	last = int64_t(time * fps * default_denominator);
-	return int64_t(fps * default_denominator);
+int64_t v1_parse(line_iterator<std::string> file, std::string line, std::vector<int> &timecodes, int64_t &last)
+{
+    double fps = atof(line.substr(7).c_str());
+    if (fps <= 0.) throw InvalidFramerate("Assumed FPS must be greater than zero");
+    if (fps > 1000.) throw InvalidFramerate("Assumed FPS must not be greater than 1000");
+
+    std::vector<TimecodeRange> ranges;
+    for (auto const &line : file) {
+        auto range = v1_parse_line(line);
+        if (range.fps != 0)
+            ranges.push_back(range);
+    }
+
+    std::sort(begin(ranges), end(ranges));
+
+    if (!ranges.empty())
+        timecodes.reserve(ranges.back().end + 2);
+    double time = 0.;
+    int frame = 0;
+    for (auto const &range : ranges) {
+        if (frame > range.start) {
+            // mkvmerge allows overlapping timecode ranges, but does completely
+            // broken things with them
+            throw InvalidFramerate("Override ranges must not overlap");
+        }
+        for (; frame < range.start; ++frame) {
+            timecodes.push_back(int(time + .5));
+            time += 1000. / fps;
+        }
+        for (; frame <= range.end; ++frame) {
+            timecodes.push_back(int(time + .5));
+            time += 1000. / range.fps;
+        }
+    }
+    timecodes.push_back(int(time + .5));
+    last = int64_t(time * fps * default_denominator);
+    return int64_t(fps * default_denominator);
 }
 }
 
-namespace agi { namespace vfr {
+namespace agi {
+namespace vfr {
 Framerate::Framerate(double fps)
-: denominator(default_denominator)
-, numerator(int64_t(fps * denominator))
+    : denominator(default_denominator)
+    , numerator(int64_t(fps * denominator))
 {
-	if (fps < 0.) throw InvalidFramerate("FPS must be greater than zero");
-	if (fps > 1000.) throw InvalidFramerate("FPS must not be greater than 1000");
-	timecodes.push_back(0);
+    if (fps < 0.) throw InvalidFramerate("FPS must be greater than zero");
+    if (fps > 1000.) throw InvalidFramerate("FPS must not be greater than 1000");
+    timecodes.push_back(0);
 }
 
 Framerate::Framerate(int64_t numerator, int64_t denominator, bool drop)
-: denominator(denominator)
-, numerator(numerator)
-, drop(drop && numerator % denominator != 0)
+    : denominator(denominator)
+    , numerator(numerator)
+    , drop(drop && numerator % denominator != 0)
 {
-	if (numerator <= 0 || denominator <= 0)
-		throw InvalidFramerate("Numerator and denominator must both be greater than zero");
-	if (numerator / denominator > 1000) throw InvalidFramerate("FPS must not be greater than 1000");
-	timecodes.push_back(0);
+    if (numerator <= 0 || denominator <= 0)
+        throw InvalidFramerate("Numerator and denominator must both be greater than zero");
+    if (numerator / denominator > 1000) throw InvalidFramerate("FPS must not be greater than 1000");
+    timecodes.push_back(0);
 }
 
-void Framerate::SetFromTimecodes() {
-	validate_timecodes(timecodes);
-	normalize_timecodes(timecodes);
-	denominator = default_denominator;
-	numerator = (timecodes.size() - 1) * denominator * 1000 / timecodes.back();
-	last = (timecodes.size() - 1) * denominator * 1000;
+void Framerate::SetFromTimecodes()
+{
+    validate_timecodes(timecodes);
+    normalize_timecodes(timecodes);
+    denominator = default_denominator;
+    numerator = (timecodes.size() - 1) * denominator * 1000 / timecodes.back();
+    last = (timecodes.size() - 1) * denominator * 1000;
 }
 
 Framerate::Framerate(std::vector<int> timecodes)
-: timecodes(std::move(timecodes))
+    : timecodes(std::move(timecodes))
 {
-	SetFromTimecodes();
+    SetFromTimecodes();
 }
 
 Framerate::Framerate(std::initializer_list<int> timecodes)
-: timecodes(timecodes)
+    : timecodes(timecodes)
 {
-	SetFromTimecodes();
+    SetFromTimecodes();
 }
 
-Framerate::Framerate(fs::path const& filename)
-: denominator(default_denominator)
+Framerate::Framerate(fs::path const &filename)
+    : denominator(default_denominator)
 {
-	auto file = agi::io::Open(filename);
-	auto encoding = agi::charset::Detect(filename);
-	auto line = *line_iterator<std::string>(*file, encoding);
-	if (line == "# timecode format v2") {
-		copy(line_iterator<int>(*file, encoding), line_iterator<int>(), back_inserter(timecodes));
-		SetFromTimecodes();
-		return;
-	}
-	if (line == "# timecode format v1" || line.substr(0, 7) == "Assume ") {
-		if (line[0] == '#')
-			line = *line_iterator<std::string>(*file, encoding);
-		numerator = v1_parse(line_iterator<std::string>(*file, encoding), line, timecodes, last);
-		return;
-	}
-
-	throw UnknownFormat(line);
+    auto file = agi::io::Open(filename);
+    auto encoding = agi::charset::Detect(filename);
+    auto line = *line_iterator<std::string>(*file, encoding);
+    if (line == "# timecode format v2") {
+        copy(line_iterator<int>(*file, encoding), line_iterator<int>(), back_inserter(timecodes));
+        SetFromTimecodes();
+        return;
+    }
+    if (line == "# timecode format v1" || line.substr(0, 7) == "Assume ") {
+        if (line[0] == '#')
+            line = *line_iterator<std::string>(*file, encoding);
+        numerator = v1_parse(line_iterator<std::string>(*file, encoding), line, timecodes, last);
+        return;
+    }
+
+    throw UnknownFormat(line);
 }
 
-void Framerate::Save(fs::path const& filename, int length) const {
-	agi::io::Save file(filename);
-	auto &out = file.Get();
+void Framerate::Save(fs::path const &filename, int length) const
+{
+    agi::io::Save file(filename);
+    auto &out = file.Get();
 
-	out << "# timecode format v2\n";
-	boost::copy(timecodes, std::ostream_iterator<int>(out, "\n"));
-	for (int written = (int)timecodes.size(); written < length; ++written)
-		out << TimeAtFrame(written) << std::endl;
+    out << "# timecode format v2\n";
+    boost::copy(timecodes, std::ostream_iterator<int>(out, "\n"));
+    for (int written = (int)timecodes.size(); written < length; ++written)
+        out << TimeAtFrame(written) << std::endl;
 }
 
-int Framerate::FrameAtTime(int ms, Time type) const {
-	// With X ms per frame, this should return 0 for:
-	// EXACT: [0, X - 1]
-	// START: [1 - X , 0]
-	// END:   [1, X]
+int Framerate::FrameAtTime(int ms, Time type) const
+{
+    // With X ms per frame, this should return 0 for:
+    // EXACT: [0, X - 1]
+    // START: [1 - X , 0]
+    // END:   [1, X]
 
-	// There are two properties we take advantage of here:
-	//  1. START and END's ranges are adjacent, meaning doing the calculations
-	//     for END and adding one gives us START
-	//  2. END is EXACT plus one ms, meaning we can subtract one ms to get EXACT
+    // There are two properties we take advantage of here:
+    //  1. START and END's ranges are adjacent, meaning doing the calculations
+    //     for END and adding one gives us START
+    //  2. END is EXACT plus one ms, meaning we can subtract one ms to get EXACT
 
-	// Combining these allows us to easily calculate START and END in terms of
-	// EXACT
+    // Combining these allows us to easily calculate START and END in terms of
+    // EXACT
 
-	if (type == START)
-		return FrameAtTime(ms - 1) + 1;
-	if (type == END)
-		return FrameAtTime(ms - 1);
+    if (type == START)
+        return FrameAtTime(ms - 1) + 1;
+    if (type == END)
+        return FrameAtTime(ms - 1);
 
-	if (ms < 0)
-		return int((ms * numerator / denominator - 999) / 1000);
+    if (ms < 0)
+        return int((ms * numerator / denominator - 999) / 1000);
 
-	if (ms > timecodes.back())
-		return int((ms * numerator - last + denominator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
+    if (ms > timecodes.back())
+        return int((ms * numerator - last + denominator - 1) / denominator / 1000) + (int)timecodes.size() - 1;
 
-	return (int)distance(lower_bound(timecodes.rbegin(), timecodes.rend(), ms, std::greater<int>()), timecodes.rend()) - 1;
+    return (int)distance(lower_bound(timecodes.rbegin(), timecodes.rend(), ms, std::greater<int>()), timecodes.rend()) - 1;
 }
 
-int Framerate::TimeAtFrame(int frame, Time type) const {
-	if (type == START) {
-		int prev = TimeAtFrame(frame - 1);
-		int cur = TimeAtFrame(frame);
-		// + 1 as these need to round up for the case of two frames 1 ms apart
-		return prev + (cur - prev + 1) / 2;
-	}
-
-	if (type == END) {
-		int cur = TimeAtFrame(frame);
-		int next = TimeAtFrame(frame + 1);
-		return cur + (next - cur + 1) / 2;
-	}
-
-	if (frame < 0)
-		return (int)(frame * denominator * 1000 / numerator);
-
-	if (frame >= (signed)timecodes.size()) {
-		int64_t frames_past_end = frame - (int)timecodes.size() + 1;
-		return int((frames_past_end * 1000 * denominator + last + numerator / 2) / numerator);
-	}
-
-	return timecodes[frame];
+int Framerate::TimeAtFrame(int frame, Time type) const
+{
+    if (type == START) {
+        int prev = TimeAtFrame(frame - 1);
+        int cur = TimeAtFrame(frame);
+        // + 1 as these need to round up for the case of two frames 1 ms apart
+        return prev + (cur - prev + 1) / 2;
+    }
+
+    if (type == END) {
+        int cur = TimeAtFrame(frame);
+        int next = TimeAtFrame(frame + 1);
+        return cur + (next - cur + 1) / 2;
+    }
+
+    if (frame < 0)
+        return (int)(frame * denominator * 1000 / numerator);
+
+    if (frame >= (signed)timecodes.size()) {
+        int64_t frames_past_end = frame - (int)timecodes.size() + 1;
+        return int((frames_past_end * 1000 * denominator + last + numerator / 2) / numerator);
+    }
+
+    return timecodes[frame];
 }
 
-void Framerate::SmpteAtFrame(int frame, int *h, int *m, int *s, int *f) const {
-	frame = std::max(frame, 0);
-	int ifps = (int)ceil(FPS());
-
-	if (drop && denominator == 1001 && numerator % 30000 == 0) {
-		// NTSC skips the first two frames of every minute except for multiples
-		// of ten. For multiples of NTSC, simply multiplying the number of
-		// frames skips seems like the most sensible option.
-		const int drop_factor = int(numerator / 30000);
-		const int one_minute = 60 * 30 * drop_factor - drop_factor * 2;
-		const int ten_minutes = 60 * 10 * 30 * drop_factor - drop_factor * 18;
-		const int ten_minute_groups = frame / ten_minutes;
-		const int last_ten_minutes  = frame % ten_minutes;
-
-		frame += ten_minute_groups * 18 * drop_factor;
-		frame += (last_ten_minutes - 2 * drop_factor) / one_minute * 2 * drop_factor;
-	}
-
-	// Non-integral frame rates other than NTSC aren't supported by SMPTE
-	// timecodes, but the user has asked for it so just give something that
-	// resembles a valid timecode which is no more than half a frame off
-	// wallclock time
-	else if (drop && ifps != FPS()) {
-		frame = int(frame / FPS() * ifps + 0.5);
-	}
-
-	*h = frame / (ifps * 60 * 60);
-	*m = (frame / (ifps * 60)) % 60;
-	*s = (frame / ifps) % 60;
-	*f = frame % ifps;
+void Framerate::SmpteAtFrame(int frame, int *h, int *m, int *s, int *f) const
+{
+    frame = std::max(frame, 0);
+    int ifps = (int)ceil(FPS());
+
+    if (drop && denominator == 1001 && numerator % 30000 == 0) {
+        // NTSC skips the first two frames of every minute except for multiples
+        // of ten. For multiples of NTSC, simply multiplying the number of
+        // frames skips seems like the most sensible option.
+        const int drop_factor = int(numerator / 30000);
+        const int one_minute = 60 * 30 * drop_factor - drop_factor * 2;
+        const int ten_minutes = 60 * 10 * 30 * drop_factor - drop_factor * 18;
+        const int ten_minute_groups = frame / ten_minutes;
+        const int last_ten_minutes  = frame % ten_minutes;
+
+        frame += ten_minute_groups * 18 * drop_factor;
+        frame += (last_ten_minutes - 2 * drop_factor) / one_minute * 2 * drop_factor;
+    }
+
+    // Non-integral frame rates other than NTSC aren't supported by SMPTE
+    // timecodes, but the user has asked for it so just give something that
+    // resembles a valid timecode which is no more than half a frame off
+    // wallclock time
+    else if (drop && ifps != FPS()) {
+        frame = int(frame / FPS() * ifps + 0.5);
+    }
+
+    *h = frame / (ifps * 60 * 60);
+    *m = (frame / (ifps * 60)) % 60;
+    *s = (frame / ifps) % 60;
+    *f = frame % ifps;
 }
 
-void Framerate::SmpteAtTime(int ms, int *h, int *m, int *s, int *f) const {
-	SmpteAtFrame(FrameAtTime(ms), h, m, s, f);
+void Framerate::SmpteAtTime(int ms, int *h, int *m, int *s, int *f) const
+{
+    SmpteAtFrame(FrameAtTime(ms), h, m, s, f);
 }
 
-int Framerate::FrameAtSmpte(int h, int m, int s, int f) const {
-	int ifps = (int)ceil(FPS());
+int Framerate::FrameAtSmpte(int h, int m, int s, int f) const
+{
+    int ifps = (int)ceil(FPS());
 
-	if (drop && denominator == 1001 && numerator % 30000 == 0) {
-		const int drop_factor = int(numerator / 30000);
-		const int one_minute = 60 * 30 * drop_factor - drop_factor * 2;
-		const int ten_minutes = 60 * 10 * 30 * drop_factor - drop_factor * 18;
+    if (drop && denominator == 1001 && numerator % 30000 == 0) {
+        const int drop_factor = int(numerator / 30000);
+        const int one_minute = 60 * 30 * drop_factor - drop_factor * 2;
+        const int ten_minutes = 60 * 10 * 30 * drop_factor - drop_factor * 18;
 
-		const int ten_m = m / 10;
-		m = m % 10;
+        const int ten_m = m / 10;
+        m = m % 10;
 
-		// The specified frame doesn't actually exist so skip forward to the
-		// next frame that does
-		if (m != 0 && s == 0 && f < 2 * drop_factor)
-			f = 2 * drop_factor;
+        // The specified frame doesn't actually exist so skip forward to the
+        // next frame that does
+        if (m != 0 && s == 0 && f < 2 * drop_factor)
+            f = 2 * drop_factor;
 
-		return h * ten_minutes * 6 + ten_m * ten_minutes + m * one_minute + s * ifps + f;
-	}
-	else if (drop && ifps != FPS()) {
-		int frame = (h * 60 * 60 + m * 60 + s) * ifps + f;
-		return int((double)frame / ifps * FPS() + 0.5);
-	}
+        return h * ten_minutes * 6 + ten_m * ten_minutes + m * one_minute + s * ifps + f;
+    }
+    else if (drop && ifps != FPS()) {
+        int frame = (h * 60 * 60 + m * 60 + s) * ifps + f;
+        return int((double)frame / ifps * FPS() + 0.5);
+    }
 
-	return (h * 60 * 60 + m * 60 + s) * ifps + f;
+    return (h * 60 * 60 + m * 60 + s) * ifps + f;
 }
 
-int Framerate::TimeAtSmpte(int h, int m, int s, int f) const {
-	return TimeAtFrame(FrameAtSmpte(h, m, s, f));
+int Framerate::TimeAtSmpte(int h, int m, int s, int f) const
+{
+    return TimeAtFrame(FrameAtSmpte(h, m, s, f));
 }
 
-} }
+}
+}
diff --git a/libaegisub/common/ycbcr_conv.cpp b/libaegisub/common/ycbcr_conv.cpp
index a8e8894f9854bbbda1ab16fdddd4369ad44d8cd0..da4bf5d6b87e128be94a7e1256d0b5fd90840d5b 100644
--- a/libaegisub/common/ycbcr_conv.cpp
+++ b/libaegisub/common/ycbcr_conv.cpp
@@ -18,81 +18,90 @@
 
 namespace {
 double matrix_coefficients[][3] = {
-	{.299, .587, .114},    // BT.601
-	{.2126, .7152, .0722}, // BT.709
-	{.3, .59, .11},        // FCC
-	{.212, .701, .087},    // SMPTE 240M
+    {.299, .587, .114},    // BT.601
+    {.2126, .7152, .0722}, // BT.709
+    {.3, .59, .11},        // FCC
+    {.212, .701, .087},    // SMPTE 240M
 };
 
-void row_mult(std::array<double, 9>& arr, std::array<double, 3> values) {
-	size_t i = 0;
-	for (auto v : values) {
-		arr[i++] *= v;
-		arr[i++] *= v;
-		arr[i++] *= v;
-	}
+void row_mult(std::array<double, 9> &arr, std::array<double, 3> values)
+{
+    size_t i = 0;
+    for (auto v : values) {
+        arr[i++] *= v;
+        arr[i++] *= v;
+        arr[i++] *= v;
+    }
 }
 
-void col_mult(std::array<double, 9>& m, std::array<double, 3> v) {
-	m = {{
-		m[0] * v[0], m[1] * v[1], m[2] * v[2],
-		m[3] * v[0], m[4] * v[1], m[5] * v[2],
-		m[6] * v[0], m[7] * v[1], m[8] * v[2],
-	}};
+void col_mult(std::array<double, 9> &m, std::array<double, 3> v)
+{
+    m = {{
+            m[0] *v[0], m[1] *v[1], m[2] *v[2],
+            m[3] *v[0], m[4] *v[1], m[5] *v[2],
+            m[6] *v[0], m[7] *v[1], m[8] *v[2],
+        }
+    };
 }
 }
 
 namespace agi {
-void ycbcr_converter::init_src(ycbcr_matrix src_mat, ycbcr_range src_range) {
-	auto coeff = matrix_coefficients[(int)src_mat];
-	double Kr = coeff[0];
-	double Kg = coeff[1];
-	double Kb = coeff[2];
-	to_ycbcr = {{
-		Kr,         Kg,         Kb,
-		-Kr/(1-Kb), -Kg/(1-Kb), 1,
-		1,          -Kg/(1-Kr), -Kb/(1-Kr),
-	}};
+void ycbcr_converter::init_src(ycbcr_matrix src_mat, ycbcr_range src_range)
+{
+    auto coeff = matrix_coefficients[(int)src_mat];
+    double Kr = coeff[0];
+    double Kg = coeff[1];
+    double Kb = coeff[2];
+    to_ycbcr = {{
+            Kr,         Kg,         Kb,
+            -Kr / (1 - Kb), -Kg / (1 - Kb), 1,
+            1,          -Kg / (1 - Kr), -Kb / (1 - Kr),
+        }
+    };
 
-	if (src_range == ycbcr_range::pc) {
-		row_mult(to_ycbcr, {{1., .5, .5}});
-		shift_to = {{0, 128., 128.}};
-	}
-	else {
-		row_mult(to_ycbcr, {{219./255., 112./255., 112./255.}});
-		shift_to = {{16., 128., 128.}};
-	}
+    if (src_range == ycbcr_range::pc) {
+        row_mult(to_ycbcr, {{1., .5, .5}});
+        shift_to = {{0, 128., 128.}};
+    }
+    else {
+        row_mult(to_ycbcr, {{219. / 255., 112. / 255., 112. / 255.}});
+        shift_to = {{16., 128., 128.}};
+    }
 }
 
-void ycbcr_converter::init_dst(ycbcr_matrix dst_mat, ycbcr_range dst_range) {
-	auto coeff = matrix_coefficients[(int)dst_mat];
-	double Kr = coeff[0];
-	double Kg = coeff[1];
-	double Kb = coeff[2];
-	from_ycbcr = {{
-		1,  0,             (1-Kr),
-		1, -(1-Kb)*Kb/Kg, -(1-Kr)*Kr/Kg,
-		1,  (1-Kb),        0,
-	}};
+void ycbcr_converter::init_dst(ycbcr_matrix dst_mat, ycbcr_range dst_range)
+{
+    auto coeff = matrix_coefficients[(int)dst_mat];
+    double Kr = coeff[0];
+    double Kg = coeff[1];
+    double Kb = coeff[2];
+    from_ycbcr = {{
+            1,  0,             (1 - Kr),
+            1, -(1 - Kb) *Kb / Kg, -(1 - Kr) *Kr / Kg,
+            1,  (1 - Kb),        0,
+        }
+    };
 
-	if (dst_range == ycbcr_range::pc) {
-		col_mult(from_ycbcr, {{1., 2., 2.}});
-		shift_from = {{0, -128., -128.}};
-	}
-	else {
-		col_mult(from_ycbcr, {{255./219., 255./112., 255./112.}});
-		shift_from = {{-16., -128., -128.}};
-	}
+    if (dst_range == ycbcr_range::pc) {
+        col_mult(from_ycbcr, {{1., 2., 2.}});
+        shift_from = {{0, -128., -128.}};
+    }
+    else {
+        col_mult(from_ycbcr, {{255. / 219., 255. / 112., 255. / 112.}});
+        shift_from = {{-16., -128., -128.}};
+    }
 }
 
-ycbcr_converter::ycbcr_converter(ycbcr_matrix mat, ycbcr_range range) {
-	init_src(mat, range);
-	init_dst(mat, range);
+ycbcr_converter::ycbcr_converter(ycbcr_matrix mat, ycbcr_range range)
+{
+    init_src(mat, range);
+    init_dst(mat, range);
 }
 
-ycbcr_converter::ycbcr_converter(ycbcr_matrix src_mat, ycbcr_range src_range, ycbcr_matrix dst_mat, ycbcr_range dst_range) {
-	init_src(src_mat, src_range);
-	init_dst(dst_mat, dst_range);
+ycbcr_converter::ycbcr_converter(ycbcr_matrix src_mat, ycbcr_range src_range, ycbcr_matrix dst_mat, ycbcr_range dst_range)
+{
+    init_src(src_mat, src_range);
+    init_dst(dst_mat, dst_range);
 }
 }
 
diff --git a/libaegisub/include/libaegisub/access.h b/libaegisub/include/libaegisub/access.h
index 9916fa33e06d669bfc3715721a15330029ec00de..405c61d740325d4a1ad0ee37e2f11f7121ee1793 100644
--- a/libaegisub/include/libaegisub/access.h
+++ b/libaegisub/include/libaegisub/access.h
@@ -14,19 +14,21 @@
 
 #include <libaegisub/fs_fwd.h>
 
-namespace agi { namespace acs {
+namespace agi {
+namespace acs {
 enum Type {
-	FileRead,
-	DirRead,
-	FileWrite,
-	DirWrite
+    FileRead,
+    DirRead,
+    FileWrite,
+    DirWrite
 };
 
-void Check(fs::path const& file, acs::Type);
+void Check(fs::path const &file, acs::Type);
 
-static inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); }
-static inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); }
+static inline void CheckFileRead(fs::path const &file) { Check(file, acs::FileRead); }
+static inline void CheckFileWrite(fs::path const &file) { Check(file, acs::FileWrite); }
 
-static inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); }
-static inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); }
-} }
+static inline void CheckDirRead(fs::path const &dir) { Check(dir, acs::DirRead); }
+static inline void CheckDirWrite(fs::path const &dir) { Check(dir, acs::DirWrite); }
+}
+}
diff --git a/libaegisub/include/libaegisub/address_of_adaptor.h b/libaegisub/include/libaegisub/address_of_adaptor.h
index 1ca2c33f960067aab24249f39687418c15205514..379842ffa760ce44f91e562d36e34af57421570e 100644
--- a/libaegisub/include/libaegisub/address_of_adaptor.h
+++ b/libaegisub/include/libaegisub/address_of_adaptor.h
@@ -18,35 +18,35 @@
 
 namespace agi {
 namespace address_of_detail {
-	using namespace boost::adaptors;
-
-	// Tag type to select the operator| overload
-	struct address_of_tag_type { };
-
-	template<typename Iterator>
-	struct take_address_of {
-		using result_type = typename std::iterator_traits<Iterator>::pointer;
-		using input_type = typename std::iterator_traits<Iterator>::reference;
-
-		result_type operator()(input_type v) const { return &v; }
-	};
-
-	template<typename Rng>
-	auto operator|(Rng&& r, address_of_tag_type)
-		-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
-	{
-		return r | transformed(take_address_of<typename Rng::iterator>());
-	}
-
-	template<typename Rng>
-	auto operator|(Rng& r, address_of_tag_type)
-		-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
-	{
-		return r | transformed(take_address_of<typename Rng::iterator>());
-	}
+using namespace boost::adaptors;
+
+// Tag type to select the operator| overload
+struct address_of_tag_type { };
+
+template<typename Iterator>
+struct take_address_of {
+    using result_type = typename std::iterator_traits<Iterator>::pointer;
+    using input_type = typename std::iterator_traits<Iterator>::reference;
+
+    result_type operator()(input_type v) const { return &v; }
+};
+
+template<typename Rng>
+auto operator|(Rng &&r, address_of_tag_type)
+-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
+{
+    return r | transformed(take_address_of<typename Rng::iterator>());
+}
+
+template<typename Rng>
+auto operator|(Rng &r, address_of_tag_type)
+-> boost::transformed_range<take_address_of<typename Rng::iterator>, Rng>
+{
+    return r | transformed(take_address_of<typename Rng::iterator>());
+}
 }
 
 namespace {
-	const auto address_of = address_of_detail::address_of_tag_type{};
+const auto address_of = address_of_detail::address_of_tag_type {};
 }
 }
diff --git a/libaegisub/include/libaegisub/ass/dialogue_parser.h b/libaegisub/include/libaegisub/ass/dialogue_parser.h
index 0aa3f9962bd8fa1ad59e4e052ba2fb9349974e9e..0ce50902de5327c946d22356bfe16a923040a9fe 100644
--- a/libaegisub/include/libaegisub/ass/dialogue_parser.h
+++ b/libaegisub/include/libaegisub/ass/dialogue_parser.h
@@ -20,64 +20,64 @@
 #undef ERROR
 
 namespace agi {
-	class SpellChecker;
+class SpellChecker;
 
-	namespace ass {
-		namespace DialogueTokenType {
-			enum {
-				TEXT = 1000,
-				WORD,
-				LINE_BREAK,
-				OVR_BEGIN,
-				OVR_END,
-				TAG_START,
-				TAG_NAME,
-				OPEN_PAREN,
-				CLOSE_PAREN,
-				ARG_SEP,
-				ARG,
-				ERROR,
-				COMMENT,
-				WHITESPACE,
-				DRAWING,
-				KARAOKE_TEMPLATE,
-				KARAOKE_VARIABLE
-			};
-		}
+namespace ass {
+namespace DialogueTokenType {
+enum {
+    TEXT = 1000,
+    WORD,
+    LINE_BREAK,
+    OVR_BEGIN,
+    OVR_END,
+    TAG_START,
+    TAG_NAME,
+    OPEN_PAREN,
+    CLOSE_PAREN,
+    ARG_SEP,
+    ARG,
+    ERROR,
+    COMMENT,
+    WHITESPACE,
+    DRAWING,
+    KARAOKE_TEMPLATE,
+    KARAOKE_VARIABLE
+};
+}
 
-		namespace SyntaxStyle {
-			enum {
-				NORMAL = 0,
-				COMMENT,
-				DRAWING,
-				OVERRIDE,
-				PUNCTUATION,
-				TAG,
-				ERROR,
-				PARAMETER,
-				LINE_BREAK,
-				KARAOKE_TEMPLATE,
-				KARAOKE_VARIABLE,
+namespace SyntaxStyle {
+enum {
+    NORMAL = 0,
+    COMMENT,
+    DRAWING,
+    OVERRIDE,
+    PUNCTUATION,
+    TAG,
+    ERROR,
+    PARAMETER,
+    LINE_BREAK,
+    KARAOKE_TEMPLATE,
+    KARAOKE_VARIABLE,
 
-				SPELLING = 32
-			};
-		}
+    SPELLING = 32
+};
+}
 
-		struct DialogueToken {
-			int type;
-			size_t length;
-		};
+struct DialogueToken {
+    int type;
+    size_t length;
+};
 
-		/// Tokenize the passed string as the body of a dialogue line
-		std::vector<DialogueToken> TokenizeDialogueBody(std::string const& str, bool karaoke_templater=false);
+/// Tokenize the passed string as the body of a dialogue line
+std::vector<DialogueToken> TokenizeDialogueBody(std::string const &str, bool karaoke_templater = false);
 
-		/// Convert the body of drawings to DRAWING tokens
-		void MarkDrawings(std::string const& str, std::vector<DialogueToken> &tokens);
+/// Convert the body of drawings to DRAWING tokens
+void MarkDrawings(std::string const &str, std::vector<DialogueToken> &tokens);
 
-		/// Split the words in the TEXT tokens of the lexed line into their
-		/// own tokens and convert the body of drawings to DRAWING tokens
-		void SplitWords(std::string const& str, std::vector<DialogueToken> &tokens);
+/// Split the words in the TEXT tokens of the lexed line into their
+/// own tokens and convert the body of drawings to DRAWING tokens
+void SplitWords(std::string const &str, std::vector<DialogueToken> &tokens);
 
-		std::vector<DialogueToken> SyntaxHighlight(std::string const& text, std::vector<DialogueToken> const& tokens, SpellChecker *spellchecker);
-	}
+std::vector<DialogueToken> SyntaxHighlight(std::string const &text, std::vector<DialogueToken> const &tokens, SpellChecker *spellchecker);
+}
 }
diff --git a/libaegisub/include/libaegisub/ass/smpte.h b/libaegisub/include/libaegisub/ass/smpte.h
index 129825182da25a2f48bae3c3d30b7aa85ef6fb95..17645b64dd655909846cdc9687fdef84b4d975a9 100644
--- a/libaegisub/include/libaegisub/ass/smpte.h
+++ b/libaegisub/include/libaegisub/ass/smpte.h
@@ -24,17 +24,17 @@ class Time;
 /// @class SmpteFormatter
 /// @brief Convert times to and from SMPTE timecodes
 class SmpteFormatter {
-	/// Frame rate to use
-	vfr::Framerate fps;
-	/// Separator character
-	char sep;
+    /// Frame rate to use
+    vfr::Framerate fps;
+    /// Separator character
+    char sep;
 
 public:
-	SmpteFormatter(vfr::Framerate fps, char sep=':');
+    SmpteFormatter(vfr::Framerate fps, char sep = ':');
 
-	/// Convert an Time to a SMPTE timecode
-	std::string ToSMPTE(Time time) const;
-	/// Convert a SMPTE timecode to an Time
-	Time FromSMPTE(std::string const& str) const;
+    /// Convert an Time to a SMPTE timecode
+    std::string ToSMPTE(Time time) const;
+    /// Convert a SMPTE timecode to an Time
+    Time FromSMPTE(std::string const &str) const;
 };
 }
diff --git a/libaegisub/include/libaegisub/ass/time.h b/libaegisub/include/libaegisub/ass/time.h
index 018b5b94a8bf157f3ca31ab4d5d27af81941f9d6..8d55c2e775464d8356bb5c900b98f76b3742ac28 100644
--- a/libaegisub/include/libaegisub/ass/time.h
+++ b/libaegisub/include/libaegisub/ass/time.h
@@ -20,24 +20,24 @@
 
 namespace agi {
 class Time {
-	/// Time in milliseconds
-	int time = 0;
+    /// Time in milliseconds
+    int time = 0;
 
 public:
-	Time(int ms = 0);
-	Time(std::string const& text);
+    Time(int ms = 0);
+    Time(std::string const &text);
 
-	/// Get millisecond, rounded to centisecond precision
-	operator int() const { return time / 10 * 10; }
+    /// Get millisecond, rounded to centisecond precision
+    operator int() const { return time / 10 * 10; }
 
-	int GetTimeHours() const;        ///< Get the hours portion of this time
-	int GetTimeMinutes() const;      ///< Get the minutes portion of this time
-	int GetTimeSeconds() const;      ///< Get the seconds portion of this time
-	int GetTimeMiliseconds() const;  ///< Get the miliseconds portion of this time
-	int GetTimeCentiseconds() const; ///< Get the centiseconds portion of this time
+    int GetTimeHours() const;        ///< Get the hours portion of this time
+    int GetTimeMinutes() const;      ///< Get the minutes portion of this time
+    int GetTimeSeconds() const;      ///< Get the seconds portion of this time
+    int GetTimeMiliseconds() const;  ///< Get the miliseconds portion of this time
+    int GetTimeCentiseconds() const; ///< Get the centiseconds portion of this time
 
-	/// Return the time as a string
-	/// @param ms Use milliseconds precision, for non-ASS formats
-	std::string GetAssFormatted(bool ms=false) const;
+    /// Return the time as a string
+    /// @param ms Use milliseconds precision, for non-ASS formats
+    std::string GetAssFormatted(bool ms = false) const;
 };
 }
diff --git a/libaegisub/include/libaegisub/ass/uuencode.h b/libaegisub/include/libaegisub/ass/uuencode.h
index 98e37b44d5cea02b5863da8b81f16a2edd26934f..23f806c51f29199ab71ade9a70ba0519142317b8 100644
--- a/libaegisub/include/libaegisub/ass/uuencode.h
+++ b/libaegisub/include/libaegisub/ass/uuencode.h
@@ -17,10 +17,12 @@
 #include <string>
 #include <vector>
 
-namespace agi { namespace ass {
+namespace agi {
+namespace ass {
 /// Encode a blob of data, using ASS's nonstandard variant
-std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks=true);
+std::string UUEncode(const char *begin, const char *end, bool insert_linebreaks = true);
 
 /// Decode an ASS uuencoded string
 std::vector<char> UUDecode(const char *begin, const char *end);
-} }
+}
+}
diff --git a/libaegisub/include/libaegisub/audio/provider.h b/libaegisub/include/libaegisub/audio/provider.h
index debb560ce72289b23cd150094b7fa94ef15cc8ea..57a44a65ada8355aa9860c486faba05aac0fa6bd 100644
--- a/libaegisub/include/libaegisub/audio/provider.h
+++ b/libaegisub/include/libaegisub/audio/provider.h
@@ -25,52 +25,51 @@
 namespace agi {
 class AudioProvider {
 protected:
-	int channels = 0;
-	/// Total number of samples per channel
-	int64_t num_samples = 0;
-	/// Samples per channel which have been decoded and can be fetched with FillBuffer
-	/// Only applicable for the cache providers
-	std::atomic<int64_t> decoded_samples{0};
-	int sample_rate = 0;
-	int bytes_per_sample = 0;
-	bool float_samples = false;
+    int channels = 0;
+    /// Total number of samples per channel
+    int64_t num_samples = 0;
+    /// Samples per channel which have been decoded and can be fetched with FillBuffer
+    /// Only applicable for the cache providers
+    std::atomic<int64_t> decoded_samples{0};
+    int sample_rate = 0;
+    int bytes_per_sample = 0;
+    bool float_samples = false;
 
-	virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0;
+    virtual void FillBuffer(void *buf, int64_t start, int64_t count) const = 0;
 
-	void ZeroFill(void *buf, int64_t count) const;
+    void ZeroFill(void *buf, int64_t count) const;
 
 public:
-	virtual ~AudioProvider() = default;
+    virtual ~AudioProvider() = default;
 
-	void GetAudio(void *buf, int64_t start, int64_t count) const;
-	void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
+    void GetAudio(void *buf, int64_t start, int64_t count) const;
+    void GetAudioWithVolume(void *buf, int64_t start, int64_t count, double volume) const;
 
-	int64_t GetNumSamples()     const { return num_samples; }
-	int64_t GetDecodedSamples() const { return decoded_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; }
+    int64_t GetNumSamples()     const { return num_samples; }
+    int64_t GetDecodedSamples() const { return decoded_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; }
 
-	/// Does this provider benefit from external caching?
-	virtual bool NeedsCache() const { return false; }
+    /// Does this provider benefit from external caching?
+    virtual bool NeedsCache() const { return false; }
 };
 
 /// Helper base class for an audio provider which wraps another provider
 class AudioProviderWrapper : public AudioProvider {
 protected:
-	std::unique_ptr<AudioProvider> source;
+    std::unique_ptr<AudioProvider> source;
 public:
-	AudioProviderWrapper(std::unique_ptr<AudioProvider> src)
-	: source(std::move(src))
-	{
-		channels = source->GetChannels();
-		num_samples = source->GetNumSamples();
-		decoded_samples = source->GetDecodedSamples();
-		sample_rate = source->GetSampleRate();
-		bytes_per_sample = source->GetBytesPerSample();
-		float_samples = source->AreSamplesFloat();
-	}
+    AudioProviderWrapper(std::unique_ptr<AudioProvider> src)
+        : source(std::move(src)) {
+        channels = source->GetChannels();
+        num_samples = source->GetNumSamples();
+        decoded_samples = source->GetDecodedSamples();
+        sample_rate = source->GetSampleRate();
+        bytes_per_sample = source->GetBytesPerSample();
+        float_samples = source->AreSamplesFloat();
+    }
 };
 
 DEFINE_EXCEPTION(AudioProviderError, Exception);
@@ -83,13 +82,13 @@ DEFINE_EXCEPTION(AudioDataNotFound, AudioProviderError);
 
 class BackgroundRunner;
 
-std::unique_ptr<AudioProvider> CreateDummyAudioProvider(fs::path const& filename, BackgroundRunner *);
-std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const& filename, BackgroundRunner *);
+std::unique_ptr<AudioProvider> CreateDummyAudioProvider(fs::path const &filename, BackgroundRunner *);
+std::unique_ptr<AudioProvider> CreatePCMAudioProvider(fs::path const &filename, BackgroundRunner *);
 
 std::unique_ptr<AudioProvider> CreateConvertAudioProvider(std::unique_ptr<AudioProvider> source_provider);
 std::unique_ptr<AudioProvider> CreateLockAudioProvider(std::unique_ptr<AudioProvider> source_provider);
-std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> source_provider, fs::path const& dir);
+std::unique_ptr<AudioProvider> CreateHDAudioProvider(std::unique_ptr<AudioProvider> source_provider, fs::path const &dir);
 std::unique_ptr<AudioProvider> CreateRAMAudioProvider(std::unique_ptr<AudioProvider> source_provider);
 
-void SaveAudioClip(AudioProvider const& provider, fs::path const& path, int start_time, int end_time);
+void SaveAudioClip(AudioProvider const &provider, fs::path const &path, int start_time, int end_time);
 }
diff --git a/libaegisub/include/libaegisub/background_runner.h b/libaegisub/include/libaegisub/background_runner.h
index 29bd8efcbc4d62596c2b03fd54aaefc9d6967c78..bd23ce95995e14fb07fb10a6f1f670ab9e267b03 100644
--- a/libaegisub/include/libaegisub/background_runner.h
+++ b/libaegisub/include/libaegisub/background_runner.h
@@ -23,57 +23,57 @@
 #include <string>
 
 namespace agi {
-	/// @class ProgressSink
-	/// @brief A receiver for progress updates sent from a worker function
-	///
-	/// Note that ProgressSinks are not required to be thread-safe. The only
-	/// guarantee provided is that they can be used on the thread they are
-	/// spawned on (which may or may not be the GUI thread).
-	class ProgressSink {
-	public:
-		/// Virtual destructor so that things can safely inherit from this
-		virtual ~ProgressSink() { }
+/// @class ProgressSink
+/// @brief A receiver for progress updates sent from a worker function
+///
+/// Note that ProgressSinks are not required to be thread-safe. The only
+/// guarantee provided is that they can be used on the thread they are
+/// spawned on (which may or may not be the GUI thread).
+class ProgressSink {
+public:
+    /// Virtual destructor so that things can safely inherit from this
+    virtual ~ProgressSink() { }
 
-		/// Set the progress to indeterminate
-		virtual void SetIndeterminate()=0;
+    /// Set the progress to indeterminate
+    virtual void SetIndeterminate() = 0;
 
-		/// Set the title of the running task
-		virtual void SetTitle(std::string const& title)=0;
-		/// Set an additional message associated with the task
-		virtual void SetMessage(std::string const& msg)=0;
-		/// Set the current task progress
-		virtual void SetProgress(int64_t cur, int64_t max)=0;
+    /// Set the title of the running task
+    virtual void SetTitle(std::string const &title) = 0;
+    /// Set an additional message associated with the task
+    virtual void SetMessage(std::string const &msg) = 0;
+    /// Set the current task progress
+    virtual void SetProgress(int64_t cur, int64_t max) = 0;
 
-		/// @brief Log a message
-		///
-		/// If any messages are logged then the dialog will not automatically close
-		/// when the task finishes so that the user has the chance to read them.
-		virtual void Log(std::string const& str)=0;
+    /// @brief Log a message
+    ///
+    /// If any messages are logged then the dialog will not automatically close
+    /// when the task finishes so that the user has the chance to read them.
+    virtual void Log(std::string const &str) = 0;
 
-		/// Has the user asked the task to cancel?
-		virtual bool IsCancelled()=0;
-	};
+    /// Has the user asked the task to cancel?
+    virtual bool IsCancelled() = 0;
+};
 
-	/// @class BackgroundRunner
-	/// @brief A class which runs a function, providing it with a progress sink
-	///
-	/// Normally implementations of this interface will spawn a new thread to
-	/// run the task on, but there are sensible implementations that may not.
-	/// For example, an implementation which has no UI and simply writes the
-	/// log output to a file would simply run on the main thread.
-	class BackgroundRunner {
-	public:
-		/// Virtual destructor so that things can safely inherit from this
-		virtual ~BackgroundRunner() { }
+/// @class BackgroundRunner
+/// @brief A class which runs a function, providing it with a progress sink
+///
+/// Normally implementations of this interface will spawn a new thread to
+/// run the task on, but there are sensible implementations that may not.
+/// For example, an implementation which has no UI and simply writes the
+/// log output to a file would simply run on the main thread.
+class BackgroundRunner {
+public:
+    /// Virtual destructor so that things can safely inherit from this
+    virtual ~BackgroundRunner() { }
 
-		/// @brief Run a function on a background thread
-		/// @param task Function to run
-		/// @throws agi::UserCancelException on cancel
-		///
-		/// Blocks the calling thread until the task completes or is canceled.
-		/// Progress updates sent to the progress sink passed to the task should
-		/// be displayed to the user in some way, along with some way for the
-		/// user to cancel the task.
-		virtual void Run(std::function<void(ProgressSink *)> task)=0;
-	};
+    /// @brief Run a function on a background thread
+    /// @param task Function to run
+    /// @throws agi::UserCancelException on cancel
+    ///
+    /// Blocks the calling thread until the task completes or is canceled.
+    /// Progress updates sent to the progress sink passed to the task should
+    /// be displayed to the user in some way, along with some way for the
+    /// user to cancel the task.
+    virtual void Run(std::function<void(ProgressSink *)> task) = 0;
+};
 }
diff --git a/libaegisub/include/libaegisub/cajun/elements.h b/libaegisub/include/libaegisub/cajun/elements.h
index 1ba4b06960261dc9253feedada7388f4da0255de..a427f53455d21d7ced502e98a8a35e1961122a7b 100644
--- a/libaegisub/include/libaegisub/cajun/elements.h
+++ b/libaegisub/include/libaegisub/cajun/elements.h
@@ -37,7 +37,7 @@ struct Null;
 // Exception - base class for all JSON-related runtime errors
 class Exception : public std::runtime_error {
 public:
-	Exception(const std::string& sMessage) : std::runtime_error(sMessage) { }
+    Exception(const std::string &sMessage) : std::runtime_error(sMessage) { }
 };
 
 /////////////////////////////////////////////////////////////////////////
@@ -55,54 +55,54 @@ public:
 //  String str = objInvoices[1]["Customer"]["Company"];
 class UnknownElement {
 public:
-   UnknownElement();
-   UnknownElement(UnknownElement&& unknown);
-   UnknownElement(Object object);
-   UnknownElement(Array array);
-   UnknownElement(double number);
-   UnknownElement(int number);
-   UnknownElement(int64_t number);
-   UnknownElement(bool boolean);
-   UnknownElement(const char *string);
-   UnknownElement(String string);
-   UnknownElement(Null null);
-
-   ~UnknownElement();
-
-   UnknownElement& operator=(UnknownElement&& unknown);
-
-   // implicit cast to actual element type. throws on failure
-   operator Object const&() const;
-   operator Array const&() const;
-   operator Integer const&() const;
-   operator Double const&() const;
-   operator Boolean const&() const;
-   operator String const&() const;
-   operator Null const&() const;
-   operator Object&();
-   operator Array&();
-   operator Integer&();
-   operator Double&();
-   operator Boolean&();
-   operator String&();
-   operator Null&();
-
-   // implements visitor pattern
-   void Accept(ConstVisitor& visitor) const;
-   void Accept(Visitor& visitor);
-
-   class Imp;
+    UnknownElement();
+    UnknownElement(UnknownElement &&unknown);
+    UnknownElement(Object object);
+    UnknownElement(Array array);
+    UnknownElement(double number);
+    UnknownElement(int number);
+    UnknownElement(int64_t number);
+    UnknownElement(bool boolean);
+    UnknownElement(const char *string);
+    UnknownElement(String string);
+    UnknownElement(Null null);
+
+    ~UnknownElement();
+
+    UnknownElement &operator=(UnknownElement &&unknown);
+
+    // implicit cast to actual element type. throws on failure
+    operator Object const &() const;
+    operator Array const &() const;
+    operator Integer const &() const;
+    operator Double const &() const;
+    operator Boolean const &() const;
+    operator String const &() const;
+    operator Null const &() const;
+    operator Object &();
+    operator Array &();
+    operator Integer &();
+    operator Double &();
+    operator Boolean &();
+    operator String &();
+    operator Null &();
+
+    // implements visitor pattern
+    void Accept(ConstVisitor &visitor) const;
+    void Accept(Visitor &visitor);
+
+    class Imp;
 
 private:
-   UnknownElement(UnknownElement const& unknown) = delete;
+    UnknownElement(UnknownElement const &unknown) = delete;
 
-   template <typename ElementTypeT>
-   ElementTypeT const& CastTo() const;
+    template <typename ElementTypeT>
+    ElementTypeT const &CastTo() const;
 
-   template <typename ElementTypeT>
-   ElementTypeT& CastTo();
+    template <typename ElementTypeT>
+    ElementTypeT &CastTo();
 
-   std::unique_ptr<Imp> m_pImp;
+    std::unique_ptr<Imp> m_pImp;
 };
 
 /////////////////////////////////////////////////////////////////////////////////
diff --git a/libaegisub/include/libaegisub/cajun/reader.h b/libaegisub/include/libaegisub/cajun/reader.h
index b181a75a76a96ebaf1ff8e0b2e9999f00ac94e18..53e67841766ffcbb76c2d2b18dad8a8f4cfb1fbb 100644
--- a/libaegisub/include/libaegisub/cajun/reader.h
+++ b/libaegisub/include/libaegisub/cajun/reader.h
@@ -12,91 +12,90 @@ Author: Terry Caton
 
 #include <vector>
 
-namespace json
-{
+namespace json {
 
 class Reader {
 public:
-	// this structure will be reported in one of the exceptions defined below
-	struct Location {
-		unsigned int m_nLine = 0;       // document line, zero-indexed
-		unsigned int m_nLineOffset = 0; // character offset from beginning of line, zero indexed
-		unsigned int m_nDocOffset = 0;  // character offset from entire document, zero indexed
-	};
-
-	// thrown during the first phase of reading. generally catches low-level
-	// problems such as errant characters or corrupt/incomplete documents
-	class ScanException final : public Exception {
-	public:
-		ScanException(std::string const& sMessage, Reader::Location locError)
-		: Exception(sMessage)
-		, m_locError(std::move(locError))
-		{ }
-
-		Reader::Location m_locError;
-	};
-
-	// thrown during the second phase of reading. generally catches
-	// higher-level problems such as missing commas or brackets
-	class ParseException final : public Exception {
-	public:
-		ParseException(std::string const& sMessage, Reader::Location locTokenBegin, Reader::Location locTokenEnd)
-		: Exception(sMessage)
-		, m_locTokenBegin(std::move(locTokenBegin))
-		, m_locTokenEnd(std::move(locTokenEnd))
-		{ }
-
-		Reader::Location m_locTokenBegin;
-		Reader::Location m_locTokenEnd;
-	};
-
-	static void Read(UnknownElement& elementRoot, std::istream& istr);
+    // this structure will be reported in one of the exceptions defined below
+    struct Location {
+        unsigned int m_nLine = 0;       // document line, zero-indexed
+        unsigned int m_nLineOffset = 0; // character offset from beginning of line, zero indexed
+        unsigned int m_nDocOffset = 0;  // character offset from entire document, zero indexed
+    };
+
+    // thrown during the first phase of reading. generally catches low-level
+    // problems such as errant characters or corrupt/incomplete documents
+    class ScanException final : public Exception {
+    public:
+        ScanException(std::string const &sMessage, Reader::Location locError)
+            : Exception(sMessage)
+            , m_locError(std::move(locError))
+        { }
+
+        Reader::Location m_locError;
+    };
+
+    // thrown during the second phase of reading. generally catches
+    // higher-level problems such as missing commas or brackets
+    class ParseException final : public Exception {
+    public:
+        ParseException(std::string const &sMessage, Reader::Location locTokenBegin, Reader::Location locTokenEnd)
+            : Exception(sMessage)
+            , m_locTokenBegin(std::move(locTokenBegin))
+            , m_locTokenEnd(std::move(locTokenEnd))
+        { }
+
+        Reader::Location m_locTokenBegin;
+        Reader::Location m_locTokenEnd;
+    };
+
+    static void Read(UnknownElement &elementRoot, std::istream &istr);
 
 private:
-	struct Token {
-		enum Type {
-			TOKEN_OBJECT_BEGIN,  // {
-			TOKEN_OBJECT_END,    // }
-			TOKEN_ARRAY_BEGIN,   // [
-			TOKEN_ARRAY_END,     // ]
-			TOKEN_NEXT_ELEMENT,  // ,
-			TOKEN_MEMBER_ASSIGN, // :
-			TOKEN_STRING,        // "xxx"
-			TOKEN_NUMBER,        // [+/-]000.000[e[+/-]000]
-			TOKEN_BOOLEAN,       // true -or- false
-			TOKEN_NULL           // null
-		};
-
-		Type nType;
-		std::string sValue;
-
-		// for malformed file debugging
-		Reader::Location locBegin;
-		Reader::Location locEnd;
-	};
-
-	class InputStream;
-	class TokenStream;
-	typedef std::vector<Token> Tokens;
-
-	// scanning istream into token sequence
-	void Scan(Tokens& tokens, InputStream& inputStream);
-
-	void EatWhiteSpace(InputStream& inputStream);
-	void MatchString(std::string& sValue, InputStream& inputStream);
-	void MatchNumber(std::string& sNumber, InputStream& inputStream);
-	void MatchExpectedString(std::string const& sExpected, InputStream& inputStream);
-
-	// parsing token sequence into element structure
-	UnknownElement Parse(TokenStream& tokenStream);
-	UnknownElement ParseObject(TokenStream& tokenStream);
-	UnknownElement ParseArray(TokenStream& tokenStream);
-	UnknownElement ParseString(TokenStream& tokenStream);
-	UnknownElement ParseNumber(TokenStream& tokenStream);
-	UnknownElement ParseBoolean(TokenStream& tokenStream);
-	UnknownElement ParseNull(TokenStream& tokenStream);
-
-	std::string const& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
+    struct Token {
+        enum Type {
+            TOKEN_OBJECT_BEGIN,  // {
+            TOKEN_OBJECT_END,    // }
+            TOKEN_ARRAY_BEGIN,   // [
+            TOKEN_ARRAY_END,     // ]
+            TOKEN_NEXT_ELEMENT,  // ,
+            TOKEN_MEMBER_ASSIGN, // :
+            TOKEN_STRING,        // "xxx"
+            TOKEN_NUMBER,        // [+/-]000.000[e[+/-]000]
+            TOKEN_BOOLEAN,       // true -or- false
+            TOKEN_NULL           // null
+        };
+
+        Type nType;
+        std::string sValue;
+
+        // for malformed file debugging
+        Reader::Location locBegin;
+        Reader::Location locEnd;
+    };
+
+    class InputStream;
+    class TokenStream;
+    typedef std::vector<Token> Tokens;
+
+    // scanning istream into token sequence
+    void Scan(Tokens &tokens, InputStream &inputStream);
+
+    void EatWhiteSpace(InputStream &inputStream);
+    void MatchString(std::string &sValue, InputStream &inputStream);
+    void MatchNumber(std::string &sNumber, InputStream &inputStream);
+    void MatchExpectedString(std::string const &sExpected, InputStream &inputStream);
+
+    // parsing token sequence into element structure
+    UnknownElement Parse(TokenStream &tokenStream);
+    UnknownElement ParseObject(TokenStream &tokenStream);
+    UnknownElement ParseArray(TokenStream &tokenStream);
+    UnknownElement ParseString(TokenStream &tokenStream);
+    UnknownElement ParseNumber(TokenStream &tokenStream);
+    UnknownElement ParseBoolean(TokenStream &tokenStream);
+    UnknownElement ParseNull(TokenStream &tokenStream);
+
+    std::string const &MatchExpectedToken(Token::Type nExpected, TokenStream &tokenStream);
 };
 
 }
diff --git a/libaegisub/include/libaegisub/cajun/visitor.h b/libaegisub/include/libaegisub/cajun/visitor.h
index ac91a9a089a6845de53f55b83da12a097ec9a155..06f077eeb29533f6fc9b1e0d43780046a2a5cecc 100644
--- a/libaegisub/include/libaegisub/cajun/visitor.h
+++ b/libaegisub/include/libaegisub/cajun/visitor.h
@@ -10,31 +10,30 @@ Author: Terry Caton
 
 #include "elements.h"
 
-namespace json
-{
+namespace json {
 
 struct Visitor {
-	virtual ~Visitor() = default;
-
-	virtual void Visit(Array& array) = 0;
-	virtual void Visit(Object& object) = 0;
-	virtual void Visit(Integer& number) = 0;
-	virtual void Visit(Double& number) = 0;
-	virtual void Visit(String& string) = 0;
-	virtual void Visit(Boolean& boolean) = 0;
-	virtual void Visit(Null& null) = 0;
+    virtual ~Visitor() = default;
+
+    virtual void Visit(Array &array) = 0;
+    virtual void Visit(Object &object) = 0;
+    virtual void Visit(Integer &number) = 0;
+    virtual void Visit(Double &number) = 0;
+    virtual void Visit(String &string) = 0;
+    virtual void Visit(Boolean &boolean) = 0;
+    virtual void Visit(Null &null) = 0;
 };
 
 struct ConstVisitor {
-	virtual ~ConstVisitor() = default;
-
-	virtual void Visit(const Array& array) = 0;
-	virtual void Visit(const Object& object) = 0;
-	virtual void Visit(int64_t number) = 0;
-	virtual void Visit(double number) = 0;
-	virtual void Visit(const String& string) = 0;
-	virtual void Visit(bool boolean) = 0;
-	virtual void Visit(const Null& null) = 0;
+    virtual ~ConstVisitor() = default;
+
+    virtual void Visit(const Array &array) = 0;
+    virtual void Visit(const Object &object) = 0;
+    virtual void Visit(int64_t number) = 0;
+    virtual void Visit(double number) = 0;
+    virtual void Visit(const String &string) = 0;
+    virtual void Visit(bool boolean) = 0;
+    virtual void Visit(const Null &null) = 0;
 };
 
 } // End namespace
diff --git a/libaegisub/include/libaegisub/cajun/writer.h b/libaegisub/include/libaegisub/cajun/writer.h
index 9b2bac23e8d07fafae3aa1d6aa663dc941bcf140..2d8cc042668bbc142de01c0d9215cf19c9147cc8 100644
--- a/libaegisub/include/libaegisub/cajun/writer.h
+++ b/libaegisub/include/libaegisub/cajun/writer.h
@@ -22,26 +22,26 @@
 namespace agi {
 
 class JsonWriter final : json::ConstVisitor {
-	std::ostream &ostr;
-	std::string indent;
+    std::ostream &ostr;
+    std::string indent;
 
-	JsonWriter(std::ostream &ostr) : ostr(ostr) { }
+    JsonWriter(std::ostream &ostr) : ostr(ostr) { }
 
-	void Visit(json::Array const& array) override;
-	void Visit(bool boolean) override;
-	void Visit(double number) override;
-	void Visit(int64_t number) override;
-	void Visit(json::Null const& null) override;
-	void Visit(json::Object const& object) override;
-	void Visit(std::string const& string) override;
-	void Visit(json::UnknownElement const& unknown);
+    void Visit(json::Array const &array) override;
+    void Visit(bool boolean) override;
+    void Visit(double number) override;
+    void Visit(int64_t number) override;
+    void Visit(json::Null const &null) override;
+    void Visit(json::Object const &object) override;
+    void Visit(std::string const &string) override;
+    void Visit(json::UnknownElement const &unknown);
 
 public:
-	template <typename T>
-	static void Write(T const& value, std::ostream& ostr) {
-		JsonWriter(ostr).Visit(value);
-		ostr.flush();
-	}
+    template <typename T>
+    static void Write(T const &value, std::ostream &ostr) {
+        JsonWriter(ostr).Visit(value);
+        ostr.flush();
+    }
 };
 
 }
diff --git a/libaegisub/include/libaegisub/calltip_provider.h b/libaegisub/include/libaegisub/calltip_provider.h
index 238b3c133450b4d0200babc1ff920845168172f9..d2497ea7fc904ee897e34b38f0db480a6d0d7f0d 100644
--- a/libaegisub/include/libaegisub/calltip_provider.h
+++ b/libaegisub/include/libaegisub/calltip_provider.h
@@ -21,12 +21,12 @@ namespace agi {
 namespace ass { struct DialogueToken; }
 
 struct Calltip {
-	const char *text;       ///< Text of the calltip
-	size_t highlight_start; ///< Start index of the current parameter in text
-	size_t highlight_end;   ///< End index of the current parameter in text
-	size_t tag_position;    ///< Start index of the tag in the input line
+    const char *text;       ///< Text of the calltip
+    size_t highlight_start; ///< Start index of the current parameter in text
+    size_t highlight_end;   ///< End index of the current parameter in text
+    size_t tag_position;    ///< Start index of the tag in the input line
 };
 
 /// Get the calltip to show for the given cursor position in the text
-Calltip GetCalltip(std::vector<ass::DialogueToken> const& tokens, std::string const& text, size_t pos);
+Calltip GetCalltip(std::vector<ass::DialogueToken> const &tokens, std::string const &text, size_t pos);
 }
diff --git a/libaegisub/include/libaegisub/character_count.h b/libaegisub/include/libaegisub/character_count.h
index 45ce53ce5f89479f8ef0ecb0c8eccf84e6cc42d9..96a45c2dd43d4e99bf6e4934008f83b107b65344 100644
--- a/libaegisub/include/libaegisub/character_count.h
+++ b/libaegisub/include/libaegisub/character_count.h
@@ -17,19 +17,19 @@
 #include <string>
 
 namespace agi {
-	enum {
-		IGNORE_NONE = 0,
-		IGNORE_WHITESPACE = 1,
-		IGNORE_PUNCTUATION = 2,
-		IGNORE_BLOCKS = 4
-	};
+enum {
+    IGNORE_NONE = 0,
+    IGNORE_WHITESPACE = 1,
+    IGNORE_PUNCTUATION = 2,
+    IGNORE_BLOCKS = 4
+};
 
-	/// Get the length in characters of the longest line in the given text
-	size_t MaxLineLength(std::string const& text, int ignore_mask);
-	/// Get the total number of characters in the string
-	size_t CharacterCount(std::string const& str, int ignore_mask);
-	size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, int ignore_mask);
-	/// Get index in bytes of the nth character in str, or str.size() if str
-	/// has less than n characters
-	size_t IndexOfCharacter(std::string const& str, size_t n);
+/// Get the length in characters of the longest line in the given text
+size_t MaxLineLength(std::string const &text, int ignore_mask);
+/// Get the total number of characters in the string
+size_t CharacterCount(std::string const &str, int ignore_mask);
+size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, int ignore_mask);
+/// Get index in bytes of the nth character in str, or str.size() if str
+/// has less than n characters
+size_t IndexOfCharacter(std::string const &str, size_t n);
 }
diff --git a/libaegisub/include/libaegisub/charset.h b/libaegisub/include/libaegisub/charset.h
index c206e647605c340c3cc33c7a8d8585eaa79670b1..75c610f3d717ef7c569fa9d2182c9b6caa3302be 100644
--- a/libaegisub/include/libaegisub/charset.h
+++ b/libaegisub/include/libaegisub/charset.h
@@ -21,13 +21,13 @@
 #include <string>
 
 namespace agi {
-	/// Character set conversion and detection.
-	namespace charset {
+/// Character set conversion and detection.
+namespace charset {
 
 /// @brief Returns the character set with the highest confidence
 /// @param file File to check
 /// @return Detected character set.
-std::string Detect(agi::fs::path const& file);
+std::string Detect(agi::fs::path const &file);
 
-	} // namespace util
+} // namespace util
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/charset_conv.h b/libaegisub/include/libaegisub/charset_conv.h
index c283c36caa6104e93727890b9032dbf6497b34a9..a2972602d1fdea70f9c7894abe6c957f000cb726 100644
--- a/libaegisub/include/libaegisub/charset_conv.h
+++ b/libaegisub/include/libaegisub/charset_conv.h
@@ -25,7 +25,7 @@
 #include <libaegisub/exception.h>
 
 namespace agi {
-	namespace charset {
+namespace charset {
 
 DEFINE_EXCEPTION(ConvError, Exception);
 DEFINE_EXCEPTION(UnsupportedConversion, ConvError);
@@ -38,75 +38,75 @@ typedef void *iconv_t;
 
 /// RAII handle for iconv
 class Iconv {
-	iconv_t cd;
-	Iconv(Iconv const&) = delete;
-	void operator=(Iconv const&) = delete;
+    iconv_t cd;
+    Iconv(Iconv const &) = delete;
+    void operator=(Iconv const &) = delete;
 
 public:
-	Iconv();
-	Iconv(const char *source, const char *dest);
-	~Iconv();
+    Iconv();
+    Iconv(const char *source, const char *dest);
+    ~Iconv();
 
-	Iconv(Iconv&& o) { std::swap(cd, o.cd); }
-	Iconv& operator=(Iconv&& o) { std::swap(cd, o.cd); return *this; }
+    Iconv(Iconv &&o) { std::swap(cd, o.cd); }
+    Iconv &operator=(Iconv &&o) { std::swap(cd, o.cd); return *this; }
 
-	size_t operator()(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-	operator iconv_t() { return cd; }
+    size_t operator()(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+    operator iconv_t() { return cd; }
 };
 
 /// Helper class that abstracts away the differences between libiconv and
 /// POSIX iconv implementations
 struct Converter {
-	virtual ~Converter() = default;
-	virtual size_t Convert(const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) = 0;
+    virtual ~Converter() = default;
+    virtual size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) = 0;
 };
 
 /// @brief A C++ wrapper for iconv
 class IconvWrapper {
-	size_t toNulLen = 0;
-	size_t fromNulLen = 0;
-	std::unique_ptr<Converter> conv;
+    size_t toNulLen = 0;
+    size_t fromNulLen = 0;
+    std::unique_ptr<Converter> conv;
 
 public:
-	/// @brief Create a converter
-	/// @param sourceEncoding Source encoding name, may be a pretty name
-	/// @param destEncoding   Destination encoding name, may be a pretty name
-	/// @param enableSubst    If true, when possible characters will be
-	///                       mutilated or dropped rather than a letting a
-	///                       conversion fail
-	IconvWrapper(const char* sourceEncoding, const char* destEncoding, bool enableSubst = true);
-	~IconvWrapper();
-
-	/// @brief Convert a string from the source to destination charset
-	/// @param source String to convert
-	/// @return Converted string. Note that std::string always uses a single byte
-	///         terminator, so c_str() may not return a valid string if the dest
-	///         charset has wider terminators
-	std::string Convert(std::string const& source) { return Convert(source.c_str(), source.size()); }
-	std::string Convert(const char *source, size_t len);
-	/// @brief Convert a string from the source to destination charset
-	/// @param source String to convert
-	/// @param[out] dest String to place the result in
-	void Convert(std::string const& source, std::string &dest) { Convert(source.c_str(), source.size(), dest); }
-	void Convert(const char *source, size_t len, std::string &dest);
-	size_t Convert(const char* source, size_t sourceSize, char* dest, size_t destSize);
-	/// Bare wrapper around iconv; see iconv documention for details
-	size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-
-	/// @brief Get the required buffer size required to fit the source string in the target charset
-	/// @param source A string in the source charset
-	/// @param sourceSize Length of the source in bytes
-	/// @return Bytes required, including NUL terminator if applicable
-	size_t RequiredBufferSize(const char* source, size_t sourceSize);
-	/// @brief Get the required buffer size required to fit the source string in the target charset
-	/// @param str A string in the source charset
-	/// @return Bytes required, not including space needed for NUL terminator
-	size_t RequiredBufferSize(std::string const& str);
-
-	/// Encoding-aware strlen for strings encoding in the source charset
-	size_t SrcStrLen(const char* str);
-	/// Encoding-aware strlen for strings encoding in the destination charset
-	size_t DstStrLen(const char* str);
+    /// @brief Create a converter
+    /// @param sourceEncoding Source encoding name, may be a pretty name
+    /// @param destEncoding   Destination encoding name, may be a pretty name
+    /// @param enableSubst    If true, when possible characters will be
+    ///                       mutilated or dropped rather than a letting a
+    ///                       conversion fail
+    IconvWrapper(const char *sourceEncoding, const char *destEncoding, bool enableSubst = true);
+    ~IconvWrapper();
+
+    /// @brief Convert a string from the source to destination charset
+    /// @param source String to convert
+    /// @return Converted string. Note that std::string always uses a single byte
+    ///         terminator, so c_str() may not return a valid string if the dest
+    ///         charset has wider terminators
+    std::string Convert(std::string const &source) { return Convert(source.c_str(), source.size()); }
+    std::string Convert(const char *source, size_t len);
+    /// @brief Convert a string from the source to destination charset
+    /// @param source String to convert
+    /// @param[out] dest String to place the result in
+    void Convert(std::string const &source, std::string &dest) { Convert(source.c_str(), source.size(), dest); }
+    void Convert(const char *source, size_t len, std::string &dest);
+    size_t Convert(const char *source, size_t sourceSize, char *dest, size_t destSize);
+    /// Bare wrapper around iconv; see iconv documention for details
+    size_t Convert(const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
+
+    /// @brief Get the required buffer size required to fit the source string in the target charset
+    /// @param source A string in the source charset
+    /// @param sourceSize Length of the source in bytes
+    /// @return Bytes required, including NUL terminator if applicable
+    size_t RequiredBufferSize(const char *source, size_t sourceSize);
+    /// @brief Get the required buffer size required to fit the source string in the target charset
+    /// @param str A string in the source charset
+    /// @return Bytes required, not including space needed for NUL terminator
+    size_t RequiredBufferSize(std::string const &str);
+
+    /// Encoding-aware strlen for strings encoding in the source charset
+    size_t SrcStrLen(const char *str);
+    /// Encoding-aware strlen for strings encoding in the destination charset
+    size_t DstStrLen(const char *str);
 };
 
 /// Is the conversion from src to dst supported by the linked iconv library?
@@ -117,15 +117,16 @@ bool IsConversionSupported(const char *src, const char *dst);
 
 /// Get a list of supported encodings with user-friendly names
 template<class T>
-T const& GetEncodingsList() {
-	static T name_list;
-	if (name_list.empty()) {
+T const &GetEncodingsList()
+{
+    static T name_list;
+    if (name_list.empty()) {
 #		define ADD(pretty, real) if (IsConversionSupported(real, "utf-8")) name_list.push_back(pretty);
 #		include <libaegisub/charsets.def>
 #		undef ADD
-	}
-	return name_list;
+    }
+    return name_list;
 }
 
-	}
+}
 }
diff --git a/libaegisub/include/libaegisub/charset_conv_win.h b/libaegisub/include/libaegisub/charset_conv_win.h
index ad89cf3d8dc77837884086dceb276808a1ad10b6..519c99c7f8aec9402adeb9a6435526e327bf9376 100644
--- a/libaegisub/include/libaegisub/charset_conv_win.h
+++ b/libaegisub/include/libaegisub/charset_conv_win.h
@@ -19,12 +19,12 @@
 #include <libaegisub/charset_conv.h>
 
 namespace agi {
-	namespace charset {
-		/// 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);
+namespace charset {
+/// 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);
-	}
+/// Convert a UTF-16 string to the local charset
+std::string ConvertLocal(std::wstring const &src);
+}
 }
diff --git a/libaegisub/include/libaegisub/color.h b/libaegisub/include/libaegisub/color.h
index b61b39f20937daae02812a6c2132f01b9860013a..d2d21cc2151cb509b9498a53f0c7541bf0e6875a 100644
--- a/libaegisub/include/libaegisub/color.h
+++ b/libaegisub/include/libaegisub/color.h
@@ -17,25 +17,25 @@
 #include <string>
 
 namespace agi {
-	struct Color {
-		unsigned char r = 0;	///< Red component
-		unsigned char g = 0;	///< Green component
-		unsigned char b = 0;	///< Blue component
-		unsigned char a = 0;	///< Alpha component
+struct Color {
+    unsigned char r = 0;	///< Red component
+    unsigned char g = 0;	///< Green component
+    unsigned char b = 0;	///< Blue component
+    unsigned char a = 0;	///< Alpha component
 
-		Color() = default;
-		Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0);
-		Color(std::string const& str);
+    Color() = default;
+    Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 0);
+    Color(std::string const &str);
 
-		bool operator==(Color const& col) const;
-		bool operator!=(Color const& col) const;
+    bool operator==(Color const &col) const;
+    bool operator!=(Color const &col) const;
 
-		std::string GetAssStyleFormatted() const;
-		std::string GetAssOverrideFormatted() const;
-		std::string GetSsaFormatted() const;
-		std::string GetHexFormatted(bool rgba=false) const;
-		std::string GetRgbFormatted() const;
+    std::string GetAssStyleFormatted() const;
+    std::string GetAssOverrideFormatted() const;
+    std::string GetSsaFormatted() const;
+    std::string GetHexFormatted(bool rgba = false) const;
+    std::string GetRgbFormatted() const;
 
-		operator std::string() const { return GetRgbFormatted(); }
-	};
+    operator std::string() const { return GetRgbFormatted(); }
+};
 }
diff --git a/libaegisub/include/libaegisub/dispatch.h b/libaegisub/include/libaegisub/dispatch.h
index d8be32fe3ddce65404e9a4d64eadb61ebca19f0c..dab511fc81ffb8487aab77efb2682c503b6c693f 100644
--- a/libaegisub/include/libaegisub/dispatch.h
+++ b/libaegisub/include/libaegisub/dispatch.h
@@ -18,33 +18,33 @@
 #include <memory>
 
 namespace agi {
-	namespace dispatch {
-		typedef std::function<void()> Thunk;
+namespace dispatch {
+typedef std::function<void()> Thunk;
 
-		class Queue {
-			virtual void DoInvoke(Thunk thunk)=0;
-		public:
-			virtual ~Queue() { }
+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 immediately
+    void Async(Thunk thunk);
 
-			/// Invoke the thunk on this processing queue, returning only when
-			/// it's complete
-			void Sync(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<void (Thunk)> invoke_main);
+/// Initialize the dispatch thread pools
+/// @param invoke_main A function which invokes the thunk on the GUI thread
+void Init(std::function<void (Thunk)> invoke_main);
 
-		/// Get the main queue, which runs on the GUI thread
-		Queue& 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();
+/// Get the generic background queue, which runs thunks in parallel
+Queue &Background();
 
-		/// Create a new serial queue
-		std::unique_ptr<Queue> Create();
-	}
+/// Create a new serial queue
+std::unique_ptr<Queue> Create();
+}
 }
diff --git a/libaegisub/include/libaegisub/exception.h b/libaegisub/include/libaegisub/exception.h
index 831e4a3713a40b4cb39cce32844d78b84da8f491..66adaecfd92df0fc27bbbd6befa488ca9835b977 100644
--- a/libaegisub/include/libaegisub/exception.h
+++ b/libaegisub/include/libaegisub/exception.h
@@ -32,71 +32,71 @@
 #include <string>
 
 namespace agi {
-	/// @class Exception
-	/// @brief Base class for all exceptions in Aegisub.
-	///
-	/// All exceptions thrown by Aegisub should derive from this class.
-	/// It is incorrect to throw anything that is not a subclass of this.
-	///
-	/// However, there are no public constructors for this class, it should
-	/// not be instantiated and thrown directly. Throw instances of a
-	/// relevant sub class, declare a new one if necessary. It is allowed to
-	/// declare sub classes of Exception and derivates in private headers
-	/// and even inside source files, as long as a caller has a chance to
-	/// catch the exception thrown.
-	///
-	/// When throwing exceptions, throw temporaries, not heap allocated
-	/// objects. (C++ FAQ Lite 17.6.) I.e. this is correct:
-	/// @code
-	/// throw agi::SomeException("Message for exception");
-	/// @endcode
-	/// This is wrong:
-	/// @code
-	/// throw new agi::SomeException("Remember this is the wrong way!");
-	/// @endcode
-	/// Exceptions must not be allocated on heap, because of the risks of
-	/// leaking memory that way. (C++ FAQ Lite 17.8.)
-	///
-	/// When catching exceptions, make sure you catch them by reference,
-	/// otherwise polymorphism will not work. (The C++ Programming
-	/// Language Special Edition 14.2.1, C++ FAQ Lite 17.7.)
-	///
-	/// Catch like this:
-	/// @code
-	/// try {
-	///     /* ... */
-	/// }
-	/// catch (agi::UserCancelException &e) {
-	///     /* handle the fact that the user cancelled */
-	/// }
-	/// catch (agi::VideoInputException &e) {
-	///     /* handle the video provider failing */
-	/// }
-	/// @endcode
-	/// Don't always handle all exceptions the code you're protected might
-	/// throw, sometimes it's better to let an exception slip through and
-	/// let code further out handle it. Sometimes you might want to catch and
-	/// package an exception into something else, for example to represent
-	/// cases such as "subtitle file could not be read @e because the file
-	/// could not be opened for reading". This is the purpose of the "inner"
-	/// exceptions.
-	class Exception {
-		/// The error message
-		std::string message;
+/// @class Exception
+/// @brief Base class for all exceptions in Aegisub.
+///
+/// All exceptions thrown by Aegisub should derive from this class.
+/// It is incorrect to throw anything that is not a subclass of this.
+///
+/// However, there are no public constructors for this class, it should
+/// not be instantiated and thrown directly. Throw instances of a
+/// relevant sub class, declare a new one if necessary. It is allowed to
+/// declare sub classes of Exception and derivates in private headers
+/// and even inside source files, as long as a caller has a chance to
+/// catch the exception thrown.
+///
+/// When throwing exceptions, throw temporaries, not heap allocated
+/// objects. (C++ FAQ Lite 17.6.) I.e. this is correct:
+/// @code
+/// throw agi::SomeException("Message for exception");
+/// @endcode
+/// This is wrong:
+/// @code
+/// throw new agi::SomeException("Remember this is the wrong way!");
+/// @endcode
+/// Exceptions must not be allocated on heap, because of the risks of
+/// leaking memory that way. (C++ FAQ Lite 17.8.)
+///
+/// When catching exceptions, make sure you catch them by reference,
+/// otherwise polymorphism will not work. (The C++ Programming
+/// Language Special Edition 14.2.1, C++ FAQ Lite 17.7.)
+///
+/// Catch like this:
+/// @code
+/// try {
+///     /* ... */
+/// }
+/// catch (agi::UserCancelException &e) {
+///     /* handle the fact that the user cancelled */
+/// }
+/// catch (agi::VideoInputException &e) {
+///     /* handle the video provider failing */
+/// }
+/// @endcode
+/// Don't always handle all exceptions the code you're protected might
+/// throw, sometimes it's better to let an exception slip through and
+/// let code further out handle it. Sometimes you might want to catch and
+/// package an exception into something else, for example to represent
+/// cases such as "subtitle file could not be read @e because the file
+/// could not be opened for reading". This is the purpose of the "inner"
+/// exceptions.
+class Exception {
+    /// The error message
+    std::string message;
 
-	protected:
-		/// @brief Protected constructor initialising members
-		/// @param msg The error message
-		///
-		/// Deriving classes should always use this constructor for initialising
-		/// the base class.
-		Exception(std::string msg) : message(std::move(msg)) { }
+protected:
+    /// @brief Protected constructor initialising members
+    /// @param msg The error message
+    ///
+    /// Deriving classes should always use this constructor for initialising
+    /// the base class.
+    Exception(std::string msg) : message(std::move(msg)) { }
 
-	public:
-		/// @brief Get the outer exception error message
-		/// @return Error message
-		std::string const& GetMessage() const { return message; }
-	};
+public:
+    /// @brief Get the outer exception error message
+    /// @return Error message
+    std::string const &GetMessage() const { return message; }
+};
 
 /// @brief Convenience macro to include the current location in code
 ///
@@ -113,42 +113,42 @@ public:                                                        \
 	classname(std::string msg) : baseclass(std::move(msg)) { } \
 }
 
-	/// @class agi::UserCancelException
-	/// @extends agi::Exception
-	/// @brief Exception for "user cancel" events
-	///
-	/// I.e. when we want to abort an operation because the user requested that we do so.
-	/// Not actually an error and should not be handled as such.
-	///
-	/// This is intended to signal that an operation should be completely aborted at the
-	/// request of the user, and should usually be handled as close to the main UI as
-	/// possible, user cancel exceptions should unwind anything that was going on at the
-	/// moment. For this to work, RAII methodology has to be used consequently in the
-	/// code in question.
-	DEFINE_EXCEPTION(UserCancelException, Exception);
+/// @class agi::UserCancelException
+/// @extends agi::Exception
+/// @brief Exception for "user cancel" events
+///
+/// I.e. when we want to abort an operation because the user requested that we do so.
+/// Not actually an error and should not be handled as such.
+///
+/// This is intended to signal that an operation should be completely aborted at the
+/// request of the user, and should usually be handled as close to the main UI as
+/// possible, user cancel exceptions should unwind anything that was going on at the
+/// moment. For this to work, RAII methodology has to be used consequently in the
+/// code in question.
+DEFINE_EXCEPTION(UserCancelException, Exception);
 
-	/// @class agi::InternalError
-	/// @extends agi:Exception
-	/// @brief Errors that should never happen and point to some invalid assumption in the code
-	///
-	/// Throw an internal error when a sanity check fails, and the insanity should have
-	/// been caught and handled at an earlier stage, i.e. when something seems to
-	/// have become inconsistent. All internal errors are of the type "this should never
-	/// happen", most often you'll want this kind of error unwind all the way past the main UI
-	/// and eventually cause an abort().
-	DEFINE_EXCEPTION(InternalError, Exception);
+/// @class agi::InternalError
+/// @extends agi:Exception
+/// @brief Errors that should never happen and point to some invalid assumption in the code
+///
+/// Throw an internal error when a sanity check fails, and the insanity should have
+/// been caught and handled at an earlier stage, i.e. when something seems to
+/// have become inconsistent. All internal errors are of the type "this should never
+/// happen", most often you'll want this kind of error unwind all the way past the main UI
+/// and eventually cause an abort().
+DEFINE_EXCEPTION(InternalError, Exception);
 
-	/// @class agi::EnvironmentError
-	/// @extends agi:Exception
-	/// @brief The execution environment is broken in some fundamental way
-	///
-	/// 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_EXCEPTION(EnvironmentError, Exception);
+/// @class agi::EnvironmentError
+/// @extends agi:Exception
+/// @brief The execution environment is broken in some fundamental way
+///
+/// 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_EXCEPTION(EnvironmentError, Exception);
 
-	/// @class agi::InvalidInputException
-	/// @extends agi::Exception
-	/// @brief Some input data were invalid and could not be processed
-	DEFINE_EXCEPTION(InvalidInputException, Exception);
+/// @class agi::InvalidInputException
+/// @extends agi::Exception
+/// @brief Some input data were invalid and could not be processed
+DEFINE_EXCEPTION(InvalidInputException, Exception);
 }
diff --git a/libaegisub/include/libaegisub/file_mapping.h b/libaegisub/include/libaegisub/file_mapping.h
index dde241f3820ee52f486c9066e4c1ef716e5e0d22..b5f989bd7e2ecc16e2a05964e3090c968dc684b7 100644
--- a/libaegisub/include/libaegisub/file_mapping.h
+++ b/libaegisub/include/libaegisub/file_mapping.h
@@ -20,47 +20,47 @@
 #include <cstdint>
 
 namespace agi {
-	// boost::interprocess::file_mapping is awesome and uses CreateFileA on Windows
-	class file_mapping {
-		boost::interprocess::file_handle_t handle;
+// boost::interprocess::file_mapping is awesome and uses CreateFileA on Windows
+class file_mapping {
+    boost::interprocess::file_handle_t handle;
 
-	public:
-		file_mapping(fs::path const& filename, bool temporary);
-		~file_mapping();
-		boost::interprocess::mapping_handle_t get_mapping_handle() const {
-			return boost::interprocess::ipcdetail::mapping_handle_from_file_handle(handle);
-		}
-	};
+public:
+    file_mapping(fs::path const &filename, bool temporary);
+    ~file_mapping();
+    boost::interprocess::mapping_handle_t get_mapping_handle() const {
+        return boost::interprocess::ipcdetail::mapping_handle_from_file_handle(handle);
+    }
+};
 
-	class read_file_mapping {
-		file_mapping file;
-		std::unique_ptr<boost::interprocess::mapped_region> region;
-		uint64_t mapping_start = 0;
-		uint64_t file_size = 0;
+class read_file_mapping {
+    file_mapping file;
+    std::unique_ptr<boost::interprocess::mapped_region> region;
+    uint64_t mapping_start = 0;
+    uint64_t file_size = 0;
 
-	public:
-		read_file_mapping(fs::path const& filename);
-		~read_file_mapping();
+public:
+    read_file_mapping(fs::path const &filename);
+    ~read_file_mapping();
 
-		uint64_t size() const { return file_size; }
-		const char *read(int64_t offset, uint64_t length);
-		const char *read(); // Map the entire file
-	};
+    uint64_t size() const { return file_size; }
+    const char *read(int64_t offset, uint64_t length);
+    const char *read(); // Map the entire file
+};
 
-	class temp_file_mapping {
-		file_mapping file;
-		uint64_t file_size = 0;
+class temp_file_mapping {
+    file_mapping file;
+    uint64_t file_size = 0;
 
-		std::unique_ptr<boost::interprocess::mapped_region> read_region;
-		uint64_t read_mapping_start = 0;
-		std::unique_ptr<boost::interprocess::mapped_region> write_region;
-		uint64_t write_mapping_start = 0;
+    std::unique_ptr<boost::interprocess::mapped_region> read_region;
+    uint64_t read_mapping_start = 0;
+    std::unique_ptr<boost::interprocess::mapped_region> write_region;
+    uint64_t write_mapping_start = 0;
 
-	public:
-		temp_file_mapping(fs::path const& filename, uint64_t size);
-		~temp_file_mapping();
+public:
+    temp_file_mapping(fs::path const &filename, uint64_t size);
+    ~temp_file_mapping();
 
-		const char *read(int64_t offset, uint64_t length);
-		char *write(int64_t offset, uint64_t length);
-	};
+    const char *read(int64_t offset, uint64_t length);
+    char *write(int64_t offset, uint64_t length);
+};
 }
diff --git a/libaegisub/include/libaegisub/format.h b/libaegisub/include/libaegisub/format.h
index b1ca504786445bcd1df0b218aeed980f372f0864..43bf94064ee10615a32f32cf271bc0209f9344d3 100644
--- a/libaegisub/include/libaegisub/format.h
+++ b/libaegisub/include/libaegisub/format.h
@@ -27,43 +27,45 @@ extern template class boost::interprocess::basic_vectorstream<std::wstring>;
 extern template class boost::interprocess::basic_vectorbuf<std::string>;
 extern template class boost::interprocess::basic_vectorbuf<std::wstring>;
 
-namespace agi { namespace format_detail {
+namespace agi {
+namespace format_detail {
 // A static cast which throws at runtime if the cast is invalid rather than
 // failing to compile, as with format strings we don't know what type to cast
 // to at compile time.
 template<typename In, typename Out, bool = std::is_convertible<In, Out>::value>
 struct runtime_cast_helper {
-	static Out cast(In const&) { throw std::bad_cast(); }
+    static Out cast(In const &) { throw std::bad_cast(); }
 };
 
 template<typename In, typename Out>
 struct runtime_cast_helper<In, Out, true> {
-	static Out cast(In const& value) {
-		return static_cast<Out>(value);
-	}
+    static Out cast(In const &value) {
+        return static_cast<Out>(value);
+    }
 };
 
 template<typename Out, typename In>
-Out runtime_cast(In const& value) {
-	return runtime_cast_helper<In, Out>::cast(value);
+Out runtime_cast(In const &value)
+{
+    return runtime_cast_helper<In, Out>::cast(value);
 }
 }
 
 template<typename Char, typename T>
 struct writer {
-	static void write(std::basic_ostream<Char>& out, int, T const& value) {
-		out << value;
-	}
+    static void write(std::basic_ostream<Char> &out, int, T const &value) {
+        out << value;
+    }
 };
 
 template<typename StreamChar, typename Char>
 struct writer<StreamChar, const Char *> {
-	static void write(std::basic_ostream<StreamChar>& out, int max_len, const Char *value);
+    static void write(std::basic_ostream<StreamChar> &out, int max_len, const Char *value);
 };
 
 template<typename StreamChar, typename Char>
 struct writer<StreamChar, std::basic_string<Char>> {
-	static void write(std::basic_ostream<StreamChar>& out, int max_len, std::basic_string<Char> const& value);
+    static void write(std::basic_ostream<StreamChar> &out, int max_len, std::basic_string<Char> const &value);
 };
 
 // Ensure things with specializations don't get implicitly initialized
@@ -75,110 +77,113 @@ template<> struct writer<wchar_t, wxString>;
 namespace format_detail {
 template<typename Char>
 struct formatter_state {
-	std::basic_ostream<Char>& out;
+    std::basic_ostream<Char> &out;
 
-	const Char *fmt;
-	const Char *fmt_cur = nullptr;
+    const Char *fmt;
+    const Char *fmt_cur = nullptr;
 
-	bool read_width = false;
-	bool read_precision = false;
-	bool pending = false;
+    bool read_width = false;
+    bool read_precision = false;
+    bool pending = false;
 
-	int width = 0;
-	int precision = 0;
+    int width = 0;
+    int precision = 0;
 
-	formatter_state(std::basic_ostream<Char>&out , const Char *fmt)
-	: out(out), fmt(fmt) { }
+    formatter_state(std::basic_ostream<Char> &out, const Char *fmt)
+        : out(out), fmt(fmt) { }
 };
 
 template<typename Char>
 class formatter : formatter_state<Char> {
-	formatter(const formatter&) = delete;
-	formatter& operator=(const formatter&) = delete;
+    formatter(const formatter &) = delete;
+    formatter &operator=(const formatter &) = delete;
 
-	boost::io::basic_ios_all_saver<Char> saver;
+    boost::io::basic_ios_all_saver<Char> saver;
 
-	bool parse_next();
-	Char next_format();
+    bool parse_next();
+    Char next_format();
 
 public:
-	formatter(std::basic_ostream<Char>& out, const Char *fmt)
-	: formatter_state<Char>(out, fmt), saver(out) { }
-	~formatter();
-
-	template<typename T>
-	void operator()(T&& value) {
-		if (!this->pending && !parse_next()) return;
-
-		if (this->read_width) {
-			this->width = runtime_cast<int>(value);
-			this->read_width = false;
-			return;
-		}
-
-		if (this->read_precision) {
-			this->precision = runtime_cast<int>(value);
-			this->read_precision = false;
-			return;
-		}
-
-		Char c = next_format();
-
-		switch (c) {
-		case 'c':
-			this->out << runtime_cast<Char>(value);
-			break;
-		case 'd': case 'i':
-			this->out << runtime_cast<intmax_t>(value);
-			break;
-		case 'o':
-			this->out << runtime_cast<intmax_t>(value);
-			break;
-		case 'x':
-			this->out << runtime_cast<intmax_t>(value);
-			break;
-		case 'u':
-			this->out << runtime_cast<uintmax_t>(value);
-			break;
-		case 'e':
-			this->out << runtime_cast<double>(value);
-			break;
-		case 'f':
-			this->out << runtime_cast<double>(value);
-			break;
-		case 'g':
-			this->out << runtime_cast<double>(value);
-			break;
-		case 'p':
-			this->out << runtime_cast<const void *>(value);
-			break;
-		default: // s and other
-			writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value);
-			break;
-		}
-	}
+    formatter(std::basic_ostream<Char> &out, const Char *fmt)
+        : formatter_state<Char>(out, fmt), saver(out) { }
+    ~formatter();
+
+    template<typename T>
+    void operator()(T &&value) {
+        if (!this->pending && !parse_next()) return;
+
+        if (this->read_width) {
+            this->width = runtime_cast<int>(value);
+            this->read_width = false;
+            return;
+        }
+
+        if (this->read_precision) {
+            this->precision = runtime_cast<int>(value);
+            this->read_precision = false;
+            return;
+        }
+
+        Char c = next_format();
+
+        switch (c) {
+        case 'c':
+            this->out << runtime_cast<Char>(value);
+            break;
+        case 'd': case 'i':
+            this->out << runtime_cast<intmax_t>(value);
+            break;
+        case 'o':
+            this->out << runtime_cast<intmax_t>(value);
+            break;
+        case 'x':
+            this->out << runtime_cast<intmax_t>(value);
+            break;
+        case 'u':
+            this->out << runtime_cast<uintmax_t>(value);
+            break;
+        case 'e':
+            this->out << runtime_cast<double>(value);
+            break;
+        case 'f':
+            this->out << runtime_cast<double>(value);
+            break;
+        case 'g':
+            this->out << runtime_cast<double>(value);
+            break;
+        case 'p':
+            this->out << runtime_cast<const void *>(value);
+            break;
+        default: // s and other
+            writer<Char, typename std::decay<T>::type>::write(this->out, this->precision, value);
+            break;
+        }
+    }
 };
 
 // Base case for variadic template recursion
 template<typename Char>
-inline void format(formatter<Char>&&) { }
+inline void format(formatter<Char> &&) { }
 
 template<typename Char, typename T, typename... Args>
-void format(formatter<Char>&& fmt, T&& first, Args&&... rest) {
-	fmt(first);
-	format(std::move(fmt), std::forward<Args>(rest)...);
+void format(formatter<Char> &&fmt, T &&first, Args &&... rest)
+{
+    fmt(first);
+    format(std::move(fmt), std::forward<Args>(rest)...);
 }
 } // namespace format_detail
 
 template<typename Char, typename... Args>
-void format(std::basic_ostream<Char>& out, const Char *fmt, Args&&... args) {
-	format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...);
+void format(std::basic_ostream<Char> &out, const Char *fmt, Args &&... args)
+{
+    format(format_detail::formatter<Char>(out, fmt), std::forward<Args>(args)...);
 }
 
 template<typename Char, typename... Args>
-std::basic_string<Char> format(const Char *fmt, Args&&... args) {
-	boost::interprocess::basic_vectorstream<std::basic_string<Char>> out;
-	format(out, fmt, std::forward<Args>(args)...);
-	return out.vector();
+std::basic_string<Char> format(const Char *fmt, Args &&... args)
+{
+    boost::interprocess::basic_vectorstream<std::basic_string<Char>> out;
+    format(out, fmt, std::forward<Args>(args)...);
+    return out.vector();
 }
 }
diff --git a/libaegisub/include/libaegisub/format_flyweight.h b/libaegisub/include/libaegisub/format_flyweight.h
index aa07061272fee2ab4c53ebf3a68a676b7630144f..6bec619396bb84905cae5b55cb4721d82d4e7cc8 100644
--- a/libaegisub/include/libaegisub/format_flyweight.h
+++ b/libaegisub/include/libaegisub/format_flyweight.h
@@ -21,16 +21,16 @@
 namespace agi {
 template<>
 struct writer<char, boost::flyweight<std::string>> {
-	static void write(std::basic_ostream<char>& out, int max_len, boost::flyweight<std::string> const& value) {
-		writer<char, std::string>::write(out, max_len, value.get());
-	}
+    static void write(std::basic_ostream<char> &out, int max_len, boost::flyweight<std::string> const &value) {
+        writer<char, std::string>::write(out, max_len, value.get());
+    }
 };
 
 template<>
 struct writer<wchar_t, boost::flyweight<std::string>> {
-	static void write(std::basic_ostream<wchar_t>& out, int max_len, boost::flyweight<std::string> const& value) {
-		writer<wchar_t, std::string>::write(out, max_len, value.get());
-	}
+    static void write(std::basic_ostream<wchar_t> &out, int max_len, boost::flyweight<std::string> const &value) {
+        writer<wchar_t, std::string>::write(out, max_len, value.get());
+    }
 };
 }
 
diff --git a/libaegisub/include/libaegisub/format_path.h b/libaegisub/include/libaegisub/format_path.h
index ff195ece6de4784d351f5c4d65138191fb282cbe..b3a937de58e7102fe089eebb8b1c0fd8b6df70b7 100644
--- a/libaegisub/include/libaegisub/format_path.h
+++ b/libaegisub/include/libaegisub/format_path.h
@@ -22,15 +22,15 @@ namespace agi {
 // Default version quotes the path
 template<>
 struct writer<char, agi::fs::path> {
-	static void write(std::basic_ostream<char>& out, int max_len, agi::fs::path const& value) {
-		out << value.string();
-	}
+    static void write(std::basic_ostream<char> &out, int max_len, agi::fs::path const &value) {
+        out << value.string();
+    }
 };
 
 template<>
 struct writer<wchar_t, agi::fs::path> {
-	static void write(std::basic_ostream<wchar_t>& out, int max_len, agi::fs::path const& value) {
-		out << value.wstring();
-	}
+    static void write(std::basic_ostream<wchar_t> &out, int max_len, agi::fs::path const &value) {
+        out << value.wstring();
+    }
 };
 }
diff --git a/libaegisub/include/libaegisub/fs.h b/libaegisub/include/libaegisub/fs.h
index e09711e931043e4d3f0b578256d45d6c09ec2d6e..7b70f1a02b3ba6d283b028ec23c69960e270f90c 100644
--- a/libaegisub/include/libaegisub/fs.h
+++ b/libaegisub/include/libaegisub/fs.h
@@ -27,8 +27,8 @@
 #undef CreateDirectory
 
 namespace agi {
-	namespace fs {
-		/// Define a filesystem error which takes a path or a string
+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()) { } \
@@ -37,141 +37,142 @@ namespace agi {
 			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_EXCEPTION(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_EXCEPTION(FileSystemUnknownError, FileSystemError);;
-
-		/// 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> 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<typename T> void GetAll(T& cont);
-		};
-
-		static inline DirectoryIterator& begin(DirectoryIterator &it) { return it; }
-		static inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); }
-
-		template<typename T>
-		inline void DirectoryIterator::GetAll(T& cont) {
-			copy(*this, end(*this), std::back_inserter(cont));
-		}
-	}
+/// @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_EXCEPTION(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_EXCEPTION(FileSystemUnknownError, FileSystemError);;
+
+/// 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> 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<typename T> void GetAll(T &cont);
+};
+
+static inline DirectoryIterator &begin(DirectoryIterator &it) { return it; }
+static inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); }
+
+template<typename T>
+inline void DirectoryIterator::GetAll(T &cont)
+{
+    copy(*this, end(*this), std::back_inserter(cont));
+}
+}
 }
diff --git a/libaegisub/include/libaegisub/hotkey.h b/libaegisub/include/libaegisub/hotkey.h
index 81cae91fb4ce8bcd53af38b166638e837d15e285..47dfd89782a1b2eeca5c4a2e9cc579770b9af4ee 100644
--- a/libaegisub/include/libaegisub/hotkey.h
+++ b/libaegisub/include/libaegisub/hotkey.h
@@ -21,105 +21,104 @@
 #include <libaegisub/signal.h>
 
 namespace json {
-	class UnknownElement;
-	typedef std::map<std::string, UnknownElement> Object;
+class UnknownElement;
+typedef std::map<std::string, UnknownElement> Object;
 }
 
 namespace agi {
-	namespace hotkey {
+namespace hotkey {
 
 /// @class Combo
 /// A Combo represents a linear sequence of characters set in an std::vector.
 /// This makes up a single combination, or "Hotkey".
 class Combo {
-	std::string keys;
-	std::string cmd_name;
-	std::string context;
+    std::string keys;
+    std::string cmd_name;
+    std::string context;
 public:
-	/// Constructor
-	/// @param ctx Context
-	/// @param cmd Command name
-	Combo(std::string ctx, std::string cmd, std::string keys)
-	: keys(std::move(keys))
-	, cmd_name(std::move(cmd))
-	, context(std::move(ctx))
-	{
-	}
-
-	/// String representation of the Combo
-	std::string const& Str() const { return keys; }
-
-	/// Command name triggered by the combination.
-	/// @return Command name
-	std::string const& CmdName() const { return cmd_name; }
-
-	/// Context this Combo is triggered in.
-	std::string const& Context() const { return context; }
+    /// Constructor
+    /// @param ctx Context
+    /// @param cmd Command name
+    Combo(std::string ctx, std::string cmd, std::string keys)
+        : keys(std::move(keys))
+        , cmd_name(std::move(cmd))
+        , context(std::move(ctx)) {
+    }
+
+    /// String representation of the Combo
+    std::string const &Str() const { return keys; }
+
+    /// Command name triggered by the combination.
+    /// @return Command name
+    std::string const &CmdName() const { return cmd_name; }
+
+    /// Context this Combo is triggered in.
+    std::string const &Context() const { return context; }
 };
 
 /// @class Hotkey
 /// Holds the map of Combo instances and handles searching for matching key sequences.
 class Hotkey {
 public:
-	/// Map to hold Combo instances
-	typedef std::multimap<std::string, Combo> HotkeyMap;
+    /// Map to hold Combo instances
+    typedef std::multimap<std::string, Combo> HotkeyMap;
 private:
-	HotkeyMap cmd_map;                  ///< Command name -> Combo
-	std::vector<const Combo *> str_map; ///< Sorted by string representation
-	const agi::fs::path config_file;    ///< Default user config location.
-	bool backup_config_file = false;
+    HotkeyMap cmd_map;                  ///< Command name -> Combo
+    std::vector<const Combo *> str_map; ///< Sorted by string representation
+    const agi::fs::path config_file;    ///< Default user config location.
+    bool backup_config_file = false;
 
-	/// Build hotkey map.
-	/// @param context Context being parsed.
-	/// @param object  json::Object holding items for context being parsed.
-	void BuildHotkey(std::string const& context, const json::Object& object);
+    /// Build hotkey map.
+    /// @param context Context being parsed.
+    /// @param object  json::Object holding items for context being parsed.
+    void BuildHotkey(std::string const &context, const json::Object &object);
 
-	/// Write active Hotkey configuration to disk.
-	void Flush();
+    /// Write active Hotkey configuration to disk.
+    void Flush();
 
-	void UpdateStrMap();
+    void UpdateStrMap();
 
-	/// Announce that the loaded hotkeys have been changed
-	agi::signal::Signal<> HotkeysChanged;
+    /// Announce that the loaded hotkeys have been changed
+    agi::signal::Signal<> HotkeysChanged;
 public:
-	/// Constructor
-	/// @param file           Location of user config file.
-	/// @param default_config Default config.
-	Hotkey(agi::fs::path const& file, std::pair<const char *, size_t> default_config);
-
-	template<size_t N>
-	Hotkey(agi::fs::path const& file, const char (&default_config)[N])
-	: Hotkey(file, {default_config, N - 1}) { }
-
-	/// Scan for a matching key.
-	/// @param context  Context requested.
-	/// @param str      Hyphen separated key sequence.
-	/// @param always   Enable the "Always" override context
-	/// @return Name of command or "" if none match
-	std::string Scan(const std::string &context, const std::string &str, bool always) const;
-
-	bool HasHotkey(const std::string &context, const std::string &str) const;
-
-	/// Get the string representation of the hotkeys for the given command
-	/// @param context Context requested
-	/// @param command Command name
-	/// @return A vector of all hotkeys for that command in the context
-	std::vector<std::string> GetHotkeys(const std::string &context, const std::string &command) const;
-
-	/// Get a string representation of a hotkeys for the given command
-	/// @param context Context requested
-	/// @param command Command name
-	/// @return A hotkey for the given command or "" if there are none
-	std::string GetHotkey(const std::string &context, const std::string &command) const;
-
-	/// Get the raw command name -> combo map for all registered hotkeys
-	HotkeyMap const& GetHotkeyMap() const { return cmd_map; }
-
-	/// Replace the loaded hotkeys with a new set
-	void SetHotkeyMap(HotkeyMap new_map);
-
-	DEFINE_SIGNAL_ADDERS(HotkeysChanged, AddHotkeyChangeListener)
+    /// Constructor
+    /// @param file           Location of user config file.
+    /// @param default_config Default config.
+    Hotkey(agi::fs::path const &file, std::pair<const char *, size_t> default_config);
+
+    template<size_t N>
+    Hotkey(agi::fs::path const &file, const char (&default_config)[N])
+        : Hotkey(file, {default_config, N - 1}) { }
+
+    /// Scan for a matching key.
+    /// @param context  Context requested.
+    /// @param str      Hyphen separated key sequence.
+    /// @param always   Enable the "Always" override context
+    /// @return Name of command or "" if none match
+    std::string Scan(const std::string &context, const std::string &str, bool always) const;
+
+    bool HasHotkey(const std::string &context, const std::string &str) const;
+
+    /// Get the string representation of the hotkeys for the given command
+    /// @param context Context requested
+    /// @param command Command name
+    /// @return A vector of all hotkeys for that command in the context
+    std::vector<std::string> GetHotkeys(const std::string &context, const std::string &command) const;
+
+    /// Get a string representation of a hotkeys for the given command
+    /// @param context Context requested
+    /// @param command Command name
+    /// @return A hotkey for the given command or "" if there are none
+    std::string GetHotkey(const std::string &context, const std::string &command) const;
+
+    /// Get the raw command name -> combo map for all registered hotkeys
+    HotkeyMap const &GetHotkeyMap() const { return cmd_map; }
+
+    /// Replace the loaded hotkeys with a new set
+    void SetHotkeyMap(HotkeyMap new_map);
+
+    DEFINE_SIGNAL_ADDERS(HotkeysChanged, AddHotkeyChangeListener)
 };
 
-	} // namespace hotkey
+} // namespace hotkey
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/io.h b/libaegisub/include/libaegisub/io.h
index 5ab4f4af3f8e2cdc2e8f42d85a51d42970103d87..b141e77a73b090ed8f15dfde8fbd7a744a91194b 100644
--- a/libaegisub/include/libaegisub/io.h
+++ b/libaegisub/include/libaegisub/io.h
@@ -24,23 +24,23 @@
 #include <memory>
 
 namespace agi {
-	namespace io {
+namespace io {
 
 DEFINE_EXCEPTION(IOError, Exception);
 DEFINE_EXCEPTION(IOFatal, IOError);
 
-std::unique_ptr<std::istream> Open(fs::path const& file, bool binary = false);
+std::unique_ptr<std::istream> Open(fs::path const &file, bool binary = false);
 
 class Save {
-	std::unique_ptr<std::ostream> fp;
-	const fs::path file_name;
-	const fs::path tmp_name;
+    std::unique_ptr<std::ostream> fp;
+    const fs::path file_name;
+    const fs::path tmp_name;
 
 public:
-	Save(fs::path const& file, bool binary = false);
-	~Save() noexcept(false);
-	std::ostream& Get() { return *fp; }
+    Save(fs::path const &file, bool binary = false);
+    ~Save() noexcept(false);
+    std::ostream &Get() { return *fp; }
 };
 
-	} // namespace io
+} // namespace io
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/json.h b/libaegisub/include/libaegisub/json.h
index 4862cd0c150a822205ffd2846121207840862a6a..57a56a10965a7a9ab9fdc8d136bb8fa04e3fadc6 100644
--- a/libaegisub/include/libaegisub/json.h
+++ b/libaegisub/include/libaegisub/json.h
@@ -19,7 +19,8 @@
 #include <libaegisub/cajun/elements.h>
 #include <libaegisub/fs_fwd.h>
 
-namespace agi { namespace json_util {
+namespace agi {
+namespace json_util {
 
 /// Parse a JSON stream.
 /// @param stream JSON stream to parse
@@ -30,6 +31,7 @@ json::UnknownElement parse(std::istream &stream);
 /// @param file Path to JSON file.
 /// @param Default config file to load incase of nonexistent file
 /// @return json::UnknownElement
-json::UnknownElement file(agi::fs::path const& file, std::pair<const char *, size_t> default_config);
+json::UnknownElement file(agi::fs::path const &file, std::pair<const char *, size_t> default_config);
 
-} }
+}
+}
diff --git a/libaegisub/include/libaegisub/kana_table.h b/libaegisub/include/libaegisub/kana_table.h
index 1a25c098f4d8aea87dde5b171ae6ce0cc68da151..61418274fb0836e00b65621cfd9f4913bd5fa731 100644
--- a/libaegisub/include/libaegisub/kana_table.h
+++ b/libaegisub/include/libaegisub/kana_table.h
@@ -19,13 +19,13 @@
 #include <vector>
 
 namespace agi {
-	struct kana_pair {
-		const char *kana;
-		const char *romaji;
-	};
+struct kana_pair {
+    const char *kana;
+    const char *romaji;
+};
 
-	/// Transliterated romaji for the given kana, or nullptr if not applicable
-	std::vector<const char *> kana_to_romaji(std::string const& kana);
+/// Transliterated romaji for the given kana, or nullptr if not applicable
+std::vector<const char *> kana_to_romaji(std::string const &kana);
 
-	boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const& romaji);
+boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const &romaji);
 }
diff --git a/libaegisub/include/libaegisub/karaoke_matcher.h b/libaegisub/include/libaegisub/karaoke_matcher.h
index 527cac18f5f619c298a9026622c2245f064053e8..08bf1f68671a5a7eb3614dbaa23e5fcfa59f9004 100644
--- a/libaegisub/include/libaegisub/karaoke_matcher.h
+++ b/libaegisub/include/libaegisub/karaoke_matcher.h
@@ -18,13 +18,13 @@
 #include <vector>
 
 namespace agi {
-	struct karaoke_match_result {
-		/// The number of strings in the source matched
-		size_t source_length;
-		/// The number of characters in the destination string matched
-		size_t destination_length;
-	};
+struct karaoke_match_result {
+    /// The number of strings in the source matched
+    size_t source_length;
+    /// The number of characters in the destination string matched
+    size_t destination_length;
+};
 
-	/// Try to automatically select the portion of dst which corresponds to the first string in src
-	karaoke_match_result auto_match_karaoke(std::vector<std::string> const& src, std::string const& dst);
+/// Try to automatically select the portion of dst which corresponds to the first string in src
+karaoke_match_result auto_match_karaoke(std::vector<std::string> const &src, std::string const &dst);
 }
diff --git a/libaegisub/include/libaegisub/keyframe.h b/libaegisub/include/libaegisub/keyframe.h
index 6fc82ff49df6550870c315aa9a8bad3ab55cf8b8..2f3095b293198a82f1c148394106a5e5da6419a9 100644
--- a/libaegisub/include/libaegisub/keyframe.h
+++ b/libaegisub/include/libaegisub/keyframe.h
@@ -18,18 +18,18 @@
 #include <vector>
 
 namespace agi {
-	namespace keyframe {
-		/// @brief Load a keyframe file
-		/// @param filename File to load
-		/// @return List of frame numbers which are keyframes
-		std::vector<int> Load(agi::fs::path const& filename);
+namespace keyframe {
+/// @brief Load a keyframe file
+/// @param filename File to load
+/// @return List of frame numbers which are keyframes
+std::vector<int> 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(agi::fs::path const& filename, std::vector<int> const& keyframes);
+/// @brief Save keyframes to a file
+/// @param filename File to save to
+/// @param keyframes List of keyframes to save
+void Save(agi::fs::path const &filename, std::vector<int> const &keyframes);
 
-		DEFINE_EXCEPTION(KeyframeFormatParseError, agi::InvalidInputException);
-		DEFINE_EXCEPTION(UnknownKeyframeFormatError, agi::InvalidInputException);
-	}
+DEFINE_EXCEPTION(KeyframeFormatParseError, agi::InvalidInputException);
+DEFINE_EXCEPTION(UnknownKeyframeFormatError, agi::InvalidInputException);
+}
 }
diff --git a/libaegisub/include/libaegisub/line_iterator.h b/libaegisub/include/libaegisub/line_iterator.h
index 1e4fee9b15e69346dc86e849aa4c60867eea2d67..955cf3bf0a2fdd5825894c863d3be4dd68e8efce 100644
--- a/libaegisub/include/libaegisub/line_iterator.h
+++ b/libaegisub/include/libaegisub/line_iterator.h
@@ -29,111 +29,113 @@ namespace agi {
 namespace charset { class IconvWrapper; }
 
 class line_iterator_base {
-	std::istream *stream = nullptr; ///< Stream to iterate over
-	std::shared_ptr<agi::charset::IconvWrapper> conv;
-	int cr = '\r'; ///< CR character in the source encoding
-	int lf = '\n'; ///< LF character in the source encoding
-	size_t width = 1;  ///< width of LF character in the source encoding
+    std::istream *stream = nullptr; ///< Stream to iterate over
+    std::shared_ptr<agi::charset::IconvWrapper> conv;
+    int cr = '\r'; ///< CR character in the source encoding
+    int lf = '\n'; ///< LF character in the source encoding
+    size_t width = 1;  ///< width of LF character in the source encoding
 
 protected:
-	bool getline(std::string &str);
+    bool getline(std::string &str);
 
 public:
-	line_iterator_base(std::istream &stream, std::string encoding = "utf-8");
+    line_iterator_base(std::istream &stream, std::string encoding = "utf-8");
 
-	line_iterator_base() = default;
-	line_iterator_base(line_iterator_base const&) = default;
-	line_iterator_base(line_iterator_base&&) = default;
+    line_iterator_base() = default;
+    line_iterator_base(line_iterator_base const &) = default;
+    line_iterator_base(line_iterator_base &&) = default;
 
-	line_iterator_base& operator=(line_iterator_base const&) = default;
-	line_iterator_base& operator=(line_iterator_base&&) = default;
+    line_iterator_base &operator=(line_iterator_base const &) = default;
+    line_iterator_base &operator=(line_iterator_base &&) = default;
 
-	bool operator==(line_iterator_base const& rgt) const { return stream == rgt.stream; }
-	bool operator!=(line_iterator_base const& rgt) const { return !operator==(rgt); }
+    bool operator==(line_iterator_base const &rgt) const { return stream == rgt.stream; }
+    bool operator!=(line_iterator_base const &rgt) const { return !operator==(rgt); }
 };
 
 /// @class line_iterator
 /// @brief An iterator over lines in a stream
 template<class OutputType = std::string>
 class line_iterator final : public line_iterator_base, public std::iterator<std::input_iterator_tag, OutputType> {
-	OutputType value; ///< Value to return when this is dereference
-
-	/// @brief Convert a string to the output type
-	/// @param str Line read from the file
-	///
-	/// line_iterator users can either ensure that operator>> is defined for
-	/// their desired output type or simply provide a specialization of this
-	/// method which does the conversion.
-	inline bool convert(std::string &str);
-
-	/// @brief Get the next value from the stream
-	void next();
+    OutputType value; ///< Value to return when this is dereference
+
+    /// @brief Convert a string to the output type
+    /// @param str Line read from the file
+    ///
+    /// line_iterator users can either ensure that operator>> is defined for
+    /// their desired output type or simply provide a specialization of this
+    /// method which does the conversion.
+    inline bool convert(std::string &str);
+
+    /// @brief Get the next value from the stream
+    void next();
 public:
-	/// @brief Constructor
-	/// @param stream The stream to read from. The calling code is responsible
-	///               for ensuring that the stream remains valid for the
-	///               lifetime of the iterator and that it get cleaned up.
-	/// @param encoding Encoding of the text read from the stream
-	line_iterator(std::istream &stream, std::string encoding = "utf-8")
-	: line_iterator_base(stream, std::move(encoding))
-	{
-		++(*this);
-	}
-
-	/// @brief Invalid iterator constructor; use for end iterator
-	line_iterator() = default;
-
-	/// @brief Copy constructor
-	/// @param that line_iterator to copy from
-	line_iterator(line_iterator<OutputType> const&) = default;
-
-	OutputType const& operator*() const { return value; }
-	OutputType const* operator->() const { return &value; }
-
-	line_iterator<OutputType>& operator++() {
-		next();
-		return *this;
-	}
-	line_iterator<OutputType> operator++(int) {
-		line_iterator<OutputType> tmp(*this);
-		++*this;
-		return tmp;
-	}
-
-	// typedefs needed by some stl algorithms
-	typedef OutputType* pointer;
-	typedef OutputType& reference;
-	typedef const OutputType* const_pointer;
-	typedef const OutputType& const_reference;
+    /// @brief Constructor
+    /// @param stream The stream to read from. The calling code is responsible
+    ///               for ensuring that the stream remains valid for the
+    ///               lifetime of the iterator and that it get cleaned up.
+    /// @param encoding Encoding of the text read from the stream
+    line_iterator(std::istream &stream, std::string encoding = "utf-8")
+        : line_iterator_base(stream, std::move(encoding)) {
+        ++(*this);
+    }
+
+    /// @brief Invalid iterator constructor; use for end iterator
+    line_iterator() = default;
+
+    /// @brief Copy constructor
+    /// @param that line_iterator to copy from
+    line_iterator(line_iterator<OutputType> const &) = default;
+
+    OutputType const &operator*() const { return value; }
+    OutputType const *operator->() const { return &value; }
+
+    line_iterator<OutputType> &operator++() {
+        next();
+        return *this;
+    }
+    line_iterator<OutputType> operator++(int) {
+        line_iterator<OutputType> tmp(*this);
+        ++*this;
+        return tmp;
+    }
+
+    // typedefs needed by some stl algorithms
+    typedef OutputType *pointer;
+    typedef OutputType &reference;
+    typedef const OutputType *const_pointer;
+    typedef const OutputType &const_reference;
 };
 
 // Enable range-based for
 template<typename T>
-line_iterator<T>& begin(line_iterator<T>& it) { return it; }
+line_iterator<T> &begin(line_iterator<T> &it) { return it; }
 
 template<typename T>
-line_iterator<T> end(line_iterator<T>&) { return agi::line_iterator<T>(); }
+line_iterator<T> end(line_iterator<T> &) { return agi::line_iterator<T>(); }
 
 template<class OutputType>
-void line_iterator<OutputType>::next() {
-	std::string str;
-	if (!getline(str))
-		return;
-	if (!convert(str))
-		next();
+void line_iterator<OutputType>::next()
+{
+    std::string str;
+    if (!getline(str))
+        return;
+    if (!convert(str))
+        next();
 }
 
 template<>
-inline void line_iterator<std::string>::next() {
-	value.clear();
-	getline(value);
+inline void line_iterator<std::string>::next()
+{
+    value.clear();
+    getline(value);
 }
 
 template<class OutputType>
-inline bool line_iterator<OutputType>::convert(std::string &str) {
-	boost::interprocess::ibufferstream ss(str.data(), str.size());
-	ss >> value;
-	return !ss.fail();
+inline bool line_iterator<OutputType>::convert(std::string &str)
+{
+    boost::interprocess::ibufferstream ss(str.data(), str.size());
+    ss >> value;
+    return !ss.fail();
 }
 
 }
diff --git a/libaegisub/include/libaegisub/line_wrap.h b/libaegisub/include/libaegisub/line_wrap.h
index a5f7311c87ecfd38ba5e8dc1d0f0cf679c1550b0..24ab1deac00dd5f7cbec5d5c5627677bad4e0001 100644
--- a/libaegisub/include/libaegisub/line_wrap.h
+++ b/libaegisub/include/libaegisub/line_wrap.h
@@ -21,149 +21,154 @@
 #include <vector>
 
 namespace agi {
-	enum WrapMode {
-		/// Semi-balanced, with the first line guaranteed to be longest if possible
-		Wrap_Balanced_FirstLonger = 0,
-		/// Simple greedy matching with breaks as late as possible
-		Wrap_Greedy = 1,
-		/// No line breaking at all
-		Wrap_None = 2,
-		/// Semi-balanced, with the last line guaranteed to be longest if possible
-		Wrap_Balanced_LastLonger = 3,
-		/// Balanced, with lines as close to equal in length as possible
-		Wrap_Balanced = 4
-	};
-
-	namespace line_wrap_detail {
-		template<class Width>
-		Width waste(Width width, Width desired) {
-			return (width - desired) * (width - desired);
-		}
-
-		template<class StartCont, class Iter, class WidthCont>
-		inline void get_line_widths(StartCont const& line_start_points, Iter begin, Iter end, WidthCont &line_widths) {
-			size_t line_start = 0;
-			for (auto & line_start_point : line_start_points) {
-				line_widths.push_back(std::accumulate(begin + line_start, begin + line_start_point, 0));
-				line_start = line_start_point;
-			}
-			line_widths.push_back(std::accumulate(begin + line_start, end, 0));
-		}
-
-		// For first-longer and last-longer, bubble words forward/backwards when
-		// possible and needed to make the first/last lines longer
-		//
-		// This is done rather than just using VSFilter's simpler greedy
-		// algorithm due to that VSFilter's algorithm is incorrect; it can
-		// produce incorrectly unbalanced lines and even excess line breaks
-		template<class StartCont, class WidthCont, class Width>
-		void unbalance(StartCont &ret, WidthCont const& widths, Width max_width, WrapMode wrap_mode) {
-			WidthCont line_widths;
-			get_line_widths(ret, widths.begin(), widths.end(), line_widths);
-
-			int from_offset = 0;
-			int to_offset = 1;
-			if (wrap_mode == agi::Wrap_Balanced_LastLonger)
-				std::swap(from_offset, to_offset);
-
-			for (size_t i = 0; i < ret.size(); ++i) {
-				// shift words until they're unbalanced in the correct direction
-				// or shifting a word would exceed the length limit
-				while (line_widths[i + from_offset] < line_widths[i + to_offset]) {
-					int shift_word_width = widths[ret[i]];
-					if (line_widths[i + from_offset] + shift_word_width > max_width)
-						break;
-
-					line_widths[i + from_offset] += shift_word_width;
-					line_widths[i + to_offset] -= shift_word_width;
-					ret[i] += to_offset + -from_offset;
-				}
-			}
-		}
-
-		template<class StartCont, class WidthCont, class Width>
-		void break_greedy(StartCont &ret, WidthCont const& widths, Width max_width) {
-			// Simple greedy matching that just starts a new line every time the
-			// max length is exceeded
-			Width cur_line_width = 0;
-			for (size_t i = 0; i < widths.size(); ++i) {
-				if (cur_line_width > 0 && widths[i] + cur_line_width > max_width) {
-					ret.push_back(i);
-					cur_line_width = 0;
-				}
-
-				cur_line_width += widths[i];
-			}
-		}
-	}
-
-	/// Get the indices at which the blocks should be wrapped
-	/// @tparam WidthCont A random-access container of Widths
-	/// @tparam Width A numeric type which represents a width
-	/// @param widths The widths of the objects to fit within the space
-	/// @param max_width The available space for the objects
-	/// @param wrap_mode WrapMode to use to decide where to insert breaks
-	/// @return Indices into widths which breaks should be inserted before
-	template<class WidthCont, class Width>
-	std::vector<size_t> get_wrap_points(WidthCont const& widths, Width max_width, WrapMode wrap_mode) {
-		using namespace line_wrap_detail;
-
-		std::vector<size_t> ret;
-
-		if (wrap_mode == Wrap_None || widths.size() < 2)
-			return ret;
-
-		// Check if any wrapping is actually needed
-		Width total_width = std::accumulate(widths.begin(), widths.end(), 0);
-		if (total_width <= max_width)
-			return ret;
-
-
-		if (wrap_mode == Wrap_Greedy) {
-			break_greedy(ret, widths, max_width);
-			return ret;
-		}
-
-		size_t num_words = distance(widths.begin(), widths.end());
-
-		// the cost of the optimal arrangement of words [0..i]
-		std::vector<Width> optimal_costs(num_words, INT_MAX);
-
-		// the optimal start word for a line ending at i
-		std::vector<size_t> line_starts(num_words, INT_MAX);
-
-		// O(num_words * min(num_words, max_width))
-		for (size_t end_word = 0; end_word < num_words; ++end_word) {
-			Width current_line_width = 0;
-			for (int start_word = end_word; start_word >= 0; --start_word) {
-				current_line_width += widths[start_word];
-
-				// Only evaluate lines over the limit if they're one word
-				if (current_line_width > max_width && (size_t)start_word != end_word)
-					break;
-
-				Width cost = waste(current_line_width, max_width);
-
-				if (start_word > 0)
-					cost += optimal_costs[start_word - 1];
-
-				if (cost < optimal_costs[end_word]) {
-					optimal_costs[end_word] = cost;
-					line_starts[end_word] = start_word;
-				}
-			}
-		}
-
-		// Select the optimal start word for each line ending with last_word
-		for (size_t last_word = num_words; last_word > 0 && line_starts[last_word - 1] > 0; last_word = line_starts[last_word]) {
-			--last_word;
-			ret.push_back(line_starts[last_word]);
-		}
-		std::reverse(ret.begin(), ret.end());
-
-		if (wrap_mode != Wrap_Balanced)
-			unbalance(ret, widths, max_width, wrap_mode);
-
-		return ret;
-	}
+enum WrapMode {
+    /// Semi-balanced, with the first line guaranteed to be longest if possible
+    Wrap_Balanced_FirstLonger = 0,
+    /// Simple greedy matching with breaks as late as possible
+    Wrap_Greedy = 1,
+    /// No line breaking at all
+    Wrap_None = 2,
+    /// Semi-balanced, with the last line guaranteed to be longest if possible
+    Wrap_Balanced_LastLonger = 3,
+    /// Balanced, with lines as close to equal in length as possible
+    Wrap_Balanced = 4
+};
+
+namespace line_wrap_detail {
+template<class Width>
+Width waste(Width width, Width desired)
+{
+    return (width - desired) * (width - desired);
+}
+
+template<class StartCont, class Iter, class WidthCont>
+inline void get_line_widths(StartCont const &line_start_points, Iter begin, Iter end, WidthCont &line_widths)
+{
+    size_t line_start = 0;
+    for (auto &line_start_point : line_start_points) {
+        line_widths.push_back(std::accumulate(begin + line_start, begin + line_start_point, 0));
+        line_start = line_start_point;
+    }
+    line_widths.push_back(std::accumulate(begin + line_start, end, 0));
+}
+
+// For first-longer and last-longer, bubble words forward/backwards when
+// possible and needed to make the first/last lines longer
+//
+// This is done rather than just using VSFilter's simpler greedy
+// algorithm due to that VSFilter's algorithm is incorrect; it can
+// produce incorrectly unbalanced lines and even excess line breaks
+template<class StartCont, class WidthCont, class Width>
+void unbalance(StartCont &ret, WidthCont const &widths, Width max_width, WrapMode wrap_mode)
+{
+    WidthCont line_widths;
+    get_line_widths(ret, widths.begin(), widths.end(), line_widths);
+
+    int from_offset = 0;
+    int to_offset = 1;
+    if (wrap_mode == agi::Wrap_Balanced_LastLonger)
+        std::swap(from_offset, to_offset);
+
+    for (size_t i = 0; i < ret.size(); ++i) {
+        // shift words until they're unbalanced in the correct direction
+        // or shifting a word would exceed the length limit
+        while (line_widths[i + from_offset] < line_widths[i + to_offset]) {
+            int shift_word_width = widths[ret[i]];
+            if (line_widths[i + from_offset] + shift_word_width > max_width)
+                break;
+
+            line_widths[i + from_offset] += shift_word_width;
+            line_widths[i + to_offset] -= shift_word_width;
+            ret[i] += to_offset + -from_offset;
+        }
+    }
+}
+
+template<class StartCont, class WidthCont, class Width>
+void break_greedy(StartCont &ret, WidthCont const &widths, Width max_width)
+{
+    // Simple greedy matching that just starts a new line every time the
+    // max length is exceeded
+    Width cur_line_width = 0;
+    for (size_t i = 0; i < widths.size(); ++i) {
+        if (cur_line_width > 0 && widths[i] + cur_line_width > max_width) {
+            ret.push_back(i);
+            cur_line_width = 0;
+        }
+
+        cur_line_width += widths[i];
+    }
+}
+}
+
+/// Get the indices at which the blocks should be wrapped
+/// @tparam WidthCont A random-access container of Widths
+/// @tparam Width A numeric type which represents a width
+/// @param widths The widths of the objects to fit within the space
+/// @param max_width The available space for the objects
+/// @param wrap_mode WrapMode to use to decide where to insert breaks
+/// @return Indices into widths which breaks should be inserted before
+template<class WidthCont, class Width>
+std::vector<size_t> get_wrap_points(WidthCont const &widths, Width max_width, WrapMode wrap_mode)
+{
+    using namespace line_wrap_detail;
+
+    std::vector<size_t> ret;
+
+    if (wrap_mode == Wrap_None || widths.size() < 2)
+        return ret;
+
+    // Check if any wrapping is actually needed
+    Width total_width = std::accumulate(widths.begin(), widths.end(), 0);
+    if (total_width <= max_width)
+        return ret;
+
+
+    if (wrap_mode == Wrap_Greedy) {
+        break_greedy(ret, widths, max_width);
+        return ret;
+    }
+
+    size_t num_words = distance(widths.begin(), widths.end());
+
+    // the cost of the optimal arrangement of words [0..i]
+    std::vector<Width> optimal_costs(num_words, INT_MAX);
+
+    // the optimal start word for a line ending at i
+    std::vector<size_t> line_starts(num_words, INT_MAX);
+
+    // O(num_words * min(num_words, max_width))
+    for (size_t end_word = 0; end_word < num_words; ++end_word) {
+        Width current_line_width = 0;
+        for (int start_word = end_word; start_word >= 0; --start_word) {
+            current_line_width += widths[start_word];
+
+            // Only evaluate lines over the limit if they're one word
+            if (current_line_width > max_width && (size_t)start_word != end_word)
+                break;
+
+            Width cost = waste(current_line_width, max_width);
+
+            if (start_word > 0)
+                cost += optimal_costs[start_word - 1];
+
+            if (cost < optimal_costs[end_word]) {
+                optimal_costs[end_word] = cost;
+                line_starts[end_word] = start_word;
+            }
+        }
+    }
+
+    // Select the optimal start word for each line ending with last_word
+    for (size_t last_word = num_words; last_word > 0 && line_starts[last_word - 1] > 0; last_word = line_starts[last_word]) {
+        --last_word;
+        ret.push_back(line_starts[last_word]);
+    }
+    std::reverse(ret.begin(), ret.end());
+
+    if (wrap_mode != Wrap_Balanced)
+        unbalance(ret, widths, max_width, wrap_mode);
+
+    return ret;
+}
 }
diff --git a/libaegisub/include/libaegisub/log.h b/libaegisub/include/libaegisub/log.h
index d980b3b804766c9d844cb322307ae78da5da5420..d768d0480c8de19d1627eca1a42362b330253b00 100644
--- a/libaegisub/include/libaegisub/log.h
+++ b/libaegisub/include/libaegisub/log.h
@@ -40,11 +40,11 @@ class LogSink;
 
 /// Severity levels
 enum Severity {
-	Exception, ///< Used when exceptions are thrown
-	Assert,    ///< Fatal and non-fatal assert logging
-	Warning,   ///< Warnings
-	Info,      ///< Information only
-	Debug      ///< Enabled by default when compiled in debug mode.
+    Exception, ///< Used when exceptions are thrown
+    Assert,    ///< Fatal and non-fatal assert logging
+    Warning,   ///< Warnings
+    Info,      ///< Information only
+    Debug      ///< Enabled by default when compiled in debug mode.
 };
 
 /// Short Severity ID
@@ -56,85 +56,85 @@ extern LogSink *log;
 
 /// Container to hold a single message
 struct SinkMessage {
-	std::string message; ///< Formatted message
-	int64_t time;        ///< Time at execution in nanoseconds since epoch
-	const char *section; ///< Section info eg "video/open" "video/seek" etc
-	const char *file;    ///< Source file
-	const char *func;    ///< Function name
-	Severity severity;   ///< Severity
-	int line;            ///< Source line
+    std::string message; ///< Formatted message
+    int64_t time;        ///< Time at execution in nanoseconds since epoch
+    const char *section; ///< Section info eg "video/open" "video/seek" etc
+    const char *file;    ///< Source file
+    const char *func;    ///< Function name
+    Severity severity;   ///< Severity
+    int line;            ///< Source line
 };
 
 class Emitter;
 
 /// Log sink, single destination for all messages
 class LogSink {
-	std::vector<SinkMessage> messages;
-	size_t next_idx = 0;
-	std::unique_ptr<dispatch::Queue> queue;
+    std::vector<SinkMessage> messages;
+    size_t next_idx = 0;
+    std::unique_ptr<dispatch::Queue> queue;
 
-	/// List of pointers to emitters
-	std::vector<std::unique_ptr<Emitter>> emitters;
+    /// List of pointers to emitters
+    std::vector<std::unique_ptr<Emitter>> emitters;
 
 public:
-	LogSink();
-	~LogSink();
+    LogSink();
+    ~LogSink();
 
-	/// Insert a message into the sink.
-	void Log(SinkMessage const& sm);
+    /// Insert a message into the sink.
+    void Log(SinkMessage const &sm);
 
-	/// @brief Subscribe an emitter
-	/// @param em Emitter to add
-	void Subscribe(std::unique_ptr<Emitter> em);
+    /// @brief Subscribe an emitter
+    /// @param em Emitter to add
+    void Subscribe(std::unique_ptr<Emitter> em);
 
-	/// @brief Unsubscribe and delete an emitter
-	/// @param em Emitter to delete
-	void Unsubscribe(Emitter *em);
+    /// @brief Unsubscribe and delete an emitter
+    /// @param em Emitter to delete
+    void Unsubscribe(Emitter *em);
 
-	/// @brief @get the complete (current) log.
-	/// @return Const pointer to internal sink.
-	std::vector<SinkMessage> GetMessages() const;
+    /// @brief @get the complete (current) log.
+    /// @return Const pointer to internal sink.
+    std::vector<SinkMessage> GetMessages() const;
 };
 
 /// An emitter to produce human readable output for a log sink.
 class Emitter {
 public:
-	/// Destructor
-	virtual ~Emitter() { }
+    /// Destructor
+    virtual ~Emitter() { }
 
-	/// Accept a single log entry
-	virtual void log(SinkMessage const& sm)=0;
+    /// Accept a single log entry
+    virtual void log(SinkMessage const &sm) = 0;
 };
 
 /// A simple emitter which writes the log to a file in json format
 class JsonEmitter final : public Emitter {
-	std::unique_ptr<std::ostream> fp;
+    std::unique_ptr<std::ostream> fp;
 
 public:
-	/// Constructor
-	/// @param directory Directory to write the log file in
-	JsonEmitter(fs::path const& directory);
+    /// Constructor
+    /// @param directory Directory to write the log file in
+    JsonEmitter(fs::path const &directory);
 
-	void log(SinkMessage const&) override;
+    void log(SinkMessage const &) override;
 };
 
 /// Generates a message and submits it to the log sink.
 class Message {
-	boost::interprocess::obufferstream msg;
-	SinkMessage sm;
-	char buffer[2048];
+    boost::interprocess::obufferstream msg;
+    SinkMessage sm;
+    char buffer[2048];
 
 public:
-	Message(const char *section, Severity severity, const char *file, const char *func, int line);
-	~Message();
-	std::ostream& stream() { return msg; }
+    Message(const char *section, Severity severity, const char *file, const char *func, int line);
+    ~Message();
+    std::ostream &stream() { return msg; }
 };
 
 /// Emit log entries to stdout.
 class EmitSTDOUT: public Emitter {
 public:
-	void log(SinkMessage const& sm) override;
+    void log(SinkMessage const &sm) override;
 };
 
-	} // namespace log
+} // namespace log
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/lua/ffi.h b/libaegisub/include/libaegisub/lua/ffi.h
index 848cf080588171fb048296eb8b87f28e8e468ff8..e227faac0d6e120214ac98238f019b63b4c4b967 100644
--- a/libaegisub/include/libaegisub/lua/ffi.h
+++ b/libaegisub/include/libaegisub/lua/ffi.h
@@ -19,38 +19,44 @@
 #include <cstdlib>
 #include <lua.hpp>
 
-namespace agi { namespace lua {
+namespace agi {
+namespace lua {
 void do_register_lib_function(lua_State *L, const char *name, const char *type_name, void *func);
 void do_register_lib_table(lua_State *L, std::initializer_list<const char *> types);
 
-static void register_lib_functions(lua_State *) {
-	// Base case of recursion; nothing to do
+static void register_lib_functions(lua_State *)
+{
+    // Base case of recursion; nothing to do
 }
 
 template<typename Func, typename... Rest>
-void register_lib_functions(lua_State *L, const char *name, Func *func, Rest... rest) {
-	// This cast isn't legal, but LuaJIT internally requires that it work, so we can rely on it too
-	do_register_lib_function(L, name, type_name<Func*>::name().c_str(), (void *)func);
-	register_lib_functions(L, rest...);
+void register_lib_functions(lua_State *L, const char *name, Func *func, Rest... rest)
+{
+    // This cast isn't legal, but LuaJIT internally requires that it work, so we can rely on it too
+    do_register_lib_function(L, name, type_name<Func *>::name().c_str(), (void *)func);
+    register_lib_functions(L, rest...);
 }
 
 template<typename... Args>
-void register_lib_table(lua_State *L, std::initializer_list<const char *> types, Args... functions) {
-	static_assert((sizeof...(functions) & 1) == 0, "Functions must be alternating names and function pointers");
-
-	do_register_lib_table(L, types); // leaves ffi.cast on the stack
-	lua_createtable(L, 0, sizeof...(functions) / 2);
-	register_lib_functions(L, functions...);
-	lua_remove(L, -2); // ffi.cast function
-	// Leaves lib table on the stack
+void register_lib_table(lua_State *L, std::initializer_list<const char *> types, Args... functions)
+{
+    static_assert((sizeof...(functions) & 1) == 0, "Functions must be alternating names and function pointers");
+
+    do_register_lib_table(L, types); // leaves ffi.cast on the stack
+    lua_createtable(L, 0, sizeof...(functions) / 2);
+    register_lib_functions(L, functions...);
+    lua_remove(L, -2); // ffi.cast function
+    // Leaves lib table on the stack
 }
 
 template<typename T>
-char *strndup(T const& str) {
-	char *ret = static_cast<char*>(malloc(str.size() + 1));
-	memcpy(ret, str.data(), str.size());
-	ret[str.size()] = 0;
-	return ret;
+char *strndup(T const &str)
+{
+    char *ret = static_cast<char *>(malloc(str.size() + 1));
+    memcpy(ret, str.data(), str.size());
+    ret[str.size()] = 0;
+    return ret;
 }
 
-} }
+}
+}
diff --git a/libaegisub/include/libaegisub/lua/modules.h b/libaegisub/include/libaegisub/lua/modules.h
index 9783b9723b620b0bdeea276b4a1c930a1d90902d..22412261580b9af06876019a097c4bfa60cae8a3 100644
--- a/libaegisub/include/libaegisub/lua/modules.h
+++ b/libaegisub/include/libaegisub/lua/modules.h
@@ -16,6 +16,8 @@
 
 struct lua_State;
 
-namespace agi { namespace lua {
-	void preload_modules(lua_State *L);
-} }
+namespace agi {
+namespace lua {
+void preload_modules(lua_State *L);
+}
+}
diff --git a/libaegisub/include/libaegisub/lua/script_reader.h b/libaegisub/include/libaegisub/lua/script_reader.h
index 9c9de1300f9205fbf5677b4bfc0e10da6fa49cf3..52ace0eb1d23f645ba25550b91209fdafeaf9e73 100644
--- a/libaegisub/include/libaegisub/lua/script_reader.h
+++ b/libaegisub/include/libaegisub/lua/script_reader.h
@@ -20,10 +20,12 @@
 
 struct lua_State;
 
-namespace agi { namespace lua {
-	/// Load a Lua or Moonscript file at the given path
-	bool LoadFile(lua_State *L, agi::fs::path const& filename);
-	/// Install our module loader and add include_path to the module search
-	/// path of the given lua state
-	bool Install(lua_State *L, std::vector<fs::path> const& include_path);
-} }
+namespace agi {
+namespace lua {
+/// Load a Lua or Moonscript file at the given path
+bool LoadFile(lua_State *L, agi::fs::path const &filename);
+/// Install our module loader and add include_path to the module search
+/// path of the given lua state
+bool Install(lua_State *L, std::vector<fs::path> const &include_path);
+}
+}
diff --git a/libaegisub/include/libaegisub/lua/utils.h b/libaegisub/include/libaegisub/lua/utils.h
index c5a65d6e4417015f2af3ed52c7560f4a254bb368..4400d9ac60d6c62100af38831507100bfbfa6099 100644
--- a/libaegisub/include/libaegisub/lua/utils.h
+++ b/libaegisub/include/libaegisub/lua/utils.h
@@ -28,7 +28,8 @@
 #define BOOST_NORETURN BOOST_ATTRIBUTE_NORETURN
 #endif
 
-namespace agi { namespace lua {
+namespace agi {
+namespace lua {
 // Exception type for errors where the error details are on the lua stack
 struct error_tag {};
 
@@ -47,54 +48,62 @@ inline void push_value(lua_State *L, void *p) { lua_pushlightuserdata(L, p); }
 
 template<typename Integer>
 typename std::enable_if<std::is_integral<Integer>::value>::type
-push_value(lua_State *L, Integer value) {
-	lua_pushinteger(L, static_cast<lua_Integer>(value));
+push_value(lua_State *L, Integer value)
+{
+    lua_pushinteger(L, static_cast<lua_Integer>(value));
 }
 
-inline void push_value(lua_State *L, fs::path const& value) {
-	lua_pushstring(L, value.string().c_str());
+inline void push_value(lua_State *L, fs::path const &value)
+{
+    lua_pushstring(L, value.string().c_str());
 }
 
-inline void push_value(lua_State *L, std::string const& value) {
-	lua_pushlstring(L, value.c_str(), value.size());
+inline void push_value(lua_State *L, std::string const &value)
+{
+    lua_pushlstring(L, value.c_str(), value.size());
 }
 
-inline void push_value(lua_State *L, lua_CFunction value) {
-	if (lua_gettop(L) >= 2 && lua_type(L, -2) == LUA_TUSERDATA) {
-		lua_pushvalue(L, -2);
-		lua_pushcclosure(L, value, 1);
-	}
-	else
-		lua_pushcclosure(L, value, 0);
+inline void push_value(lua_State *L, lua_CFunction value)
+{
+    if (lua_gettop(L) >= 2 && lua_type(L, -2) == LUA_TUSERDATA) {
+        lua_pushvalue(L, -2);
+        lua_pushcclosure(L, value, 1);
+    }
+    else
+        lua_pushcclosure(L, value, 0);
 }
 
 template<typename T>
-void push_value(lua_State *L, std::vector<T> const& value) {
-	lua_createtable(L, value.size(), 0);
-	for (size_t i = 0; i < value.size(); ++i) {
-		push_value(L, value[i]);
-		lua_rawseti(L, -2, i + 1);
-	}
+void push_value(lua_State *L, std::vector<T> const &value)
+{
+    lua_createtable(L, value.size(), 0);
+    for (size_t i = 0; i < value.size(); ++i) {
+        push_value(L, value[i]);
+        lua_rawseti(L, -2, i + 1);
+    }
 }
 
 int exception_wrapper(lua_State *L, int (*func)(lua_State *L));
 /// Wrap a function which may throw exceptions and make it trigger lua errors
 /// whenever it throws
 template<int (*func)(lua_State *L)>
-int exception_wrapper(lua_State *L) {
-	return exception_wrapper(L, func);
+int exception_wrapper(lua_State *L)
+{
+    return exception_wrapper(L, func);
 }
 
 template<typename T>
-void set_field(lua_State *L, const char *name, T value) {
-	push_value(L, value);
-	lua_setfield(L, -2, name);
+void set_field(lua_State *L, const char *name, T value)
+{
+    push_value(L, value);
+    lua_setfield(L, -2, name);
 }
 
 template<int (*func)(lua_State *L)>
-void set_field(lua_State *L, const char *name) {
-	push_value(L, exception_wrapper<func>);
-	lua_setfield(L, -2, name);
+void set_field(lua_State *L, const char *name)
+{
+    push_value(L, exception_wrapper<func>);
+    lua_setfield(L, -2, name);
 }
 
 std::string get_string_or_default(lua_State *L, int idx);
@@ -107,62 +116,66 @@ size_t check_uint(lua_State *L, int idx);
 void *check_udata(lua_State *L, int idx, const char *mt);
 
 template<typename T, typename... Args>
-T *make(lua_State *L, const char *mt, Args&&... args) {
-	auto obj = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
-	new(obj) T(std::forward<Args>(args)...);
-	luaL_getmetatable(L, mt);
-	lua_setmetatable(L, -2);
-	return obj;
+T *make(lua_State *L, const char *mt, Args &&... args)
+{
+    auto obj = static_cast<T *>(lua_newuserdata(L, sizeof(T)));
+    new (obj) T(std::forward<Args>(args)...);
+    luaL_getmetatable(L, mt);
+    lua_setmetatable(L, -2);
+    return obj;
 }
 
 template<typename T>
-T& get(lua_State *L, int idx, const char *mt) {
-	return *static_cast<T *>(check_udata(L, idx, mt));
+T &get(lua_State *L, int idx, const char *mt)
+{
+    return *static_cast<T *>(check_udata(L, idx, mt));
 }
 
 #ifdef _DEBUG
 struct LuaStackcheck {
-	lua_State *L;
-	int startstack;
+    lua_State *L;
+    int startstack;
 
-	void check_stack(int additional);
-	void dump();
+    void check_stack(int additional);
+    void dump();
 
-	LuaStackcheck(lua_State *L) : L(L), startstack(lua_gettop(L)) { }
-	~LuaStackcheck() { check_stack(0); }
+    LuaStackcheck(lua_State *L) : L(L), startstack(lua_gettop(L)) { }
+    ~LuaStackcheck() { check_stack(0); }
 };
 #else
 struct LuaStackcheck {
-	void check_stack(int) { }
-	void dump() { }
-	LuaStackcheck(lua_State*) { }
+    void check_stack(int) { }
+    void dump() { }
+    LuaStackcheck(lua_State *) { }
 };
 #endif
 
 struct LuaForEachBreak {};
 
 template<typename Func>
-void lua_for_each(lua_State *L, Func&& func) {
-	{
-		LuaStackcheck stackcheck(L);
-		lua_pushnil(L); // initial key
-		while (lua_next(L, -2)) {
-			try {
-				func();
-			}
-			catch (LuaForEachBreak) {
-				lua_pop(L, 2); // pop value and key
-				break;
-			}
-			lua_pop(L, 1); // pop value, leave key
-		}
-		stackcheck.check_stack(0);
-	}
-	lua_pop(L, 1); // pop table
+void lua_for_each(lua_State *L, Func &&func)
+{
+    {
+        LuaStackcheck stackcheck(L);
+        lua_pushnil(L); // initial key
+        while (lua_next(L, -2)) {
+            try {
+                func();
+            }
+            catch (LuaForEachBreak) {
+                lua_pop(L, 2); // pop value and key
+                break;
+            }
+            lua_pop(L, 1); // pop value, leave key
+        }
+        stackcheck.check_stack(0);
+    }
+    lua_pop(L, 1); // pop table
 }
 
 /// Lua error handler which adds the stack trace to the error message, with
 /// moonscript line rewriting support
 int add_stack_trace(lua_State *L);
 
-} }
+}
+}
diff --git a/libaegisub/include/libaegisub/make_unique.h b/libaegisub/include/libaegisub/make_unique.h
index 3af862e97bd6914335beecbd2ada616be571a975..17e4ddb45db3b1e26aca32199785f3d0b86cfd04 100644
--- a/libaegisub/include/libaegisub/make_unique.h
+++ b/libaegisub/include/libaegisub/make_unique.h
@@ -17,8 +17,9 @@
 #include <memory>
 
 namespace agi {
-	template<typename T, typename... Args>
-	std::unique_ptr<T> make_unique(Args&&... args) {
-		return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-	}
+template<typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args &&... args)
+{
+    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
 }
diff --git a/libaegisub/include/libaegisub/mru.h b/libaegisub/include/libaegisub/mru.h
index 9ce3dd43f2243ab1e036ee0772a24f995fecad62..1b7201f4b11a3a38c82e7672d140eca1e7a17ed9 100644
--- a/libaegisub/include/libaegisub/mru.h
+++ b/libaegisub/include/libaegisub/mru.h
@@ -20,8 +20,8 @@
 #include <libaegisub/fs_fwd.h>
 
 namespace json {
-	class UnknownElement;
-	typedef std::vector<UnknownElement> Array;
+class UnknownElement;
+typedef std::vector<UnknownElement> Array;
 }
 
 namespace agi {
@@ -38,61 +38,61 @@ DEFINE_EXCEPTION(MRUError, Exception);
 /// If a file fails to open, Remove() should be called.
 class MRUManager {
 public:
-	/// @brief Map for time->value pairs.
-	using MRUListMap = std::vector<agi::fs::path>;
-
-	/// @brief Constructor
-	/// @param config File to load MRU values from
-	MRUManager(agi::fs::path const& config, std::pair<const char *, size_t> default_config, agi::Options *options = nullptr);
-
-	template<size_t N>
-	MRUManager(agi::fs::path const& file, const char (&default_config)[N])
-	: MRUManager(file, {default_config, N - 1}) { }
-
-	/// @brief Add entry to the list.
-	/// @param key List name
-	/// @param entry Entry to add
-	/// @exception MRUError thrown when an invalid key is used.
-	void Add(const char *key, agi::fs::path const& entry);
-
-	/// @brief Remove entry from the list.
-	/// @param key List name
-	/// @param entry Entry to add
-	/// @exception MRUError thrown when an invalid key is used.
-	void Remove(const char *key, agi::fs::path const& entry);
-
-	/// @brief Return list
-	/// @param key List name
-	/// @exception MRUError thrown when an invalid key is used.
-	const MRUListMap* Get(const char *key);
-
-	/// @brief Return A single entry in a list.
-	/// @param key List name
-	/// @param entry 0-base position of entry
-	/// @exception MRUError thrown when an invalid key is used.
-	agi::fs::path const& GetEntry(const char *key, const size_t entry);
-
-	/// Write MRU lists to disk.
-	void Flush();
+    /// @brief Map for time->value pairs.
+    using MRUListMap = std::vector<agi::fs::path>;
+
+    /// @brief Constructor
+    /// @param config File to load MRU values from
+    MRUManager(agi::fs::path const &config, std::pair<const char *, size_t> default_config, agi::Options *options = nullptr);
+
+    template<size_t N>
+    MRUManager(agi::fs::path const &file, const char (&default_config)[N])
+        : MRUManager(file, {default_config, N - 1}) { }
+
+    /// @brief Add entry to the list.
+    /// @param key List name
+    /// @param entry Entry to add
+    /// @exception MRUError thrown when an invalid key is used.
+    void Add(const char *key, agi::fs::path const &entry);
+
+    /// @brief Remove entry from the list.
+    /// @param key List name
+    /// @param entry Entry to add
+    /// @exception MRUError thrown when an invalid key is used.
+    void Remove(const char *key, agi::fs::path const &entry);
+
+    /// @brief Return list
+    /// @param key List name
+    /// @exception MRUError thrown when an invalid key is used.
+    const MRUListMap *Get(const char *key);
+
+    /// @brief Return A single entry in a list.
+    /// @param key List name
+    /// @param entry 0-base position of entry
+    /// @exception MRUError thrown when an invalid key is used.
+    agi::fs::path const &GetEntry(const char *key, const size_t entry);
+
+    /// Write MRU lists to disk.
+    void Flush();
 
 private:
-	/// Internal name of the config file, set during object construction.
-	const agi::fs::path config_name;
-
-	/// User preferences object for maximum number of items to list
-	agi::Options *const options;
-
-	/// Internal MRUMap values.
-	std::array<MRUListMap, 7> mru;
-
-	/// @brief Load MRU Lists.
-	/// @param key List name.
-	/// @param array json::Array of values.
-	void Load(const char *key, ::json::Array const& array);
-	/// @brief Prune MRUListMap to the desired length.
-	/// This uses the user-set values for MRU list length.
-	void Prune(const char *key, MRUListMap& map) const;
-	MRUListMap &Find(const char *key);
+    /// Internal name of the config file, set during object construction.
+    const agi::fs::path config_name;
+
+    /// User preferences object for maximum number of items to list
+    agi::Options *const options;
+
+    /// Internal MRUMap values.
+    std::array<MRUListMap, 7> mru;
+
+    /// @brief Load MRU Lists.
+    /// @param key List name.
+    /// @param array json::Array of values.
+    void Load(const char *key, ::json::Array const &array);
+    /// @brief Prune MRUListMap to the desired length.
+    /// This uses the user-set values for MRU list length.
+    void Prune(const char *key, MRUListMap &map) const;
+    MRUListMap &Find(const char *key);
 };
 
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/of_type_adaptor.h b/libaegisub/include/libaegisub/of_type_adaptor.h
index bccbefcf81f20f9d5e40540079e0a26669dc0ef4..a650713a10bbb701d690afa06c6d9d19a283d373 100644
--- a/libaegisub/include/libaegisub/of_type_adaptor.h
+++ b/libaegisub/include/libaegisub/of_type_adaptor.h
@@ -18,60 +18,62 @@
 #include <boost/range/adaptor/transformed.hpp>
 
 namespace agi {
-	namespace of_type_detail {
-		using namespace boost::adaptors;
+namespace of_type_detail {
+using namespace boost::adaptors;
 
-		/// Tag type returned from of_type<T>() to select the operator| overload
-		template<class T> struct of_type_tag {};
+/// Tag type returned from of_type<T>() to select the operator| overload
+template<class T> struct of_type_tag {};
 
-		/// Take the address of a value and dynamic_cast it to Type*
-		template<class Type>
-		struct cast_to {
-			typedef Type *result_type;
+/// Take the address of a value and dynamic_cast it to Type*
+template<class Type>
+struct cast_to {
+    typedef Type *result_type;
 
-			template<class InType> Type *operator()(InType &ptr) const {
-				return typeid(ptr) == typeid(Type) ? static_cast<Type*>(&ptr) : nullptr;
-			}
+    template<class InType> Type *operator()(InType &ptr) const {
+        return typeid(ptr) == typeid(Type) ? static_cast<Type *>(&ptr) : nullptr;
+    }
 
-			template<class InType> Type *operator()(std::unique_ptr<InType>& ptr) const {
-				return (*this)(*ptr);
-			}
+    template<class InType> Type *operator()(std::unique_ptr<InType> &ptr) const {
+        return (*this)(*ptr);
+    }
 
-			template<class InType> Type *operator()(std::unique_ptr<InType> const& ptr) const {
-				return (*this)(*ptr);
-			}
+    template<class InType> Type *operator()(std::unique_ptr<InType> const &ptr) const {
+        return (*this)(*ptr);
+    }
 
-			template<class InType> Type *operator()(InType *ptr) const {
-				return (*this)(*ptr);
-			}
-		};
+    template<class InType> Type *operator()(InType *ptr) const {
+        return (*this)(*ptr);
+    }
+};
 
-		template<class Type>
-		inline bool not_null(Type *ptr) {
-			return !!ptr;
-		}
+template<class Type>
+inline bool not_null(Type *ptr)
+{
+    return !!ptr;
+}
 
-		// Defined here for ADL reasons, since we don't want the tag type in
-		// the top-level agi namespace (and it lets us get away with the using
-		// namespace above)
-		template<class Rng, class Type>
-		inline auto operator|(Rng& r, of_type_tag<Type>)
-			-> decltype(r | transformed(cast_to<Type>()) | filtered(not_null<Type>))
-		{
-			return r | transformed(cast_to<Type>()) | filtered(not_null<Type>);
-		}
+// Defined here for ADL reasons, since we don't want the tag type in
+// the top-level agi namespace (and it lets us get away with the using
+// namespace above)
+template<class Rng, class Type>
+inline auto operator|(Rng &r, of_type_tag<Type>)
+-> decltype(r | transformed(cast_to<Type>()) | filtered(not_null<Type>))
+{
+    return r | transformed(cast_to<Type>()) | filtered(not_null<Type>);
+}
 
-		// const overload of the above
-		template<class Rng, class Type>
-		inline auto operator|(Rng const& r, of_type_tag<Type>)
-			-> decltype(r | transformed(cast_to<const Type>()) | filtered(not_null<const Type>))
-		{
-			return r | transformed(cast_to<const Type>()) | filtered(not_null<const Type>);
-		}
-	}
+// const overload of the above
+template<class Rng, class Type>
+inline auto operator|(Rng const &r, of_type_tag<Type>)
+-> decltype(r | transformed(cast_to<const Type>()) | filtered(not_null<const Type>))
+{
+    return r | transformed(cast_to<const Type>()) | filtered(not_null<const Type>);
+}
+}
 
-	template<class T>
-	inline of_type_detail::of_type_tag<T> of_type() {
-		return of_type_detail::of_type_tag<T>();
-	}
+template<class T>
+inline of_type_detail::of_type_tag<T> of_type()
+{
+    return of_type_detail::of_type_tag<T>();
+}
 }
diff --git a/libaegisub/include/libaegisub/option.h b/libaegisub/include/libaegisub/option.h
index eb94ef670ba44b2fd848872e83f85e012a6d0d10..23e5519a427483dbcec07ab060f0f8d95a47d6d8 100644
--- a/libaegisub/include/libaegisub/option.h
+++ b/libaegisub/include/libaegisub/option.h
@@ -27,8 +27,8 @@
 #include <libaegisub/fs_fwd.h>
 
 namespace json {
-	class UnknownElement;
-	typedef std::map<std::string, UnknownElement> Object;
+class UnknownElement;
+typedef std::map<std::string, UnknownElement> Object;
 }
 
 namespace agi {
@@ -36,59 +36,59 @@ class OptionValue;
 
 class Options {
 public:
-	/// Options class settings.
-	enum OptionSetting {
-		NONE       = 0x000,		///< Do nothing (default)
-		FLUSH_SKIP = 0x001		///< Skip writing the config file to disk
-	};
+    /// Options class settings.
+    enum OptionSetting {
+        NONE       = 0x000,		///< Do nothing (default)
+        FLUSH_SKIP = 0x001		///< Skip writing the config file to disk
+    };
 
 private:
-	std::vector<std::unique_ptr<OptionValue>> values;
+    std::vector<std::unique_ptr<OptionValue>> values;
 
-	/// User config (file that will be written to disk)
-	const agi::fs::path config_file;
+    /// User config (file that will be written to disk)
+    const agi::fs::path config_file;
 
-	/// Settings.
-	const OptionSetting setting;
+    /// Settings.
+    const OptionSetting setting;
 
-	/// @brief Load a config file into the Options object.
-	/// @param config Config to load.
-	/// @param ignore_errors Log invalid entires in the option file and continue rather than throwing an exception
-	void LoadConfig(std::istream& stream, bool ignore_errors = false);
+    /// @brief Load a config file into the Options object.
+    /// @param config Config to load.
+    /// @param ignore_errors Log invalid entires in the option file and continue rather than throwing an exception
+    void LoadConfig(std::istream &stream, bool ignore_errors = false);
 
 public:
-	/// @brief Constructor
-	/// @param file User config that will be loaded from and written back to.
-	/// @param default_config Default configuration.
-	Options(agi::fs::path const& file, std::pair<const char *, size_t> default_config, const OptionSetting setting = NONE);
-
-	template<size_t N>
-	Options(agi::fs::path const& file, const char (&default_config)[N], const OptionSetting setting = NONE)
-	: Options(file, {default_config, N - 1}, setting) { }
-
-	/// Destructor
-	~Options();
-
-	/// @brief Get an option by name.
-	/// @param name Option to get.
-	/// Get an option value object by name throw an internal exception if the option is not found.
-	OptionValue *Get(const char *name);
-	OptionValue *Get(std::string const& name) { return Get(name.c_str()); }
-
-	/// @brief Next configuration file to load.
-	/// @param[in] src Stream to load from.
-	/// Load next config which will supersede any values from previous configs
-	/// can be called as many times as required, but only after ConfigDefault() and
-	/// before ConfigUser()
-	void ConfigNext(std::istream &stream) { LoadConfig(stream); }
-
-	/// @brief Set user config file.
-	/// Set the user configuration file and read options from it, closes all
-	/// possible config file loading and sets the file to write to.
-	void ConfigUser();
-
-	/// Write the user configuration to disk, throws an exception if something goes wrong.
-	void Flush() const;
+    /// @brief Constructor
+    /// @param file User config that will be loaded from and written back to.
+    /// @param default_config Default configuration.
+    Options(agi::fs::path const &file, std::pair<const char *, size_t> default_config, const OptionSetting setting = NONE);
+
+    template<size_t N>
+    Options(agi::fs::path const &file, const char (&default_config)[N], const OptionSetting setting = NONE)
+        : Options(file, {default_config, N - 1}, setting) { }
+
+    /// Destructor
+    ~Options();
+
+    /// @brief Get an option by name.
+    /// @param name Option to get.
+    /// Get an option value object by name throw an internal exception if the option is not found.
+    OptionValue *Get(const char *name);
+    OptionValue *Get(std::string const &name) { return Get(name.c_str()); }
+
+    /// @brief Next configuration file to load.
+    /// @param[in] src Stream to load from.
+    /// Load next config which will supersede any values from previous configs
+    /// can be called as many times as required, but only after ConfigDefault() and
+    /// before ConfigUser()
+    void ConfigNext(std::istream &stream) { LoadConfig(stream); }
+
+    /// @brief Set user config file.
+    /// Set the user configuration file and read options from it, closes all
+    /// possible config file loading and sets the file to write to.
+    void ConfigUser();
+
+    /// Write the user configuration to disk, throws an exception if something goes wrong.
+    void Flush() const;
 };
 
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/option_value.h b/libaegisub/include/libaegisub/option_value.h
index 63870419cc693808e1438b2ec91e2d5189d48fb5..b3e6300b57eb359cc7dbd1396a52a6fa8baba815 100644
--- a/libaegisub/include/libaegisub/option_value.h
+++ b/libaegisub/include/libaegisub/option_value.h
@@ -30,81 +30,81 @@ namespace agi {
 /// Option type
 /// No bitsets here.
 enum class OptionType {
-	String     = 0,	///< String
-	Int        = 1,	///< Integer
-	Double     = 2,	///< Double
-	Color      = 3,	///< Color
-	Bool       = 4,	///< Bool
-	ListString = 100,	///< List of Strings
-	ListInt    = 101,	///< List of Integers
-	ListDouble = 102,	///< List of Doubles
-	ListColor  = 103,	///< List of Colors
-	ListBool   = 104	///< List of Bools
+    String     = 0,	///< String
+    Int        = 1,	///< Integer
+    Double     = 2,	///< Double
+    Color      = 3,	///< Color
+    Bool       = 4,	///< Bool
+    ListString = 100,	///< List of Strings
+    ListInt    = 101,	///< List of Integers
+    ListDouble = 102,	///< List of Doubles
+    ListColor  = 103,	///< List of Colors
+    ListBool   = 104	///< List of Bools
 };
 
 /// @class OptionValue
 /// Holds an actual option.
 class OptionValue {
-	agi::signal::Signal<OptionValue const&> ValueChanged;
-	std::string name;
-
-	std::string TypeToString(OptionType type) const;
-	InternalError TypeError(OptionType type) const;
-
-	template<typename T>
-	T *As(OptionType type) {
-		if (GetType() == type)
-			return static_cast<T *>(this);
-		throw TypeError(type);
-	}
-
-	template<typename T>
-	const T *As(OptionType type) const {
-		if (GetType() == type)
-			return static_cast<const T *>(this);
-		throw TypeError(type);
-	}
+    agi::signal::Signal<OptionValue const &> ValueChanged;
+    std::string name;
+
+    std::string TypeToString(OptionType type) const;
+    InternalError TypeError(OptionType type) const;
+
+    template<typename T>
+    T *As(OptionType type) {
+        if (GetType() == type)
+            return static_cast<T *>(this);
+        throw TypeError(type);
+    }
+
+    template<typename T>
+    const T *As(OptionType type) const {
+        if (GetType() == type)
+            return static_cast<const T *>(this);
+        throw TypeError(type);
+    }
 
 protected:
-	void NotifyChanged() { ValueChanged(*this); }
+    void NotifyChanged() { ValueChanged(*this); }
 
-	OptionValue(std::string name) BOOST_NOEXCEPT : name(std::move(name)) { }
+OptionValue(std::string name) BOOST_NOEXCEPT : name(std::move(name)) { }
 
 public:
-	virtual ~OptionValue() = default;
-
-	std::string const& GetName() const { return name; }
-	virtual OptionType GetType() const = 0;
-	virtual bool IsDefault() const = 0;
-	virtual void Reset() = 0;
-
-	std::string const& GetString() const;
-	int64_t const& GetInt() const;
-	double const& GetDouble() const;
-	Color const& GetColor() const;
-	bool const& GetBool() const;
-
-	void SetString(const std::string);
-	void SetInt(const int64_t);
-	void SetDouble(const double);
-	void SetColor(const Color);
-	void SetBool(const bool);
-
-	std::vector<std::string> const& GetListString() const;
-	std::vector<int64_t> const& GetListInt() const;
-	std::vector<double> const& GetListDouble() const;
-	std::vector<Color> const& GetListColor() const;
-	std::vector<bool> const& GetListBool() const;
-
-	void SetListString(std::vector<std::string>);
-	void SetListInt(std::vector<int64_t>);
-	void SetListDouble(std::vector<double>);
-	void SetListColor(std::vector<Color>);
-	void SetListBool(std::vector<bool>);
-
-	virtual void Set(const OptionValue *new_value)=0;
-
-	DEFINE_SIGNAL_ADDERS(ValueChanged, Subscribe)
+    virtual ~OptionValue() = default;
+
+    std::string const &GetName() const { return name; }
+    virtual OptionType GetType() const = 0;
+    virtual bool IsDefault() const = 0;
+    virtual void Reset() = 0;
+
+    std::string const &GetString() const;
+    int64_t const &GetInt() const;
+    double const &GetDouble() const;
+    Color const &GetColor() const;
+    bool const &GetBool() const;
+
+    void SetString(const std::string);
+    void SetInt(const int64_t);
+    void SetDouble(const double);
+    void SetColor(const Color);
+    void SetBool(const bool);
+
+    std::vector<std::string> const &GetListString() const;
+    std::vector<int64_t> const &GetListInt() const;
+    std::vector<double> const &GetListDouble() const;
+    std::vector<Color> const &GetListColor() const;
+    std::vector<bool> const &GetListBool() const;
+
+    void SetListString(std::vector<std::string>);
+    void SetListInt(std::vector<int64_t>);
+    void SetListDouble(std::vector<double>);
+    void SetListColor(std::vector<Color>);
+    void SetListBool(std::vector<bool>);
+
+    virtual void Set(const OptionValue *new_value) = 0;
+
+    DEFINE_SIGNAL_ADDERS(ValueChanged, Subscribe)
 };
 
 #define CONFIG_OPTIONVALUE(type_name, type)                                           \
diff --git a/libaegisub/include/libaegisub/owning_intrusive_list.h b/libaegisub/include/libaegisub/owning_intrusive_list.h
index 63173583556542ab9fa17580773aa9e0d0c87805..ca59df764a31f805ac7a46bb6dc7a1a58f82d9cf 100644
--- a/libaegisub/include/libaegisub/owning_intrusive_list.h
+++ b/libaegisub/include/libaegisub/owning_intrusive_list.h
@@ -22,70 +22,70 @@ namespace agi {
 
 template<typename T>
 class owning_intrusive_list final : private boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type {
-	typedef typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type base;
+    typedef typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type base;
 public:
-	using base::back;
-	using base::begin;
-	using base::cbegin;
-	using base::cend;
-	using base::crbegin;
-	using base::crend;
-	using base::empty;
-	using base::end;
-	using base::front;
-	using base::insert;
-	using base::iterator_to;
-	using base::merge;
-	using base::push_back;
-	using base::push_front;
-	using base::rbegin;
-	using base::rend;
-	using base::reverse;
-	using base::s_iterator_to;
-	using base::shift_backwards;
-	using base::shift_forward;
-	using base::size;
-	using base::sort;
-	using base::splice;
-	using base::swap;
+    using base::back;
+    using base::begin;
+    using base::cbegin;
+    using base::cend;
+    using base::crbegin;
+    using base::crend;
+    using base::empty;
+    using base::end;
+    using base::front;
+    using base::insert;
+    using base::iterator_to;
+    using base::merge;
+    using base::push_back;
+    using base::push_front;
+    using base::rbegin;
+    using base::rend;
+    using base::reverse;
+    using base::s_iterator_to;
+    using base::shift_backwards;
+    using base::shift_forward;
+    using base::size;
+    using base::sort;
+    using base::splice;
+    using base::swap;
 
-	using typename base::const_node_ptr;
-	using typename base::const_pointer;
-	using typename base::const_reverse_iterator;
-	using typename base::node;
-	using typename base::node_algorithms;
-	using typename base::node_ptr;
-	using typename base::node_traits;
-	using typename base::pointer;
-	using typename base::reference;
-	using typename base::reverse_iterator;
-	using typename base::size_type;
-	using typename base::value_type;
-	using typename base::iterator;
-	using typename base::const_iterator;
-	using typename base::const_reference;
-	using typename base::difference_type;
+    using typename base::const_node_ptr;
+    using typename base::const_pointer;
+    using typename base::const_reverse_iterator;
+    using typename base::node;
+    using typename base::node_algorithms;
+    using typename base::node_ptr;
+    using typename base::node_traits;
+    using typename base::pointer;
+    using typename base::reference;
+    using typename base::reverse_iterator;
+    using typename base::size_type;
+    using typename base::value_type;
+    using typename base::iterator;
+    using typename base::const_iterator;
+    using typename base::const_reference;
+    using typename base::difference_type;
 
-	iterator erase(const_iterator b, const_iterator e) { return this->erase_and_dispose(b, e, [](T *e) { delete e; }); }
-	iterator erase(const_iterator b, const_iterator e, difference_type n) { return this->erase_and_dispose(b, e, n, [](T *e) { delete e; }); }
-	iterator erase(const_iterator i) { return this->erase_and_dispose(i, [](T *e) { delete e; }); }
-	void clear() { this->clear_and_dispose([](T *e) { delete e; }); }
-	void pop_back() { this->pop_back_and_dispose([](T *e) { delete e; }); }
-	void pop_front() { this->pop_front_and_dispose([](T *e) { delete e; }); }
-	void remove(const_reference value) { return this->remove_and_dispose(value, [](T *e) { delete e; }); }
-	void unique() { this->unique_and_dispose([](T *e) { delete e; }); }
+    iterator erase(const_iterator b, const_iterator e) { return this->erase_and_dispose(b, e, [](T * e) { delete e; }); }
+    iterator erase(const_iterator b, const_iterator e, difference_type n) { return this->erase_and_dispose(b, e, n, [](T * e) { delete e; }); }
+    iterator erase(const_iterator i) { return this->erase_and_dispose(i, [](T * e) { delete e; }); }
+    void clear() { this->clear_and_dispose([](T * e) { delete e; }); }
+    void pop_back() { this->pop_back_and_dispose([](T * e) { delete e; }); }
+    void pop_front() { this->pop_front_and_dispose([](T * e) { delete e; }); }
+    void remove(const_reference value) { return this->remove_and_dispose(value, [](T * e) { delete e; }); }
+    void unique() { this->unique_and_dispose([](T * e) { delete e; }); }
 
-	template<class Pred> void remove_if(Pred&& pred) {
-		this->remove_if_and_dispose(std::forward<Pred>(pred), [](T *e) { delete e; });
-	}
+    template<class Pred> void remove_if(Pred &&pred) {
+        this->remove_if_and_dispose(std::forward<Pred>(pred), [](T * e) { delete e; });
+    }
 
-	template<class BinaryPredicate> void unique(BinaryPredicate&& pred) {
-		this->unique_and_dispose(std::forward<BinaryPredicate>(pred), [](T *e) { delete e; });
-	}
+    template<class BinaryPredicate> void unique(BinaryPredicate &&pred) {
+        this->unique_and_dispose(std::forward<BinaryPredicate>(pred), [](T * e) { delete e; });
+    }
 
-	~owning_intrusive_list() {
-		clear();
-	}
+    ~owning_intrusive_list() {
+        clear();
+    }
 };
 
 }
diff --git a/libaegisub/include/libaegisub/path.h b/libaegisub/include/libaegisub/path.h
index a1bcb7afcf14ed895ad1c206f47545ef7380a850..863eb5497ef9cb8f8fa91e17f6dd535b4a6437c7 100644
--- a/libaegisub/include/libaegisub/path.h
+++ b/libaegisub/include/libaegisub/path.h
@@ -22,52 +22,52 @@
 namespace agi {
 /// Class for handling everything path-related in Aegisub
 class Path {
-	/// Token -> Path map
-	std::array<fs::path, 8> paths;
+    /// Token -> Path map
+    std::array<fs::path, 8> paths;
 
-	/// Platform-specific code to fill in the default paths, called in the constructor
-	void FillPlatformSpecificPaths();
+    /// Platform-specific code to fill in the default paths, called in the constructor
+    void FillPlatformSpecificPaths();
 
 public:
-	/// Constructor
-	Path();
+    /// 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;
+    /// 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 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)); }
+    /// 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;
+    /// 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;
+    /// 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(const char *token_name, fs::path const& token_value);
+    /// 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(const char *token_name, fs::path const &token_value);
 };
 
 }
diff --git a/libaegisub/include/libaegisub/scoped_ptr.h b/libaegisub/include/libaegisub/scoped_ptr.h
index 6267fe7e19f92bbede6da8debbfb3973680900b7..4121ea6bd2991370b94ff985198d616f43f3319a 100644
--- a/libaegisub/include/libaegisub/scoped_ptr.h
+++ b/libaegisub/include/libaegisub/scoped_ptr.h
@@ -22,28 +22,27 @@ namespace agi {
 /// A generic scoped holder for non-pointer handles
 template<class T, class Del = void(*)(T)>
 class scoped_holder {
-	T value;
-	Del destructor;
+    T value;
+    Del destructor;
 
-	scoped_holder(scoped_holder const&);
-	scoped_holder& operator=(scoped_holder const&);
+    scoped_holder(scoped_holder const &);
+    scoped_holder &operator=(scoped_holder const &);
 public:
-	operator T() const { return value; }
-	T operator->() const { return value; }
+    operator T() const { return value; }
+    T operator->() const { return value; }
 
-	scoped_holder& operator=(T new_value) {
-		if (value)
-			destructor(value);
-		value = new_value;
-		return *this;
-	}
+    scoped_holder &operator=(T new_value) {
+        if (value)
+            destructor(value);
+        value = new_value;
+        return *this;
+    }
 
-	scoped_holder(T value, Del destructor)
-	: value(value)
-	, destructor(destructor)
-	{
-	}
+    scoped_holder(T value, Del destructor)
+        : value(value)
+        , destructor(destructor) {
+    }
 
-	~scoped_holder() { if (value) destructor(value); }
+    ~scoped_holder() { if (value) destructor(value); }
 };
 }
diff --git a/libaegisub/include/libaegisub/signal.h b/libaegisub/include/libaegisub/signal.h
index ca520e49f2b4d74a94feb7378cc688297592830e..763291d29dcbd752e67c0a2a388d63c433427194 100644
--- a/libaegisub/include/libaegisub/signal.h
+++ b/libaegisub/include/libaegisub/signal.h
@@ -19,26 +19,27 @@
 #include <memory>
 #include <vector>
 
-namespace agi { namespace signal {
+namespace agi {
+namespace signal {
 class Connection;
 
 /// Implementation details; nothing outside this file should directly touch
 /// anything in the detail namespace
 namespace detail {
-	class SignalBase;
-	class ConnectionToken {
-		friend class agi::signal::Connection;
-		friend class SignalBase;
-
-		SignalBase *signal;
-		bool blocked = false;
-		bool claimed = false;
-
-		ConnectionToken(SignalBase *signal) : signal(signal) { }
-		inline void Disconnect();
-	public:
-		~ConnectionToken() { Disconnect(); }
-	};
+class SignalBase;
+class ConnectionToken {
+    friend class agi::signal::Connection;
+    friend class SignalBase;
+
+    SignalBase *signal;
+    bool blocked = false;
+    bool claimed = false;
+
+    ConnectionToken(SignalBase *signal) : signal(signal) { }
+    inline void Disconnect();
+public:
+    ~ConnectionToken() { Disconnect(); }
+};
 }
 
 /// A connection which is not automatically closed
@@ -52,150 +53,151 @@ namespace detail {
 /// connection to the slot. If there is any chance that the signal will outlive
 /// the slot, this must be done.
 class UnscopedConnection {
-	friend class Connection;
-	detail::ConnectionToken *token;
+    friend class Connection;
+    detail::ConnectionToken *token;
 public:
-	UnscopedConnection(detail::ConnectionToken *token) : token(token) { }
+    UnscopedConnection(detail::ConnectionToken *token) : token(token) { }
 };
 
 /// Object representing a connection to a signal
 class Connection {
-	std::unique_ptr<detail::ConnectionToken> token;
+    std::unique_ptr<detail::ConnectionToken> token;
 public:
-	Connection() = default;
-	Connection(UnscopedConnection src) BOOST_NOEXCEPT : token(src.token) { token->claimed = true; }
-	Connection(Connection&& that) BOOST_NOEXCEPT : token(std::move(that.token)) { }
-	Connection(detail::ConnectionToken *token) BOOST_NOEXCEPT : token(token) { token->claimed = true; }
-	Connection& operator=(Connection&& that) BOOST_NOEXCEPT { token = std::move(that.token); return *this; }
-
-	/// @brief End this connection
-	///
-	/// This normally does not need to be manually called, as a connection is
-	/// automatically closed when all Connection objects referring to it are
-	/// gone. To temporarily enable or disable a connection, use Block/Unblock
-	/// instead
-	void Disconnect() { if (token) token->Disconnect(); }
-
-	/// @brief Disable this connection until Unblock is called
-	void Block() { if (token) token->blocked = true; }
-
-	/// @brief Reenable this connection after it was disabled by Block
-	void Unblock() { if (token) token->blocked = false; }
+    Connection() = default;
+Connection(UnscopedConnection src) BOOST_NOEXCEPT : token(src.token) { token->claimed = true; }
+Connection(Connection &&that) BOOST_NOEXCEPT : token(std::move(that.token)) { }
+Connection(detail::ConnectionToken *token) BOOST_NOEXCEPT : token(token) { token->claimed = true; }
+    Connection &operator=(Connection &&that) BOOST_NOEXCEPT { token = std::move(that.token); return *this; }
+
+    /// @brief End this connection
+    ///
+    /// This normally does not need to be manually called, as a connection is
+    /// automatically closed when all Connection objects referring to it are
+    /// gone. To temporarily enable or disable a connection, use Block/Unblock
+    /// instead
+    void Disconnect() { if (token) token->Disconnect(); }
+
+    /// @brief Disable this connection until Unblock is called
+    void Block() { if (token) token->blocked = true; }
+
+    /// @brief Reenable this connection after it was disabled by Block
+    void Unblock() { if (token) token->blocked = false; }
 };
 
 namespace detail {
-	/// Polymorphic base class for slots
-	///
-	/// This class has two purposes: to avoid having to make Connection
-	/// templated on what type of connection it is controlling, and to avoid
-	/// some messiness with templated friend classes
-	class SignalBase {
-		friend class ConnectionToken;
-		/// @brief Disconnect the passed slot from the signal
-		/// @param tok Token to disconnect
-		virtual void Disconnect(ConnectionToken *tok)=0;
-
-		/// Signals can't be copied
-		SignalBase(SignalBase const&) = delete;
-		SignalBase& operator=(SignalBase const&) = delete;
-	protected:
-		SignalBase() = default;
-		/// @brief Notify a slot that it has been disconnected
-		/// @param tok Token to disconnect
-		///
-		/// Used by the signal when the signal wishes to end a connection (such
-		/// as if the signal is being destroyed while slots are still connected
-		/// to it)
-		void DisconnectToken(ConnectionToken *tok) { tok->signal = nullptr; }
-
-		/// @brief Has a token been claimed by a scoped connection object?
-		bool TokenClaimed(ConnectionToken *tok) { return tok->claimed; }
-
-		/// @brief Create a new connection to this slot
-		ConnectionToken *MakeToken() { return new ConnectionToken(this); }
-
-		/// @brief Check if a connection currently wants to receive signals
-		bool Blocked(ConnectionToken *tok) { return tok->blocked; }
-	};
-
-	inline void ConnectionToken::Disconnect() {
-		if (signal) signal->Disconnect(this);
-		signal = nullptr;
-	}
+/// Polymorphic base class for slots
+///
+/// This class has two purposes: to avoid having to make Connection
+/// templated on what type of connection it is controlling, and to avoid
+/// some messiness with templated friend classes
+class SignalBase {
+    friend class ConnectionToken;
+    /// @brief Disconnect the passed slot from the signal
+    /// @param tok Token to disconnect
+    virtual void Disconnect(ConnectionToken *tok) = 0;
+
+    /// Signals can't be copied
+    SignalBase(SignalBase const &) = delete;
+    SignalBase &operator=(SignalBase const &) = delete;
+protected:
+    SignalBase() = default;
+    /// @brief Notify a slot that it has been disconnected
+    /// @param tok Token to disconnect
+    ///
+    /// Used by the signal when the signal wishes to end a connection (such
+    /// as if the signal is being destroyed while slots are still connected
+    /// to it)
+    void DisconnectToken(ConnectionToken *tok) { tok->signal = nullptr; }
+
+    /// @brief Has a token been claimed by a scoped connection object?
+    bool TokenClaimed(ConnectionToken *tok) { return tok->claimed; }
+
+    /// @brief Create a new connection to this slot
+    ConnectionToken *MakeToken() { return new ConnectionToken(this); }
+
+    /// @brief Check if a connection currently wants to receive signals
+    bool Blocked(ConnectionToken *tok) { return tok->blocked; }
+};
+
+inline void ConnectionToken::Disconnect()
+{
+    if (signal) signal->Disconnect(this);
+    signal = nullptr;
+}
 }
 
 template<typename... Args>
 class Signal final : private detail::SignalBase {
-	using Slot = std::function<void(Args...)>;
-	std::vector<std::pair<detail::ConnectionToken*, Slot>> slots; /// Signals currently connected to this slot
-
-	void Disconnect(detail::ConnectionToken *tok) override {
-		for (auto it = begin(slots), e = end(slots); it != e; ++it) {
-			if (tok == it->first) {
-				slots.erase(it);
-				return;
-			}
-		}
-	}
-
-	UnscopedConnection DoConnect(Slot sig) {
-		auto token = MakeToken();
-		slots.emplace_back(token, sig);
-		return UnscopedConnection(token);
-	}
+    using Slot = std::function<void(Args...)>;
+    std::vector<std::pair<detail::ConnectionToken *, Slot>> slots; /// Signals currently connected to this slot
+
+    void Disconnect(detail::ConnectionToken *tok) override {
+        for (auto it = begin(slots), e = end(slots); it != e; ++it) {
+            if (tok == it->first) {
+                slots.erase(it);
+                return;
+            }
+        }
+    }
+
+    UnscopedConnection DoConnect(Slot sig) {
+        auto token = MakeToken();
+        slots.emplace_back(token, sig);
+        return UnscopedConnection(token);
+    }
 
 public:
-	~Signal() {
-		for (auto& slot : slots) {
-			DisconnectToken(slot.first);
-			if (!TokenClaimed(slot.first)) delete slot.first;
-		}
-	}
-
-	/// Trigger this signal
-	///
-	/// The order in which connected slots are called is undefined and should
-	/// not be relied on
-	void operator()(Args... args) {
-		for (size_t i = slots.size(); i > 0; --i) {
-			if (!Blocked(slots[i - 1].first))
-				slots[i - 1].second(args...);
-		}
-	}
-
-	/// @brief Connect a signal to this slot
-	/// @param sig Signal to connect
-	/// @return The connection object
-	UnscopedConnection Connect(Slot sig) {
-		return DoConnect(sig);
-	}
-
-	// Convenience wrapper for a member function which matches the signal's signature
-	template<typename T>
-	UnscopedConnection Connect(void (T::*func)(Args...), T* a1) {
-		return DoConnect([=](Args... args) { (a1->*func)(args...); });
-	}
-
-	// Convenience wrapper for a callable which does not use any signal args
-	template<typename Thunk, typename = decltype((*(Thunk *)0)())>
-	UnscopedConnection Connect(Thunk&& func) {
-		return DoConnect([=](Args... args) mutable { func(); });
-	}
-
-	// Convenience wrapper for a member function which does not use any signal
-	// args. The match is overly-broad to avoid having two methods with the
-	// same signature when the signal has no args.
-	template<typename T, typename MemberThunk>
-	UnscopedConnection Connect(MemberThunk func, T* obj) {
-		return DoConnect([=](Args... args) { (obj->*func)(); });
-	}
-
-	// Convenience wrapper for a member function which uses only the first
-	// signal arg.
-	template<typename T, typename Arg1>
-	UnscopedConnection Connect(void (T::*func)(Arg1), T* a1) {
-		return DoConnect(std::bind(func, a1, std::placeholders::_1));
-	}
+    ~Signal() {
+        for (auto &slot : slots) {
+            DisconnectToken(slot.first);
+            if (!TokenClaimed(slot.first)) delete slot.first;
+        }
+    }
+
+    /// Trigger this signal
+    ///
+    /// The order in which connected slots are called is undefined and should
+    /// not be relied on
+    void operator()(Args... args) {
+        for (size_t i = slots.size(); i > 0; --i) {
+            if (!Blocked(slots[i - 1].first))
+                slots[i - 1].second(args...);
+        }
+    }
+
+    /// @brief Connect a signal to this slot
+    /// @param sig Signal to connect
+    /// @return The connection object
+    UnscopedConnection Connect(Slot sig) {
+        return DoConnect(sig);
+    }
+
+    // Convenience wrapper for a member function which matches the signal's signature
+    template<typename T>
+    UnscopedConnection Connect(void (T::*func)(Args...), T *a1) {
+        return DoConnect([ = ](Args... args) { (a1->*func)(args...); });
+    }
+
+    // Convenience wrapper for a callable which does not use any signal args
+    template<typename Thunk, typename = decltype((*(Thunk *)0)())>
+    UnscopedConnection Connect(Thunk && func) {
+        return DoConnect([ = ](Args... args) mutable { func(); });
+    }
+
+    // Convenience wrapper for a member function which does not use any signal
+    // args. The match is overly-broad to avoid having two methods with the
+    // same signature when the signal has no args.
+    template<typename T, typename MemberThunk>
+    UnscopedConnection Connect(MemberThunk func, T *obj) {
+        return DoConnect([ = ](Args... args) { (obj->*func)(); });
+    }
+
+    // Convenience wrapper for a member function which uses only the first
+    // signal arg.
+    template<typename T, typename Arg1>
+    UnscopedConnection Connect(void (T::*func)(Arg1), T *a1) {
+        return DoConnect(std::bind(func, a1, std::placeholders::_1));
+    }
 };
 
 /// Create a vector of scoped connections from an initializer list
@@ -203,11 +205,13 @@ public:
 /// Required due to that initializer lists copy their input, and trying to pass
 /// an initializer list directly to a vector results in a
 /// std::initializer_list<Connection>, which can't be copied.
-inline std::vector<Connection> make_vector(std::initializer_list<UnscopedConnection> connections) {
-	return std::vector<Connection>(std::begin(connections), std::end(connections));
+inline std::vector<Connection> make_vector(std::initializer_list<UnscopedConnection> connections)
+{
+    return std::vector<Connection>(std::begin(connections), std::end(connections));
 }
 
-} }
+}
+}
 
 /// @brief Define functions which forward their arguments to the connect method
 ///        of the named signal
diff --git a/libaegisub/include/libaegisub/spellchecker.h b/libaegisub/include/libaegisub/spellchecker.h
index 450a39407c64fae06cdebfd924a2fb099e46d03b..87504decff31d1dd3d28ba1bd05421ecc04bb1db 100644
--- a/libaegisub/include/libaegisub/spellchecker.h
+++ b/libaegisub/include/libaegisub/spellchecker.h
@@ -22,38 +22,38 @@
 namespace agi {
 class SpellChecker {
 public:
-	virtual ~SpellChecker() { }
-
-	/// Add word to the dictionary
-	/// @param word Word to add
-	virtual void AddWord(std::string const& word)=0;
-
-	/// Remove word from the dictionary
-	/// @param word Word to remove
-	virtual void RemoveWord(std::string const& word)=0;
-
-	/// Can the word be added to the current dictionary?
-	/// @param word Word to check
-	/// @return Whether or not word can be added
-	virtual bool CanAddWord(std::string const& word)=0;
-
-	/// Can the word be removed from the current dictionary?
-	/// @param word Word to check
-	/// @return Whether or not word can be removed
-	virtual bool CanRemoveWord(std::string const& word)=0;
-
-	/// Check if the given word is spelled correctly
-	/// @param word Word to check
-	/// @return Whether or not the word is valid
-	virtual bool CheckWord(std::string const& word)=0;
-
-	/// Get possible corrections for a misspelled word
-	/// @param word Word to get suggestions for
-	/// @return List of suggestions, if any
-	virtual std::vector<std::string> GetSuggestions(std::string const& word)=0;
-
-	/// Get a list of languages which dictionaries are present for
-	virtual std::vector<std::string> GetLanguageList()=0;
+    virtual ~SpellChecker() { }
+
+    /// Add word to the dictionary
+    /// @param word Word to add
+    virtual void AddWord(std::string const &word) = 0;
+
+    /// Remove word from the dictionary
+    /// @param word Word to remove
+    virtual void RemoveWord(std::string const &word) = 0;
+
+    /// Can the word be added to the current dictionary?
+    /// @param word Word to check
+    /// @return Whether or not word can be added
+    virtual bool CanAddWord(std::string const &word) = 0;
+
+    /// Can the word be removed from the current dictionary?
+    /// @param word Word to check
+    /// @return Whether or not word can be removed
+    virtual bool CanRemoveWord(std::string const &word) = 0;
+
+    /// Check if the given word is spelled correctly
+    /// @param word Word to check
+    /// @return Whether or not the word is valid
+    virtual bool CheckWord(std::string const &word) = 0;
+
+    /// Get possible corrections for a misspelled word
+    /// @param word Word to get suggestions for
+    /// @return List of suggestions, if any
+    virtual std::vector<std::string> GetSuggestions(std::string const &word) = 0;
+
+    /// Get a list of languages which dictionaries are present for
+    virtual std::vector<std::string> GetLanguageList() = 0;
 };
 
 }
diff --git a/libaegisub/include/libaegisub/split.h b/libaegisub/include/libaegisub/split.h
index 0b152b2245a24fc41a1d47335ab8dd54588f9c0e..b143b218b4b9ddead0489d4daa6c916afcb52d4d 100644
--- a/libaegisub/include/libaegisub/split.h
+++ b/libaegisub/include/libaegisub/split.h
@@ -17,93 +17,97 @@
 #include <boost/range/iterator_range.hpp>
 
 namespace agi {
-	typedef boost::iterator_range<std::string::const_iterator> StringRange;
-
-	template<typename Iterator>
-	class split_iterator {
-		bool is_end = false;
-		Iterator b;
-		Iterator cur;
-		Iterator e;
-		typename Iterator::value_type c;
-
-	public:
-		using iterator_category = std::forward_iterator_tag;
-		using value_type = boost::iterator_range<Iterator>;
-		using pointer = value_type*;
-		using reference = value_type&;
-		using difference_type = ptrdiff_t;
-
-		split_iterator(Iterator begin, Iterator end, typename Iterator::value_type c)
-		: b(begin), cur(begin), e(end), c(c)
-		{
-			if (b != e)
-				cur = std::find(b, e, c);
-			else
-				is_end = true;
-		}
-
-		split_iterator() : is_end(true) { }
-
-		bool eof() const { return is_end; }
-
-		boost::iterator_range<Iterator> operator*() const {
-			return boost::make_iterator_range(b, cur);
-		}
-
-		bool operator==(split_iterator const& it) const {
-			if (is_end || it.is_end)
-				return is_end && it.is_end;
-			return b == it.b && cur == it.cur && e == it.e && c == it.c;
-		}
-
-		bool operator!=(split_iterator const& it) const {
-			return !(*this == it);
-		}
-
-		split_iterator& operator++() {
-			if (cur != e) {
-				b = cur + 1;
-				cur = std::find(b, e, c);
-			}
-			else {
-				b = e;
-				is_end = true;
-			}
-
-			return *this;
-		}
-
-		split_iterator operator++(int) {
-			split_iterator tmp = *this;
-			++*this;
-			return tmp;
-		}
-	};
-
-	template<typename Iterator>
-	split_iterator<Iterator> begin(split_iterator<Iterator> const& it) {
-		return it;
-	}
-
-	template<typename Iterator>
-	split_iterator<Iterator> end(split_iterator<Iterator> const&) {
-		return split_iterator<Iterator>();
-	}
-
-	static inline std::string str(StringRange const& r) {
-		return std::string(r.begin(), r.end());
-	}
-
-	template<typename Str, typename Char>
-	split_iterator<typename Str::const_iterator> Split(Str const& str, Char delim) {
-		return split_iterator<typename Str::const_iterator>(begin(str), end(str), delim);
-	}
-
-	template<typename Cont, typename Str, typename Char>
-	void Split(Cont& out, Str const& str, Char delim) {
-		out.clear();
-		for (auto const& tok : Split(str, delim))
-			out.emplace_back(begin(tok), end(tok));
-	}
+typedef boost::iterator_range<std::string::const_iterator> StringRange;
+
+template<typename Iterator>
+class split_iterator {
+    bool is_end = false;
+    Iterator b;
+    Iterator cur;
+    Iterator e;
+    typename Iterator::value_type c;
+
+public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = boost::iterator_range<Iterator>;
+    using pointer = value_type*;
+    using reference = value_type&;
+    using difference_type = ptrdiff_t;
+
+    split_iterator(Iterator begin, Iterator end, typename Iterator::value_type c)
+        : b(begin), cur(begin), e(end), c(c) {
+        if (b != e)
+            cur = std::find(b, e, c);
+        else
+            is_end = true;
+    }
+
+    split_iterator() : is_end(true) { }
+
+    bool eof() const { return is_end; }
+
+    boost::iterator_range<Iterator> operator*() const {
+        return boost::make_iterator_range(b, cur);
+    }
+
+    bool operator==(split_iterator const &it) const {
+        if (is_end || it.is_end)
+            return is_end && it.is_end;
+        return b == it.b && cur == it.cur && e == it.e && c == it.c;
+    }
+
+    bool operator!=(split_iterator const &it) const {
+        return !(*this == it);
+    }
+
+    split_iterator &operator++() {
+        if (cur != e) {
+            b = cur + 1;
+            cur = std::find(b, e, c);
+        }
+        else {
+            b = e;
+            is_end = true;
+        }
+
+        return *this;
+    }
+
+    split_iterator operator++(int) {
+        split_iterator tmp = *this;
+        ++*this;
+        return tmp;
+    }
+};
+
+template<typename Iterator>
+split_iterator<Iterator> begin(split_iterator<Iterator> const &it)
+{
+    return it;
+}
+
+template<typename Iterator>
+split_iterator<Iterator> end(split_iterator<Iterator> const &)
+{
+    return split_iterator<Iterator>();
+}
+
+static inline std::string str(StringRange const &r)
+{
+    return std::string(r.begin(), r.end());
+}
+
+template<typename Str, typename Char>
+split_iterator<typename Str::const_iterator> Split(Str const &str, Char delim)
+{
+    return split_iterator<typename Str::const_iterator>(begin(str), end(str), delim);
+}
+
+template<typename Cont, typename Str, typename Char>
+void Split(Cont &out, Str const &str, Char delim)
+{
+    out.clear();
+    for (auto const &tok : Split(str, delim))
+        out.emplace_back(begin(tok), end(tok));
+}
 }
diff --git a/libaegisub/include/libaegisub/thesaurus.h b/libaegisub/include/libaegisub/thesaurus.h
index 887d59b13accc6547fe411a143720e282bd3a544..70780cc7eca4c8c26bc5ec821a4cd543a7a02f30 100644
--- a/libaegisub/include/libaegisub/thesaurus.h
+++ b/libaegisub/include/libaegisub/thesaurus.h
@@ -26,26 +26,26 @@ class read_file_mapping;
 namespace charset { class IconvWrapper; }
 
 class Thesaurus {
-	/// Map of word -> byte position in the data file
-	boost::container::flat_map<std::string, size_t> offsets;
-	/// Read handle to the data file
-	std::unique_ptr<read_file_mapping> dat;
-	/// Converter from the data file's charset to UTF-8
-	std::unique_ptr<charset::IconvWrapper> conv;
+    /// Map of word -> byte position in the data file
+    boost::container::flat_map<std::string, size_t> offsets;
+    /// Read handle to the data file
+    std::unique_ptr<read_file_mapping> dat;
+    /// Converter from the data file's charset to UTF-8
+    std::unique_ptr<charset::IconvWrapper> conv;
 
 public:
-	/// A pair of a word and synonyms for that word
-	typedef std::pair<std::string, std::vector<std::string>> Entry;
-
-	/// Constructor
-	/// @param dat_path Path to data file
-	/// @param idx_path Path to index file
-	Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path);
-	~Thesaurus();
-
-	/// Look up synonyms for a word
-	/// @param word Word to look up
-	std::vector<Entry> Lookup(std::string const& word);
+    /// A pair of a word and synonyms for that word
+    typedef std::pair<std::string, std::vector<std::string>> Entry;
+
+    /// Constructor
+    /// @param dat_path Path to data file
+    /// @param idx_path Path to index file
+    Thesaurus(agi::fs::path const &dat_path, agi::fs::path const &idx_path);
+    ~Thesaurus();
+
+    /// Look up synonyms for a word
+    /// @param word Word to look up
+    std::vector<Entry> Lookup(std::string const &word);
 };
 
 }
diff --git a/libaegisub/include/libaegisub/type_name.h b/libaegisub/include/libaegisub/type_name.h
index 75f0215410471fef2414d6da6d6c427b86ccb684..6abd41d1f79dc926d005a55a172dcc687b391217 100644
--- a/libaegisub/include/libaegisub/type_name.h
+++ b/libaegisub/include/libaegisub/type_name.h
@@ -53,26 +53,28 @@ AGI_TYPE_NAME_MODIFIER(" ", const);
 #undef AGI_TYPE_NAME_MODIFIER
 
 template<typename First>
-std::string function_args(bool is_first) {
-	return std::string(is_first ? "" : ", ") + type_name<First>::name() + ")";
+std::string function_args(bool is_first)
+{
+    return std::string(is_first ? "" : ", ") + type_name<First>::name() + ")";
 }
 
 template<typename First, typename Second, typename... Rest>
-std::string function_args(bool is_first) {
-	return std::string(is_first ? "" : ", ") + type_name<First>::name() + function_args<Second, Rest...>(false);
+std::string function_args(bool is_first)
+{
+    return std::string(is_first ? "" : ", ") + type_name<First>::name() + function_args<Second, Rest...>(false);
 }
 
 template<typename Return, typename... Args>
 struct type_name<Return (*)(Args...)> {
-	static std::string name() {
-		return std::string(type_name<Return>::name()) + " (*)(" + function_args<Args...>(true);
-	}
+    static std::string name() {
+        return std::string(type_name<Return>::name()) + " (*)(" + function_args<Args...>(true);
+    }
 };
 
 template<typename Return>
 struct type_name<Return (*)()> {
-	static std::string name() {
-		return std::string(type_name<Return>::name()) + " (*)()";
-	}
+    static std::string name() {
+        return std::string(type_name<Return>::name()) + " (*)()";
+    }
 };
 }
diff --git a/libaegisub/include/libaegisub/util.h b/libaegisub/include/libaegisub/util.h
index 4aa34619f053a53672e9007f1625a572d7e63d5d..a9ca2351707c216fca455952a2edce6c5fe20c3b 100644
--- a/libaegisub/include/libaegisub/util.h
+++ b/libaegisub/include/libaegisub/util.h
@@ -19,65 +19,70 @@
 
 struct tm;
 
-namespace agi { namespace util {
-	/// Clamp `b` to the range [`a`,`c`]
-	template<typename T>
-	static inline T mid(T a, T b, T c) {
-		return std::max(a, std::min(b, c));
-	}
+namespace agi {
+namespace util {
+/// Clamp `b` to the range [`a`,`c`]
+template<typename T>
+static inline T mid(T a, T b, T c)
+{
+    return std::max(a, std::min(b, c));
+}
 
-	bool try_parse(std::string const& str, double *out);
-	bool try_parse(std::string const& str, int *out);
+bool try_parse(std::string const &str, double *out);
+bool try_parse(std::string const &str, int *out);
 
-	/// 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);
+/// 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);
 
-	/// Case-insensitive find with proper case folding
-	/// @param haystack String to search
-	/// @param needle String to look for
-	/// @return make_pair(-1,-1) if `needle` could not be found, or a range equivalent to `needle` in `haystack` if it could
-	///
-	/// `needle` and `haystack` must both be in Normalization Form D. The size
-	/// of the match might be different from the size of `needle`, since it's
-	/// based on the unfolded length.
-	std::pair<size_t, size_t> ifind(std::string const& haystack, std::string const& needle);
+/// Case-insensitive find with proper case folding
+/// @param haystack String to search
+/// @param needle String to look for
+/// @return make_pair(-1,-1) if `needle` could not be found, or a range equivalent to `needle` in `haystack` if it could
+///
+/// `needle` and `haystack` must both be in Normalization Form D. The size
+/// of the match might be different from the size of `needle`, since it's
+/// based on the unfolded length.
+std::pair<size_t, size_t> ifind(std::string const &haystack, std::string const &needle);
 
-	class tagless_find_helper {
-		std::vector<std::pair<size_t, size_t>> blocks;
-		size_t start = 0;
+class tagless_find_helper {
+    std::vector<std::pair<size_t, size_t>> blocks;
+    size_t start = 0;
 
-	public:
-		/// Strip ASS override tags at or after `start` in `str`, and initialize
-		/// state for mapping ranges back to the input string
-		std::string strip_tags(std::string const& str, size_t start);
+public:
+    /// Strip ASS override tags at or after `start` in `str`, and initialize
+    /// state for mapping ranges back to the input string
+    std::string strip_tags(std::string const &str, size_t start);
 
-		/// Convert a range in the string returned by `strip_tags()` to a range
-		/// int the string last passed to `strip_tags()`
-		void map_range(size_t& start, size_t& end);
-	};
+    /// Convert a range in the string returned by `strip_tags()` to a range
+    /// int the string last passed to `strip_tags()`
+    void map_range(size_t &start, size_t &end);
+};
 
-	/// Set the name of the calling thread in the Visual Studio debugger
-	/// @param name New name for the thread
-	void SetThreadName(const char *name);
+/// Set the name of the calling thread in the Visual Studio debugger
+/// @param name New name for the thread
+void SetThreadName(const char *name);
 
-	/// A thin wrapper around this_thread::sleep_for that uses std::thread on
-	/// Windows (to avoid having to compile boost.thread) and boost::thread
-	/// elsewhere (because libstcc++ 4.7 is missing it).
-	void sleep_for(int ms);
+/// A thin wrapper around this_thread::sleep_for that uses std::thread on
+/// Windows (to avoid having to compile boost.thread) and boost::thread
+/// elsewhere (because libstcc++ 4.7 is missing it).
+void sleep_for(int ms);
 
-	// boost.range doesn't have wrappers for the C++11 stuff
-	template<typename Range, typename Predicate>
-	bool any_of(Range&& r, Predicate&& p) {
-		return std::any_of(std::begin(r), std::end(r), std::forward<Predicate>(p));
-	}
+// boost.range doesn't have wrappers for the C++11 stuff
+template<typename Range, typename Predicate>
+bool any_of(Range &&r, Predicate &&p)
+{
+    return std::any_of(std::begin(r), std::end(r), std::forward<Predicate>(p));
+}
 
-	std::string ErrorString(int error);
+std::string ErrorString(int error);
 
-	template<typename Integer>
-	auto range(Integer end) -> decltype(boost::irange<Integer>(0, end)) {
-		return boost::irange<Integer>(0, end);
-	}
-} } // namespace agi::util
+template<typename Integer>
+auto range(Integer end) -> decltype(boost::irange<Integer>(0, end))
+{
+    return boost::irange<Integer>(0, end);
+}
+}
+} // namespace agi::util
diff --git a/libaegisub/include/libaegisub/util_osx.h b/libaegisub/include/libaegisub/util_osx.h
index 9c839e181e16ea1df69d7394895ba6f36b356f9b..7046bf516bb504b480b24ebcfb072db1e6383054 100644
--- a/libaegisub/include/libaegisub/util_osx.h
+++ b/libaegisub/include/libaegisub/util_osx.h
@@ -31,15 +31,15 @@
 #include <string>
 
 namespace agi {
-	namespace osx {
-		class AppNapDisabler {
-			__attribute__((unused)) void *handle;
-		public:
-			AppNapDisabler(std::string reason);
-			~AppNapDisabler();
-		};
-	}
-    namespace util {
+namespace osx {
+class AppNapDisabler {
+    __attribute__((unused)) void *handle;
+public:
+    AppNapDisabler(std::string reason);
+    ~AppNapDisabler();
+};
+}
+namespace util {
 /// @brief Get the esources directory.
 /// @return Resources directory.
 ///
@@ -54,5 +54,5 @@ std::string GetBundleResourcesDirectory();
 std::string GetBundleSharedSupportDirectory();
 
 std::string GetApplicationSupportDirectory();
-    } // namespace util
+} // namespace util
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/vfr.h b/libaegisub/include/libaegisub/vfr.h
index 4f6e1b15d62f75f2c390bdfabce813e3236649da..cb6a9e35002d86cc27d807bedfe5f0147d5e65d8 100644
--- a/libaegisub/include/libaegisub/vfr.h
+++ b/libaegisub/include/libaegisub/vfr.h
@@ -21,22 +21,22 @@
 #include <libaegisub/fs_fwd.h>
 
 namespace agi {
-	/// Framerate handling.
-	namespace vfr {
+/// Framerate handling.
+namespace vfr {
 
 enum Time {
-	/// Use the actual frame times
-	/// With 1 FPS video, frame 0 is [0, 999] ms
-	EXACT,
-	/// Calculate based on the rules for start times of lines
-	/// Lines are first visible on the first frame with start time less than
-	/// or equal to the start time; thus with 1.0 FPS video, frame 0 is
-	/// [-999, 0] ms
-	START,
-	/// Calculate based on the rules for end times of lines
-	/// Lines are last visible on the last frame with start time less than the
-	/// end time; thus with 1.0 FPS video, frame 0 is [1, 1000] ms
-	END
+    /// Use the actual frame times
+    /// With 1 FPS video, frame 0 is [0, 999] ms
+    EXACT,
+    /// Calculate based on the rules for start times of lines
+    /// Lines are first visible on the first frame with start time less than
+    /// or equal to the start time; thus with 1.0 FPS video, frame 0 is
+    /// [-999, 0] ms
+    START,
+    /// Calculate based on the rules for end times of lines
+    /// Lines are last visible on the last frame with start time less than the
+    /// end time; thus with 1.0 FPS video, frame 0 is [1, 1000] ms
+    END
 };
 
 DEFINE_EXCEPTION(Error, Exception);
@@ -51,159 +51,159 @@ DEFINE_EXCEPTION(MalformedLine, Error);
 /// @brief Class for managing everything related to converting frames to times
 ///        or vice versa
 class Framerate {
-	/// Denominator of the FPS
-	///
-	/// For v1 VFR, the assumed FPS is used, for v2 the average FPS
-	int64_t denominator = 0;
-	/// Numerator of the FPS
-	///
-	/// For v1 VFR, the assumed FPS is used, for v2 the average FPS
-	int64_t numerator = 0;
-
-	/// Unrounded frame-seconds of the final frame in timecodes. For CFR and v2,
-	/// this is simply frame count * denominator, but for v1 it's the
-	/// "unrounded" frame count, since override ranges generally don't exactly
-	/// cover timebase-unit ranges of time. This is needed to match mkvmerge's
-	/// rounding past the end of the final override range.
-	int64_t last = 0;
-
-	/// Start time in milliseconds of each frame
-	std::vector<int> timecodes;
-
-	/// Does this frame rate need drop frames and have them enabled?
-	bool drop = false;
-
-	/// Set FPS properties from the timecodes vector
-	void SetFromTimecodes();
+    /// Denominator of the FPS
+    ///
+    /// For v1 VFR, the assumed FPS is used, for v2 the average FPS
+    int64_t denominator = 0;
+    /// Numerator of the FPS
+    ///
+    /// For v1 VFR, the assumed FPS is used, for v2 the average FPS
+    int64_t numerator = 0;
+
+    /// Unrounded frame-seconds of the final frame in timecodes. For CFR and v2,
+    /// this is simply frame count * denominator, but for v1 it's the
+    /// "unrounded" frame count, since override ranges generally don't exactly
+    /// cover timebase-unit ranges of time. This is needed to match mkvmerge's
+    /// rounding past the end of the final override range.
+    int64_t last = 0;
+
+    /// Start time in milliseconds of each frame
+    std::vector<int> timecodes;
+
+    /// Does this frame rate need drop frames and have them enabled?
+    bool drop = false;
+
+    /// Set FPS properties from the timecodes vector
+    void SetFromTimecodes();
 public:
-	Framerate(Framerate const&) = default;
-	Framerate& operator=(Framerate const&) = default;
+    Framerate(Framerate const &) = default;
+    Framerate &operator=(Framerate const &) = default;
 
 #ifndef _MSC_VER
-	Framerate(Framerate&&) = default;
-	Framerate& operator=(Framerate&&) = default;
+    Framerate(Framerate &&) = default;
+    Framerate &operator=(Framerate &&) = default;
 #endif
 
-	/// @brief VFR from timecodes file
-	/// @param filename File with v1 or v2 timecodes
-	///
-	/// Note that loading a v1 timecode file with Assume X and no overrides is
-	/// 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(fs::path const& filename);
-
-	/// @brief CFR constructor
-	/// @param fps Frames per second or 0 for unloaded
-	Framerate(double fps = 0.);
-
-	/// @brief CFR constructor with rational timebase
-	/// @param numerator Timebase numerator
-	/// @param denominator Timebase denominator
-	/// @param drop Enable drop frames if the FPS requires it
-	Framerate(int64_t numerator, int64_t denominator, bool drop=true);
-
-	/// @brief VFR from frame times
-	/// @param timecodes Vector of frame start times in milliseconds
-	Framerate(std::vector<int> timecodes);
-	Framerate(std::initializer_list<int> timecodes);
-
-	/// @brief Get the frame visible at a given time
-	/// @param ms Time in milliseconds
-	/// @param type Time mode
-	///
-	/// When type is EXACT, the frame returned is the frame visible at the given
-	/// time; when it is START or END it is the frame on which a line with that
-	/// start/end time would first/last be visible
-	int FrameAtTime(int ms, Time type = EXACT) const;
-
-	/// @brief Get the time at a given frame
-	/// @param frame Frame number
-	/// @param type Time mode
-	///
-	/// When type is EXACT, the frame's exact start time is returned; START and
-	/// END give a time somewhere within the range that will result in the line
-	/// starting/ending on that frame
-	///
-	/// With v2 timecodes, frames outside the defined range are not an error
-	/// and are guaranteed to be monotonically increasing/decreasing values
-	/// which when passed to FrameAtTime will return the original frame; they
-	/// are not guaranteed to be sensible or useful for any other purpose
-	///
-	/// v1 timecodes and CFR do not have a defined range, and will give sensible
-	/// results for all frame numbers
-	int TimeAtFrame(int frame, Time type = EXACT) const;
-
-	/// @brief Get the components of the SMPTE timecode for the given time
-	/// @param[out] h Hours component
-	/// @param[out] m Minutes component
-	/// @param[out] s Seconds component
-	/// @param[out] f Frames component
-	///
-	/// For NTSC (30000/1001), this generates proper SMPTE timecodes with drop
-	/// frames handled. For multiples of NTSC, this multiplies the number of
-	/// dropped frames. For other non-integral frame rates, it drops frames in
-	/// an undefined manner which results in no more than half a second error
-	/// from wall clock time.
-	///
-	/// For integral frame rates, no frame dropping occurs.
-	void SmpteAtTime(int ms, int *h, int *m, int *s, int *f) const;
-
-	/// @brief Get the components of the SMPTE timecode for the given frame
-	/// @param[out] h Hours component
-	/// @param[out] m Minutes component
-	/// @param[out] s Seconds component
-	/// @param[out] f Frames component
-	///
-	/// For NTSC (30000/1001), this generates proper SMPTE timecodes with drop
-	/// frames handled. For multiples of NTSC, this multiplies the number of
-	/// dropped frames. For other non-integral frame rates, it drops frames in
-	/// an undefined manner which results in no more than half a second error
-	/// from wall clock time.
-	///
-	/// For integral frame rates, no frame dropping occurs.
-	void SmpteAtFrame(int frame, int *h, int *m, int *s, int *f) const;
-
-	/// @brief Get the frame indicated by the SMPTE timecode components
-	/// @param h Hours component
-	/// @param m Minutes component
-	/// @param s Seconds component
-	/// @param f Frames component
-	/// @return Frame number
-	/// @see SmpteAtFrame
-	int FrameAtSmpte(int h, int m, int s, int f) const;
-
-	/// @brief Get the time indicated by the SMPTE timecode components
-	/// @param h Hours component
-	/// @param m Minutes component
-	/// @param s Seconds component
-	/// @param f Frames component
-	/// @return Time in milliseconds
-	/// @see SmpteAtTime
-	int TimeAtSmpte(int h, int m, int s, int f) const;
-
-	/// @brief Save the current time codes to a file as v2 timecodes
-	/// @param file File name
-	/// @param length Minimum number of frames to output
-	///
-	/// The length parameter is only particularly useful for v1 timecodes (and
-	/// 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(fs::path const& file, int length = -1) const;
-
-	/// Is this frame rate possibly variable?
-	bool IsVFR() const {return timecodes.size() > 1; }
-
-	/// Does this represent a valid frame rate?
-	bool IsLoaded() const { return numerator > 0; }
-
-	/// Get average FPS of this frame rate
-	double FPS() const { return double(numerator) / denominator; }
-
-	/// Does this frame rate need drop frames for SMPTE timeish frame numbers?
-	bool NeedsDropFrames() const { return drop; }
+    /// @brief VFR from timecodes file
+    /// @param filename File with v1 or v2 timecodes
+    ///
+    /// Note that loading a v1 timecode file with Assume X and no overrides is
+    /// 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(fs::path const &filename);
+
+    /// @brief CFR constructor
+    /// @param fps Frames per second or 0 for unloaded
+    Framerate(double fps = 0.);
+
+    /// @brief CFR constructor with rational timebase
+    /// @param numerator Timebase numerator
+    /// @param denominator Timebase denominator
+    /// @param drop Enable drop frames if the FPS requires it
+    Framerate(int64_t numerator, int64_t denominator, bool drop = true);
+
+    /// @brief VFR from frame times
+    /// @param timecodes Vector of frame start times in milliseconds
+    Framerate(std::vector<int> timecodes);
+    Framerate(std::initializer_list<int> timecodes);
+
+    /// @brief Get the frame visible at a given time
+    /// @param ms Time in milliseconds
+    /// @param type Time mode
+    ///
+    /// When type is EXACT, the frame returned is the frame visible at the given
+    /// time; when it is START or END it is the frame on which a line with that
+    /// start/end time would first/last be visible
+    int FrameAtTime(int ms, Time type = EXACT) const;
+
+    /// @brief Get the time at a given frame
+    /// @param frame Frame number
+    /// @param type Time mode
+    ///
+    /// When type is EXACT, the frame's exact start time is returned; START and
+    /// END give a time somewhere within the range that will result in the line
+    /// starting/ending on that frame
+    ///
+    /// With v2 timecodes, frames outside the defined range are not an error
+    /// and are guaranteed to be monotonically increasing/decreasing values
+    /// which when passed to FrameAtTime will return the original frame; they
+    /// are not guaranteed to be sensible or useful for any other purpose
+    ///
+    /// v1 timecodes and CFR do not have a defined range, and will give sensible
+    /// results for all frame numbers
+    int TimeAtFrame(int frame, Time type = EXACT) const;
+
+    /// @brief Get the components of the SMPTE timecode for the given time
+    /// @param[out] h Hours component
+    /// @param[out] m Minutes component
+    /// @param[out] s Seconds component
+    /// @param[out] f Frames component
+    ///
+    /// For NTSC (30000/1001), this generates proper SMPTE timecodes with drop
+    /// frames handled. For multiples of NTSC, this multiplies the number of
+    /// dropped frames. For other non-integral frame rates, it drops frames in
+    /// an undefined manner which results in no more than half a second error
+    /// from wall clock time.
+    ///
+    /// For integral frame rates, no frame dropping occurs.
+    void SmpteAtTime(int ms, int *h, int *m, int *s, int *f) const;
+
+    /// @brief Get the components of the SMPTE timecode for the given frame
+    /// @param[out] h Hours component
+    /// @param[out] m Minutes component
+    /// @param[out] s Seconds component
+    /// @param[out] f Frames component
+    ///
+    /// For NTSC (30000/1001), this generates proper SMPTE timecodes with drop
+    /// frames handled. For multiples of NTSC, this multiplies the number of
+    /// dropped frames. For other non-integral frame rates, it drops frames in
+    /// an undefined manner which results in no more than half a second error
+    /// from wall clock time.
+    ///
+    /// For integral frame rates, no frame dropping occurs.
+    void SmpteAtFrame(int frame, int *h, int *m, int *s, int *f) const;
+
+    /// @brief Get the frame indicated by the SMPTE timecode components
+    /// @param h Hours component
+    /// @param m Minutes component
+    /// @param s Seconds component
+    /// @param f Frames component
+    /// @return Frame number
+    /// @see SmpteAtFrame
+    int FrameAtSmpte(int h, int m, int s, int f) const;
+
+    /// @brief Get the time indicated by the SMPTE timecode components
+    /// @param h Hours component
+    /// @param m Minutes component
+    /// @param s Seconds component
+    /// @param f Frames component
+    /// @return Time in milliseconds
+    /// @see SmpteAtTime
+    int TimeAtSmpte(int h, int m, int s, int f) const;
+
+    /// @brief Save the current time codes to a file as v2 timecodes
+    /// @param file File name
+    /// @param length Minimum number of frames to output
+    ///
+    /// The length parameter is only particularly useful for v1 timecodes (and
+    /// 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(fs::path const &file, int length = -1) const;
+
+    /// Is this frame rate possibly variable?
+    bool IsVFR() const {return timecodes.size() > 1; }
+
+    /// Does this represent a valid frame rate?
+    bool IsLoaded() const { return numerator > 0; }
+
+    /// Get average FPS of this frame rate
+    double FPS() const { return double(numerator) / denominator; }
+
+    /// Does this frame rate need drop frames for SMPTE timeish frame numbers?
+    bool NeedsDropFrames() const { return drop; }
 };
 
-	} // namespace vfr
+} // namespace vfr
 } // namespace agi
diff --git a/libaegisub/include/libaegisub/ycbcr_conv.h b/libaegisub/include/libaegisub/ycbcr_conv.h
index d79e70cefd86040d60b1ba38bf4636049ecff3a3..c265c3f0451cb14305f91f688141193d43e50e32 100644
--- a/libaegisub/include/libaegisub/ycbcr_conv.h
+++ b/libaegisub/include/libaegisub/ycbcr_conv.h
@@ -21,76 +21,76 @@
 
 namespace agi {
 enum class ycbcr_matrix {
-	bt601,
-	bt709,
-	fcc,
-	smpte_240m
+    bt601,
+    bt709,
+    fcc,
+    smpte_240m
 };
 
 enum class ycbcr_range {
-	tv,
-	pc
+    tv,
+    pc
 };
 
 /// A converter between YCbCr colorspaces and RGB
 class ycbcr_converter {
-	std::array<double, 9> from_ycbcr;
-	std::array<double, 9> to_ycbcr;
-
-	std::array<double, 3> shift_from;
-	std::array<double, 3> shift_to;
-
-	void init_dst(ycbcr_matrix dst_mat, ycbcr_range dst_range);
-	void init_src(ycbcr_matrix src_mat, ycbcr_range src_range);
-
-	template<typename T>
-	static std::array<double, 3> prod(std::array<double, 9> m, std::array<T, 3> v) {
-		return {{
-			m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
-			m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
-			m[6] * v[0] + m[7] * v[1] + m[8] * v[2],
-		}};
-	}
-
-	template<typename T, typename U>
-	static std::array<double, 3> add(std::array<T, 3> left, std::array<U, 3> right) {
-		return {{left[0] + right[0], left[1] + right[1], left[2] + right[2]}};
-	}
-
-	static uint8_t clamp(double v) {
-		auto i = static_cast<int>(v);
-		i = i > 255 ? 255 : i;
-		return i < 0 ? 0 : i;
-	}
-
-	static std::array<uint8_t, 3> to_uint8_t(std::array<double, 3> val) {
-		return {{clamp(val[0] + .5), clamp(val[1] + .5), clamp(val[2] + .5)}};
-	}
+    std::array<double, 9> from_ycbcr;
+    std::array<double, 9> to_ycbcr;
+
+    std::array<double, 3> shift_from;
+    std::array<double, 3> shift_to;
+
+    void init_dst(ycbcr_matrix dst_mat, ycbcr_range dst_range);
+    void init_src(ycbcr_matrix src_mat, ycbcr_range src_range);
+
+    template<typename T>
+    static std::array<double, 3> prod(std::array<double, 9> m, std::array<T, 3> v) {
+        return {{
+                m[0] *v[0] + m[1] *v[1] + m[2] *v[2],
+                m[3] *v[0] + m[4] *v[1] + m[5] *v[2],
+                m[6] *v[0] + m[7] *v[1] + m[8] *v[2],
+            }};
+    }
+
+    template<typename T, typename U>
+    static std::array<double, 3> add(std::array<T, 3> left, std::array<U, 3> right) {
+        return {{left[0] + right[0], left[1] + right[1], left[2] + right[2]}};
+    }
+
+    static uint8_t clamp(double v) {
+        auto i = static_cast<int>(v);
+        i = i > 255 ? 255 : i;
+        return i < 0 ? 0 : i;
+    }
+
+    static std::array<uint8_t, 3> to_uint8_t(std::array<double, 3> val) {
+        return {{clamp(val[0] + .5), clamp(val[1] + .5), clamp(val[2] + .5)}};
+    }
 
 public:
-	ycbcr_converter(ycbcr_matrix mat, ycbcr_range range);
-	ycbcr_converter(ycbcr_matrix src_mat, ycbcr_range src_range, ycbcr_matrix dst_mat, ycbcr_range dst_range);
-
-	/// Convert from rgb to dst_mat/dst_range
-	std::array<uint8_t, 3> rgb_to_ycbcr(std::array<uint8_t, 3> input) const {
-		return to_uint8_t(add(prod(to_ycbcr, input), shift_to));
-	}
-
-	/// Convert from src_mat/src_range to rgb
-	std::array<uint8_t, 3> ycbcr_to_rgb(std::array<uint8_t, 3> input) const {
-		return to_uint8_t(prod(from_ycbcr, add(input, shift_from)));
-	}
-
-	/// Convert rgb to ycbcr using src_mat and then back using dst_mat
-	std::array<uint8_t, 3> rgb_to_rgb(std::array<uint8_t, 3> input) const {
-		return to_uint8_t(prod(from_ycbcr,
-			add(add(prod(to_ycbcr, input), shift_to), shift_from)));
-	}
-
-	Color rgb_to_rgb(Color c) const {
-		auto arr = rgb_to_rgb(std::array<uint8_t, 3>{{c.r, c.g, c.b}});
-		return Color{arr[0], arr[1], arr[2], c.a};
-	}
+    ycbcr_converter(ycbcr_matrix mat, ycbcr_range range);
+    ycbcr_converter(ycbcr_matrix src_mat, ycbcr_range src_range, ycbcr_matrix dst_mat, ycbcr_range dst_range);
+
+    /// Convert from rgb to dst_mat/dst_range
+    std::array<uint8_t, 3> rgb_to_ycbcr(std::array<uint8_t, 3> input) const {
+        return to_uint8_t(add(prod(to_ycbcr, input), shift_to));
+    }
+
+    /// Convert from src_mat/src_range to rgb
+    std::array<uint8_t, 3> ycbcr_to_rgb(std::array<uint8_t, 3> input) const {
+        return to_uint8_t(prod(from_ycbcr, add(input, shift_from)));
+    }
+
+    /// Convert rgb to ycbcr using src_mat and then back using dst_mat
+    std::array<uint8_t, 3> rgb_to_rgb(std::array<uint8_t, 3> input) const {
+        return to_uint8_t(prod(from_ycbcr,
+                               add(add(prod(to_ycbcr, input), shift_to), shift_from)));
+    }
+
+    Color rgb_to_rgb(Color c) const {
+        auto arr = rgb_to_rgb(std::array<uint8_t, 3> {{c.r, c.g, c.b}});
+        return Color{arr[0], arr[1], arr[2], c.a};
+    }
 };
 }
 
diff --git a/libaegisub/lua/modules.cpp b/libaegisub/lua/modules.cpp
index 061c0b8c05eeca02023a8ce5b058a18ec80ceae3..df2bb087bf7cb4abce427a90cf2a6f62f56e7261 100644
--- a/libaegisub/lua/modules.cpp
+++ b/libaegisub/lua/modules.cpp
@@ -25,49 +25,54 @@ extern "C" int luaopen_unicode_impl(lua_State *L);
 extern "C" int luaopen_lfs_impl(lua_State *L);
 extern "C" int luaopen_lpeg(lua_State *L);
 
-namespace agi { namespace lua {
+namespace agi {
+namespace lua {
 int regex_init(lua_State *L);
 
-void preload_modules(lua_State *L) {
-	luaL_openlibs(L);
+void preload_modules(lua_State *L)
+{
+    luaL_openlibs(L);
 
-	lua_getglobal(L, "package");
-	lua_getfield(L, -1, "preload");
+    lua_getglobal(L, "package");
+    lua_getfield(L, -1, "preload");
 
-	set_field(L, "aegisub.__re_impl", luaopen_re_impl);
-	set_field(L, "aegisub.__unicode_impl", luaopen_unicode_impl);
-	set_field(L, "aegisub.__lfs_impl", luaopen_lfs_impl);
-	set_field(L, "lpeg", luaopen_lpeg);
-	set_field(L, "luabins", luaopen_luabins);
+    set_field(L, "aegisub.__re_impl", luaopen_re_impl);
+    set_field(L, "aegisub.__unicode_impl", luaopen_unicode_impl);
+    set_field(L, "aegisub.__lfs_impl", luaopen_lfs_impl);
+    set_field(L, "lpeg", luaopen_lpeg);
+    set_field(L, "luabins", luaopen_luabins);
 
-	lua_pop(L, 2);
+    lua_pop(L, 2);
 
-	register_lib_functions(L); // silence an unused static function warning
+    register_lib_functions(L); // silence an unused static function warning
 }
 
-void do_register_lib_function(lua_State *L, const char *name, const char *type_name, void *func) {
-	lua_pushvalue(L, -2); // push cast function
-	lua_pushstring(L, type_name);
-	lua_pushlightuserdata(L, func);
-	lua_call(L, 2, 1);
-	lua_setfield(L, -2, name);
+void do_register_lib_function(lua_State *L, const char *name, const char *type_name, void *func)
+{
+    lua_pushvalue(L, -2); // push cast function
+    lua_pushstring(L, type_name);
+    lua_pushlightuserdata(L, func);
+    lua_call(L, 2, 1);
+    lua_setfield(L, -2, name);
 }
 
-void do_register_lib_table(lua_State *L, std::initializer_list<const char *> types) {
-	lua_getglobal(L, "require");
-	lua_pushstring(L, "ffi");
-	lua_call(L, 1, 1);
+void do_register_lib_table(lua_State *L, std::initializer_list<const char *> types)
+{
+    lua_getglobal(L, "require");
+    lua_pushstring(L, "ffi");
+    lua_call(L, 1, 1);
 
-	// Register all passed type with the ffi
-	for (auto type : types) {
-		lua_getfield(L, -1, "cdef");
-		lua_pushfstring(L, "typedef struct %s %s;", type, type);
-		lua_call(L, 1, 0);
-	}
+    // Register all passed type with the ffi
+    for (auto type : types) {
+        lua_getfield(L, -1, "cdef");
+        lua_pushfstring(L, "typedef struct %s %s;", type, type);
+        lua_call(L, 1, 0);
+    }
 
-	lua_getfield(L, -1, "cast");
-	lua_remove(L, -2); // ffi table
+    lua_getfield(L, -1, "cast");
+    lua_remove(L, -2); // ffi table
 
-	// leaves ffi.cast on the stack
+    // leaves ffi.cast on the stack
+}
+}
 }
-} }
diff --git a/libaegisub/lua/modules/lfs.cpp b/libaegisub/lua/modules/lfs.cpp
index 7698360f3d78e9c1c89336765a6ef00803377a87..38f29d7928852a64ba247379c0ff366c067b6220 100644
--- a/libaegisub/lua/modules/lfs.cpp
+++ b/libaegisub/lua/modules/lfs.cpp
@@ -30,114 +30,130 @@ AGI_DEFINE_TYPE_NAME(DirectoryIterator);
 
 namespace {
 template<typename Func>
-auto wrap(char **err, Func f) -> decltype(f()) {
-	try {
-		return f();
-	}
-	catch (std::exception const& e) {
-		*err = strdup(e.what());
-	}
-	catch (agi::Exception const& e) {
-		*err = strndup(e.GetMessage());
-	}
-	catch (...) {
-		*err = strdup("Unknown error");
-	}
-	return 0;
+auto wrap(char **err, Func f) -> decltype(f())
+{
+    try {
+        return f();
+    }
+    catch (std::exception const &e) {
+        *err = strdup(e.what());
+    }
+    catch (agi::Exception const &e) {
+        *err = strndup(e.GetMessage());
+    }
+    catch (...) {
+        *err = strdup("Unknown error");
+    }
+    return 0;
 }
 
 template<typename Ret>
-bool setter(const char *path, char **err, Ret (*f)(bfs::path const&)) {
-	return wrap(err, [=]{
-		f(path);
-		return true;
-	});
+bool setter(const char *path, char **err, Ret (*f)(bfs::path const &))
+{
+    return wrap(err, [ = ] {
+        f(path);
+        return true;
+    });
 }
 
-bool lfs_chdir(const char *dir, char **err) {
-	return setter(dir, err, &bfs::current_path);
+bool lfs_chdir(const char *dir, char **err)
+{
+    return setter(dir, err, &bfs::current_path);
 }
 
-char *currentdir(char **err) {
-	return wrap(err, []{
-		return strndup(bfs::current_path().string());
-	});
+char *currentdir(char **err)
+{
+    return wrap(err, [] {
+        return strndup(bfs::current_path().string());
+    });
 }
 
-bool mkdir(const char *dir, char **err) {
-	return setter(dir, err, &CreateDirectory);
+bool mkdir(const char *dir, char **err)
+{
+    return setter(dir, err, &CreateDirectory);
 }
 
-bool lfs_rmdir(const char *dir, char **err) {
-	return setter(dir, err, &Remove);
+bool lfs_rmdir(const char *dir, char **err)
+{
+    return setter(dir, err, &Remove);
 }
 
-bool touch(const char *path, char **err) {
-	return setter(path, err, &Touch);
+bool touch(const char *path, char **err)
+{
+    return setter(path, err, &Touch);
 }
 
-char *dir_next(DirectoryIterator &it, char **err) {
-	if (it == end(it)) return nullptr;
-	return wrap(err, [&]{
-		auto str = strndup(*it);
-		++it;
-		return str;
-	});
+char *dir_next(DirectoryIterator &it, char **err)
+{
+    if (it == end(it)) return nullptr;
+    return wrap(err, [&] {
+        auto str = strndup(*it);
+        ++it;
+        return str;
+    });
 }
 
-void dir_close(DirectoryIterator &it) {
-	it = DirectoryIterator();
+void dir_close(DirectoryIterator &it)
+{
+    it = DirectoryIterator();
 }
 
-void dir_free(DirectoryIterator *it) {
-	delete it;
+void dir_free(DirectoryIterator *it)
+{
+    delete it;
 }
 
-DirectoryIterator *dir_new(const char *path, char **err) {
-	return wrap(err, [=]{
-		return new DirectoryIterator(path, "");
-	});
+DirectoryIterator *dir_new(const char *path, char **err)
+{
+    return wrap(err, [ = ] {
+        return new DirectoryIterator(path, "");
+    });
 }
 
-const char *get_mode(const char *path, char **err) {
-	return wrap(err, [=]() -> const char * {
-		switch (bfs::status(path).type()) {
-			case bfs::file_not_found: return nullptr;         break;
-			case bfs::regular_file:   return "file";          break;
-			case bfs::directory_file: return "directory";     break;
-			case bfs::symlink_file:   return "link";          break;
-			case bfs::block_file:     return "block device";  break;
-			case bfs::character_file: return "char device";   break;
-			case bfs::fifo_file:      return "fifo";          break;
-			case bfs::socket_file:    return "socket";        break;
-			case bfs::reparse_file:   return "reparse point"; break;
-			default:                  return "other";         break;
-		}
-	});
+const char *get_mode(const char *path, char **err)
+{
+    return wrap(err, [ = ]() -> const char * {
+        switch (bfs::status(path).type())
+        {
+        case bfs::file_not_found: return nullptr;         break;
+        case bfs::regular_file:   return "file";          break;
+        case bfs::directory_file: return "directory";     break;
+        case bfs::symlink_file:   return "link";          break;
+        case bfs::block_file:     return "block device";  break;
+        case bfs::character_file: return "char device";   break;
+        case bfs::fifo_file:      return "fifo";          break;
+        case bfs::socket_file:    return "socket";        break;
+        case bfs::reparse_file:   return "reparse point"; break;
+        default:                  return "other";         break;
+        }
+    });
 }
 
-time_t get_mtime(const char *path, char **err) {
-	return wrap(err, [=] { return ModifiedTime(path); });
+time_t get_mtime(const char *path, char **err)
+{
+    return wrap(err, [ = ] { return ModifiedTime(path); });
 }
 
-uintmax_t get_size(const char *path, char **err) {
-	return wrap(err, [=] { return Size(path); });
+uintmax_t get_size(const char *path, char **err)
+{
+    return wrap(err, [ = ] { return Size(path); });
 }
 }
 
-extern "C" int luaopen_lfs_impl(lua_State *L) {
-	agi::lua::register_lib_table(L, {"DirectoryIterator"},
-		"chdir", lfs_chdir,
-		"currentdir", currentdir,
-		"mkdir", mkdir,
-		"rmdir", lfs_rmdir,
-		"touch", touch,
-		"get_mtime", get_mtime,
-		"get_mode", get_mode,
-		"get_size", get_size,
-		"dir_new", dir_new,
-		"dir_free", dir_free,
-		"dir_next", dir_next,
-		"dir_close", dir_close);
-	return 1;
+extern "C" int luaopen_lfs_impl(lua_State *L)
+{
+    agi::lua::register_lib_table(L, {"DirectoryIterator"},
+                                 "chdir", lfs_chdir,
+                                 "currentdir", currentdir,
+                                 "mkdir", mkdir,
+                                 "rmdir", lfs_rmdir,
+                                 "touch", touch,
+                                 "get_mtime", get_mtime,
+                                 "get_mode", get_mode,
+                                 "get_size", get_size,
+                                 "dir_new", dir_new,
+                                 "dir_free", dir_free,
+                                 "dir_next", dir_next,
+                                 "dir_close", dir_close);
+    return 1;
 }
diff --git a/libaegisub/lua/modules/lpeg.c b/libaegisub/lua/modules/lpeg.c
index 8d67335ab99fd63a8de7e7e38952744f100bea39..19e356645b1d024487bf154b567ec456b0521a6d 100644
--- a/libaegisub/lua/modules/lpeg.c
+++ b/libaegisub/lua/modules/lpeg.c
@@ -91,14 +91,14 @@ typedef byte Charset[CHARSETSIZE];
 
 /* Virtual Machine's instructions */
 typedef enum Opcode {
-  IAny, IChar, ISet, ISpan,
-  IBack,
-  IRet, IEnd,
-  IChoice, IJmp, ICall, IOpenCall,
-  ICommit, IPartialCommit, IBackCommit, IFailTwice, IFail, IGiveup,
-  IFunc,
-  IFullCapture, IEmptyCapture, IEmptyCaptureIdx,
-  IOpenCapture, ICloseCapture, ICloseRunTime
+    IAny, IChar, ISet, ISpan,
+    IBack,
+    IRet, IEnd,
+    IChoice, IJmp, ICall, IOpenCall,
+    ICommit, IPartialCommit, IBackCommit, IFailTwice, IFail, IGiveup,
+    IFunc,
+    IFullCapture, IEmptyCapture, IEmptyCaptureIdx,
+    IOpenCapture, ICloseCapture, ICloseRunTime
 } Opcode;
 
 
@@ -111,42 +111,42 @@ typedef enum Opcode {
 #define ISFENVOFF	0x40
 
 static const int opproperties[] = {
-  /* IAny */		ISCHECK | ISFIXCHECK | ISJMP,
-  /* IChar */		ISCHECK | ISFIXCHECK | ISJMP,
-  /* ISet */		ISCHECK | ISFIXCHECK | ISJMP,
-  /* ISpan */		ISNOFAIL,
-  /* IBack */		0,
-  /* IRet */		0,
-  /* IEnd */		0,
-  /* IChoice */		ISJMP,
-  /* IJmp */		ISJMP | ISNOFAIL,
-  /* ICall */		ISJMP,
-  /* IOpenCall */	ISFENVOFF,
-  /* ICommit */		ISJMP,
-  /* IPartialCommit */	ISJMP,
-  /* IBackCommit */	ISJMP,
-  /* IFailTwice */	0,
-  /* IFail */		0,
-  /* IGiveup */		0,
-  /* IFunc */		ISCHECK | ISJMP,
-  /* IFullCapture */	ISCAPTURE | ISNOFAIL | ISFENVOFF,
-  /* IEmptyCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE,
-  /* IEmptyCaptureIdx */ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
-  /* IOpenCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
-  /* ICloseCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
-  /* ICloseRunTime */	ISCAPTURE | ISFENVOFF
+    /* IAny */		ISCHECK | ISFIXCHECK | ISJMP,
+    /* IChar */		ISCHECK | ISFIXCHECK | ISJMP,
+    /* ISet */		ISCHECK | ISFIXCHECK | ISJMP,
+    /* ISpan */		ISNOFAIL,
+    /* IBack */		0,
+    /* IRet */		0,
+    /* IEnd */		0,
+    /* IChoice */		ISJMP,
+    /* IJmp */		ISJMP | ISNOFAIL,
+    /* ICall */		ISJMP,
+    /* IOpenCall */	ISFENVOFF,
+    /* ICommit */		ISJMP,
+    /* IPartialCommit */	ISJMP,
+    /* IBackCommit */	ISJMP,
+    /* IFailTwice */	0,
+    /* IFail */		0,
+    /* IGiveup */		0,
+    /* IFunc */		ISCHECK | ISJMP,
+    /* IFullCapture */	ISCAPTURE | ISNOFAIL | ISFENVOFF,
+    /* IEmptyCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE,
+    /* IEmptyCaptureIdx */ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
+    /* IOpenCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
+    /* ICloseCapture */	ISCAPTURE | ISNOFAIL | ISMOVABLE | ISFENVOFF,
+    /* ICloseRunTime */	ISCAPTURE | ISFENVOFF
 };
 
 
 typedef union Instruction {
-  struct Inst {
-    byte code;
-    byte aux;
-    short offset;
-  } i;
-  PattFunc f;
-  int iv;
-  byte buff[1];
+    struct Inst {
+        byte code;
+        byte aux;
+        short offset;
+    } i;
+    PattFunc f;
+    int iv;
+    byte buff[1];
 } Instruction;
 
 static const Instruction giveup = {{IGiveup, 0, 0}};
@@ -176,18 +176,18 @@ static const Instruction giveup = {{IGiveup, 0, 0}};
 
 /* kinds of captures */
 typedef enum CapKind {
-  Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction,
-  Cquery, Cstring, Csubst, Cfold, Cruntime, Cgroup
+    Cclose, Cposition, Cconst, Cbackref, Carg, Csimple, Ctable, Cfunction,
+    Cquery, Cstring, Csubst, Cfold, Cruntime, Cgroup
 } CapKind;
 
 #define iscapnosize(k)	((k) == Cposition || (k) == Cconst)
 
 
 typedef struct Capture {
-  const char *s;  /* position */
-  short idx;
-  byte kind;
-  byte siz;
+    const char *s;  /* position */
+    short idx;
+    byte kind;
+    byte siz;
 } Capture;
 
 
@@ -210,36 +210,39 @@ typedef struct Capture {
 
 
 
-static int sizei (const Instruction *i) {
-  switch((Opcode)i->i.code) {
+static int sizei (const Instruction *i)
+{
+    switch ((Opcode)i->i.code) {
     case ISet: case ISpan: return CHARSETINSTSIZE;
     case IFunc: return funcinstsize(i);
     default: return 1;
-  }
+    }
 }
 
 
-static const char *val2str (lua_State *L, int idx) {
-  const char *k = lua_tostring(L, idx);
-  if (k != NULL)
-    return lua_pushfstring(L, "rule '%s'", k);
-  else
-    return lua_pushfstring(L, "rule <a %s>", luaL_typename(L, idx));
+static const char *val2str (lua_State *L, int idx)
+{
+    const char *k = lua_tostring(L, idx);
+    if (k != NULL)
+        return lua_pushfstring(L, "rule '%s'", k);
+    else
+        return lua_pushfstring(L, "rule <a %s>", luaL_typename(L, idx));
 }
 
 
-static int getposition (lua_State *L, int t, int i) {
-  int res;
-  lua_getfenv(L, -1);
-  lua_rawgeti(L, -1, i);  /* get key from pattern's environment */
-  lua_gettable(L, t);  /* get position from positions table */
-  res = lua_tointeger(L, -1);
-  if (res == 0) {  /* key has no registered position? */
-    lua_rawgeti(L, -2, i);  /* get key again */
-    return luaL_error(L, "%s is not defined in given grammar", val2str(L, -1));
-  }
-  lua_pop(L, 2);  /* remove environment and position */
-  return res;
+static int getposition (lua_State *L, int t, int i)
+{
+    int res;
+    lua_getfenv(L, -1);
+    lua_rawgeti(L, -1, i);  /* get key from pattern's environment */
+    lua_gettable(L, t);  /* get position from positions table */
+    res = lua_tointeger(L, -1);
+    if (res == 0) {  /* key has no registered position? */
+        lua_rawgeti(L, -2, i);  /* get key again */
+        return luaL_error(L, "%s is not defined in given grammar", val2str(L, -1));
+    }
+    lua_pop(L, 2);  /* remove environment and position */
+    return res;
 }
 
 
@@ -250,103 +253,109 @@ static int getposition (lua_State *L, int t, int i) {
 */
 
 
-static void printcharset (const Charset st) {
-  int i;
-  printf("[");
-  for (i = 0; i <= UCHAR_MAX; i++) {
-    int first = i;
-    while (testchar(st, i) && i <= UCHAR_MAX) i++;
-    if (i - 1 == first)  /* unary range? */
-      printf("(%02x)", first);
-    else if (i - 1 > first)  /* non-empty range? */
-      printf("(%02x-%02x)", first, i - 1);
-  }
-  printf("]");
+static void printcharset (const Charset st)
+{
+    int i;
+    printf("[");
+    for (i = 0; i <= UCHAR_MAX; i++) {
+        int first = i;
+        while (testchar(st, i) && i <= UCHAR_MAX) i++;
+        if (i - 1 == first)  /* unary range? */
+            printf("(%02x)", first);
+        else if (i - 1 > first)  /* non-empty range? */
+            printf("(%02x-%02x)", first, i - 1);
+    }
+    printf("]");
 }
 
 
-static void printcapkind (int kind) {
-  const char *const modes[] = {
-    "close", "position", "constant", "backref",
-    "argument", "simple", "table", "function",
-    "query", "string", "substitution", "fold",
-    "runtime", "group"};
-  printf("%s", modes[kind]);
+static void printcapkind (int kind)
+{
+    const char *const modes[] = {
+        "close", "position", "constant", "backref",
+        "argument", "simple", "table", "function",
+        "query", "string", "substitution", "fold",
+        "runtime", "group"
+    };
+    printf("%s", modes[kind]);
 }
 
 
-static void printjmp (const Instruction *op, const Instruction *p) {
-  printf("-> ");
-  if (p->i.offset == 0) printf("FAIL");
-  else printf("%d", (int)(dest(0, p) - op));
+static void printjmp (const Instruction *op, const Instruction *p)
+{
+    printf("-> ");
+    if (p->i.offset == 0) printf("FAIL");
+    else printf("%d", (int)(dest(0, p) - op));
 }
 
 
-static void printinst (const Instruction *op, const Instruction *p) {
-  const char *const names[] = {
-    "any", "char", "set", "span", "back",
-    "ret", "end",
-    "choice", "jmp", "call", "open_call",
-    "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
-     "func",
-     "fullcapture", "emptycapture", "emptycaptureidx", "opencapture",
-     "closecapture", "closeruntime"
-  };
-  printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
-  switch ((Opcode)p->i.code) {
+static void printinst (const Instruction *op, const Instruction *p)
+{
+    const char *const names[] = {
+        "any", "char", "set", "span", "back",
+        "ret", "end",
+        "choice", "jmp", "call", "open_call",
+        "commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
+        "func",
+        "fullcapture", "emptycapture", "emptycaptureidx", "opencapture",
+        "closecapture", "closeruntime"
+    };
+    printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
+    switch ((Opcode)p->i.code) {
     case IChar: {
-      printf("'%c'", p->i.aux);
-      printjmp(op, p);
-      break;
+        printf("'%c'", p->i.aux);
+        printjmp(op, p);
+        break;
     }
     case IAny: {
-      printf("* %d", p->i.aux);
-      printjmp(op, p);
-      break;
+        printf("* %d", p->i.aux);
+        printjmp(op, p);
+        break;
     }
     case IFullCapture: case IOpenCapture:
     case IEmptyCapture: case IEmptyCaptureIdx:
     case ICloseCapture: case ICloseRunTime: {
-      printcapkind(getkind(p));
-      printf("(n = %d)  (off = %d)", getoff(p), p->i.offset);
-      break;
+        printcapkind(getkind(p));
+        printf("(n = %d)  (off = %d)", getoff(p), p->i.offset);
+        break;
     }
     case ISet: {
-      printcharset((p+1)->buff);
-      printjmp(op, p);
-      break;
+        printcharset((p + 1)->buff);
+        printjmp(op, p);
+        break;
     }
     case ISpan: {
-      printcharset((p+1)->buff);
-      break;
+        printcharset((p + 1)->buff);
+        break;
     }
     case IOpenCall: {
-      printf("-> %d", p->i.offset);
-      break;
+        printf("-> %d", p->i.offset);
+        break;
     }
     case IChoice: {
-      printjmp(op, p);
-      printf(" (%d)", p->i.aux);
-      break;
+        printjmp(op, p);
+        printf(" (%d)", p->i.aux);
+        break;
     }
     case IJmp: case ICall: case ICommit:
     case IPartialCommit: case IBackCommit: {
-      printjmp(op, p);
-      break;
+        printjmp(op, p);
+        break;
     }
     default: break;
-  }
-  printf("\n");
+    }
+    printf("\n");
 }
 
 
-static void printpatt (Instruction *p) {
-  Instruction *op = p;
-  for (;;) {
-    printinst(op, p);
-    if ((Opcode)p->i.code == IEnd) break;
-    p += sizei(p);
-  }
+static void printpatt (Instruction *p)
+{
+    Instruction *op = p;
+    for (;;) {
+        printinst(op, p);
+        if ((Opcode)p->i.code == IEnd) break;
+        p += sizei(p);
+    }
 }
 
 /* }====================================================== */
@@ -360,9 +369,9 @@ static void printpatt (Instruction *p) {
 
 
 typedef struct Stack {
-  const char *s;
-  const Instruction *p;
-  int caplevel;
+    const char *s;
+    const Instruction *p;
+    int caplevel;
 } Stack;
 
 
@@ -373,50 +382,53 @@ static int runtimecap (lua_State *L, Capture *close, Capture *ocap,
                        const char *o, const char *s, int ptop);
 
 
-static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop) {
-  Capture *newc;
-  if (captop >= INT_MAX/((int)sizeof(Capture) * 2))
-    luaL_error(L, "too many captures");
-  newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
-  memcpy(newc, cap, captop * sizeof(Capture));
-  lua_replace(L, caplistidx(ptop));
-  return newc;
-}
-
-
-static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
-  Stack *stack = getstackbase(L, ptop);
-  Stack *newstack;
-  int n = *stacklimit - stack;
-  int max, newn;
-  lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
-  max = lua_tointeger(L, -1);
-  lua_pop(L, 1);
-  if (n >= max)
-    luaL_error(L, "too many pending calls/choices");
-  newn = 2*n; if (newn > max) newn = max;
-  newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
-  memcpy(newstack, stack, n * sizeof(Stack));
-  lua_replace(L, stackidx(ptop));
-  *stacklimit = newstack + newn;
-  return newstack + n;
-
+static Capture *doublecap (lua_State *L, Capture *cap, int captop, int ptop)
+{
+    Capture *newc;
+    if (captop >= INT_MAX / ((int)sizeof(Capture) * 2))
+        luaL_error(L, "too many captures");
+    newc = (Capture *)lua_newuserdata(L, captop * 2 * sizeof(Capture));
+    memcpy(newc, cap, captop * sizeof(Capture));
+    lua_replace(L, caplistidx(ptop));
+    return newc;
 }
 
 
-static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
-  int i;
-  assert(base[0].kind == Cruntime && base[0].siz == 0);
-  base[0].idx = fd;  /* first returned capture */
-  for (i = 1; i < n; i++) {  /* add extra captures */
-    base[i].siz = 1;  /* mark it as closed */
-    base[i].s = s;
-    base[i].kind = Cruntime;
-    base[i].idx = fd + i;  /* stack index */
-  }
-  base[n].kind = Cclose;  /* add closing entry */
-  base[n].siz = 1;
-  base[n].s = s;
+static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop)
+{
+    Stack *stack = getstackbase(L, ptop);
+    Stack *newstack;
+    int n = *stacklimit - stack;
+    int max, newn;
+    lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+    max = lua_tointeger(L, -1);
+    lua_pop(L, 1);
+    if (n >= max)
+        luaL_error(L, "too many pending calls/choices");
+    newn = 2 * n; if (newn > max) newn = max;
+    newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
+    memcpy(newstack, stack, n * sizeof(Stack));
+    lua_replace(L, stackidx(ptop));
+    *stacklimit = newstack + newn;
+    return newstack + n;
+
+}
+
+
+static void adddyncaptures (const char *s, Capture *base, int n, int fd)
+{
+    int i;
+    assert(base[0].kind == Cruntime && base[0].siz == 0);
+    base[0].idx = fd;  /* first returned capture */
+    for (i = 1; i < n; i++) {  /* add extra captures */
+        base[i].siz = 1;  /* mark it as closed */
+        base[i].s = s;
+        base[i].kind = Cruntime;
+        base[i].idx = fd + i;  /* stack index */
+    }
+    base[n].kind = Cclose;  /* add closing entry */
+    base[n].siz = 1;
+    base[n].s = s;
 }
 
 
@@ -424,200 +436,201 @@ static void adddyncaptures (const char *s, Capture *base, int n, int fd) {
 
 static const char *match (lua_State *L,
                           const char *o, const char *s, const char *e,
-                          Instruction *op, Capture *capture, int ptop) {
-  Stack stackbase[INITBACK];
-  Stack *stacklimit = stackbase + INITBACK;
-  Stack *stack = stackbase;  /* point to first empty slot in stack */
-  int capsize = INITCAPSIZE;
-  int captop = 0;  /* point to first empty slot in captures */
-  const Instruction *p = op;
-  stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
-  lua_pushlightuserdata(L, stackbase);
-  for (;;) {
+                          Instruction *op, Capture *capture, int ptop)
+{
+    Stack stackbase[INITBACK];
+    Stack *stacklimit = stackbase + INITBACK;
+    Stack *stack = stackbase;  /* point to first empty slot in stack */
+    int capsize = INITCAPSIZE;
+    int captop = 0;  /* point to first empty slot in captures */
+    const Instruction *p = op;
+    stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
+    lua_pushlightuserdata(L, stackbase);
+    for (;;) {
 #if defined(DEBUG)
-      printf("s: |%s| stck: %ld c: %d  ",
-             s, stack - getstackbase(L, ptop), captop);
-      printinst(op, p);
+        printf("s: |%s| stck: %ld c: %d  ",
+               s, stack - getstackbase(L, ptop), captop);
+        printinst(op, p);
 #endif
-    switch ((Opcode)p->i.code) {
-      case IEnd: {
-        assert(stack == getstackbase(L, ptop) + 1);
-        capture[captop].kind = Cclose;
-        capture[captop].s = NULL;
-        return s;
-      }
-      case IGiveup: {
-        assert(stack == getstackbase(L, ptop));
-        return NULL;
-      }
-      case IRet: {
-        assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
-        p = (--stack)->p;
-        continue;
-      }
-      case IAny: {
-        int n = p->i.aux;
-        if (n <= e - s) { p++; s += n; }
-        else condfailed(p);
-        continue;
-      }
-      case IChar: {
-        if ((byte)*s == p->i.aux && s < e) { p++; s++; }
-        else condfailed(p);
-        continue;
-      }
-      case ISet: {
-        int c = (byte)*s;
-        if (testchar((p+1)->buff, c) && s < e)
-          { p += CHARSETINSTSIZE; s++; }
-        else condfailed(p);
-        continue;
-      }
-      case IBack: {
-        int n = p->i.aux;
-        if (n > s - o) goto fail;
-        s -= n; p++;
-        continue;
-      }
-      case ISpan: {
-        for (; s < e; s++) {
-          int c = (byte)*s;
-          if (!testchar((p+1)->buff, c)) break;
+        switch ((Opcode)p->i.code) {
+        case IEnd: {
+            assert(stack == getstackbase(L, ptop) + 1);
+            capture[captop].kind = Cclose;
+            capture[captop].s = NULL;
+            return s;
         }
-        p += CHARSETINSTSIZE;
-        continue;
-      }
-      case IFunc: {
-        const char *r = (p+1)->f(s, e, o, (p+2)->buff);
-        if (r != NULL) { s = r; p += funcinstsize(p); }
-        else condfailed(p);
-        continue;
-      }
-      case IJmp: {
-        p += p->i.offset;
-        continue;
-      }
-      case IChoice: {
-        if (stack == stacklimit)
-          stack = doublestack(L, &stacklimit, ptop);
-        stack->p = dest(0, p);
-        stack->s = s - p->i.aux;
-        stack->caplevel = captop;
-        stack++;
-        p++;
-        continue;
-      }
-      case ICall: {
-        if (stack == stacklimit)
-          stack = doublestack(L, &stacklimit, ptop);
-        stack->s = NULL;
-        stack->p = p + 1;  /* save return address */
-        stack++;
-        p += p->i.offset;
-        continue;
-      }
-      case ICommit: {
-        assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
-        stack--;
-        p += p->i.offset;
-        continue;
-      }
-      case IPartialCommit: {
-        assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
-        (stack - 1)->s = s;
-        (stack - 1)->caplevel = captop;
-        p += p->i.offset;
-        continue;
-      }
-      case IBackCommit: {
-        assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
-        s = (--stack)->s;
-        captop = stack->caplevel;
-        p += p->i.offset;
-        continue;
-      }
-      case IFailTwice:
-        assert(stack > getstackbase(L, ptop));
-        stack--;
-        /* go through */
-      case IFail:
-      fail: { /* pattern failed: try to backtrack */
-        do {  /* remove pending calls */
-          assert(stack > getstackbase(L, ptop));
-          s = (--stack)->s;
-        } while (s == NULL);
-        captop = stack->caplevel;
-        p = stack->p;
-        continue;
-      }
-      case ICloseRunTime: {
-        int fr = lua_gettop(L) + 1;  /* stack index of first result */
-        int ncap = runtimecap(L, capture + captop, capture, o, s, ptop);
-        lua_Integer res = lua_tointeger(L, fr) - 1;  /* offset */
-        int n = lua_gettop(L) - fr;  /* number of new captures */
-        if (res == -1) {  /* may not be a number */
-          if (!lua_toboolean(L, fr)) {  /* false value? */
-            lua_settop(L, fr - 1);  /* remove results */
-            goto fail;  /* and fail */
-          }
-          else if (lua_isboolean(L, fr))  /* true? */
-            res = s - o;  /* keep current position */
+        case IGiveup: {
+            assert(stack == getstackbase(L, ptop));
+            return NULL;
         }
-        if (res < s - o || res > e - o)
-          luaL_error(L, "invalid position returned by match-time capture");
-        s = o + res;  /* update current position */
-        captop -= ncap;  /* remove nested captures */
-        lua_remove(L, fr);  /* remove first result (offset) */
-        if (n > 0) {  /* captures? */
-          if ((captop += n + 1) >= capsize) {
-            capture = doublecap(L, capture, captop, ptop);
-            capsize = 2 * captop;
-          }
-          adddyncaptures(s, capture + captop - n - 1, n, fr);
+        case IRet: {
+            assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
+            p = (--stack)->p;
+            continue;
         }
-        p++;
-        continue;
-      }
-      case ICloseCapture: {
-        const char *s1 = s - getoff(p);
-        assert(captop > 0);
-        if (capture[captop - 1].siz == 0 &&
-            s1 - capture[captop - 1].s < UCHAR_MAX) {
-          capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
-          p++;
-          continue;
+        case IAny: {
+            int n = p->i.aux;
+            if (n <= e - s) { p++; s += n; }
+            else condfailed(p);
+            continue;
         }
-        else {
-          capture[captop].siz = 1;  /* mark entry as closed */
-          goto capture;
+        case IChar: {
+            if ((byte)*s == p->i.aux && s < e) { p++; s++; }
+            else condfailed(p);
+            continue;
+        }
+        case ISet: {
+            int c = (byte) * s;
+            if (testchar((p + 1)->buff, c) && s < e)
+            { p += CHARSETINSTSIZE; s++; }
+            else condfailed(p);
+            continue;
         }
-      }
-      case IEmptyCapture: case IEmptyCaptureIdx:
-        capture[captop].siz = 1;  /* mark entry as closed */
-        goto capture;
-      case IOpenCapture:
-        capture[captop].siz = 0;  /* mark entry as open */
-        goto capture;
-      case IFullCapture:
-        capture[captop].siz = getoff(p) + 1;  /* save capture size */
-      capture: {
-        capture[captop].s = s - getoff(p);
-        capture[captop].idx = p->i.offset;
-        capture[captop].kind = getkind(p);
-        if (++captop >= capsize) {
-          capture = doublecap(L, capture, captop, ptop);
-          capsize = 2 * captop;
+        case IBack: {
+            int n = p->i.aux;
+            if (n > s - o) goto fail;
+            s -= n; p++;
+            continue;
+        }
+        case ISpan: {
+            for (; s < e; s++) {
+                int c = (byte) * s;
+                if (!testchar((p + 1)->buff, c)) break;
+            }
+            p += CHARSETINSTSIZE;
+            continue;
+        }
+        case IFunc: {
+            const char *r = (p + 1)->f(s, e, o, (p + 2)->buff);
+            if (r != NULL) { s = r; p += funcinstsize(p); }
+            else condfailed(p);
+            continue;
+        }
+        case IJmp: {
+            p += p->i.offset;
+            continue;
+        }
+        case IChoice: {
+            if (stack == stacklimit)
+                stack = doublestack(L, &stacklimit, ptop);
+            stack->p = dest(0, p);
+            stack->s = s - p->i.aux;
+            stack->caplevel = captop;
+            stack++;
+            p++;
+            continue;
+        }
+        case ICall: {
+            if (stack == stacklimit)
+                stack = doublestack(L, &stacklimit, ptop);
+            stack->s = NULL;
+            stack->p = p + 1;  /* save return address */
+            stack++;
+            p += p->i.offset;
+            continue;
+        }
+        case ICommit: {
+            assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+            stack--;
+            p += p->i.offset;
+            continue;
+        }
+        case IPartialCommit: {
+            assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+            (stack - 1)->s = s;
+            (stack - 1)->caplevel = captop;
+            p += p->i.offset;
+            continue;
+        }
+        case IBackCommit: {
+            assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
+            s = (--stack)->s;
+            captop = stack->caplevel;
+            p += p->i.offset;
+            continue;
+        }
+        case IFailTwice:
+            assert(stack > getstackbase(L, ptop));
+            stack--;
+        /* go through */
+        case IFail:
+fail: { /* pattern failed: try to backtrack */
+                do {  /* remove pending calls */
+                    assert(stack > getstackbase(L, ptop));
+                    s = (--stack)->s;
+                } while (s == NULL);
+                captop = stack->caplevel;
+                p = stack->p;
+                continue;
+            }
+        case ICloseRunTime: {
+            int fr = lua_gettop(L) + 1;  /* stack index of first result */
+            int ncap = runtimecap(L, capture + captop, capture, o, s, ptop);
+            lua_Integer res = lua_tointeger(L, fr) - 1;  /* offset */
+            int n = lua_gettop(L) - fr;  /* number of new captures */
+            if (res == -1) {  /* may not be a number */
+                if (!lua_toboolean(L, fr)) {  /* false value? */
+                    lua_settop(L, fr - 1);  /* remove results */
+                    goto fail;  /* and fail */
+                }
+                else if (lua_isboolean(L, fr))  /* true? */
+                    res = s - o;  /* keep current position */
+            }
+            if (res < s - o || res > e - o)
+                luaL_error(L, "invalid position returned by match-time capture");
+            s = o + res;  /* update current position */
+            captop -= ncap;  /* remove nested captures */
+            lua_remove(L, fr);  /* remove first result (offset) */
+            if (n > 0) {  /* captures? */
+                if ((captop += n + 1) >= capsize) {
+                    capture = doublecap(L, capture, captop, ptop);
+                    capsize = 2 * captop;
+                }
+                adddyncaptures(s, capture + captop - n - 1, n, fr);
+            }
+            p++;
+            continue;
+        }
+        case ICloseCapture: {
+            const char *s1 = s - getoff(p);
+            assert(captop > 0);
+            if (capture[captop - 1].siz == 0 &&
+                s1 - capture[captop - 1].s < UCHAR_MAX) {
+                capture[captop - 1].siz = s1 - capture[captop - 1].s + 1;
+                p++;
+                continue;
+            }
+            else {
+                capture[captop].siz = 1;  /* mark entry as closed */
+                goto capture;
+            }
+        }
+        case IEmptyCapture: case IEmptyCaptureIdx:
+            capture[captop].siz = 1;  /* mark entry as closed */
+            goto capture;
+        case IOpenCapture:
+            capture[captop].siz = 0;  /* mark entry as open */
+            goto capture;
+        case IFullCapture:
+            capture[captop].siz = getoff(p) + 1;  /* save capture size */
+capture: {
+                capture[captop].s = s - getoff(p);
+                capture[captop].idx = p->i.offset;
+                capture[captop].kind = getkind(p);
+                if (++captop >= capsize) {
+                    capture = doublecap(L, capture, captop, ptop);
+                    capsize = 2 * captop;
+                }
+                p++;
+                continue;
+            }
+        case IOpenCall: {
+            lua_rawgeti(L, penvidx(ptop), p->i.offset);
+            luaL_error(L, "reference to %s outside a grammar", val2str(L, -1));
+        }
+        default: assert(0); return NULL;
         }
-        p++;
-        continue;
-      }
-      case IOpenCall: {
-        lua_rawgeti(L, penvidx(ptop), p->i.offset);
-        luaL_error(L, "reference to %s outside a grammar", val2str(L, -1));
-      }
-      default: assert(0); return NULL;
     }
-  }
 }
 
 /* }====================================================== */
@@ -639,152 +652,154 @@ static const char *match (lua_State *L,
 ** mapping rule keys to the position of their code in the pattern.
 */
 static int verify (lua_State *L, Instruction *op, const Instruction *p,
-                   Instruction *e, int postable, int rule) {
-  static const char dummy[] = "";
-  Stack back[MAXBACKVER];
-  int backtop = 0;  /* point to first empty slot in back */
-  while (p != e) {
-    switch ((Opcode)p->i.code) {
-      case IRet: {
-        p = back[--backtop].p;
-        continue;
-      }
-      case IChoice: {
-        if (backtop >= MAXBACKVER)
-          return luaL_error(L, "too many pending calls/choices");
-        back[backtop].p = dest(0, p);
-        back[backtop++].s = dummy;
-        p++;
-        continue;
-      }
-      case ICall: {
-        assert((p + 1)->i.code != IRet);  /* no tail call */
-        if (backtop >= MAXBACKVER)
-          return luaL_error(L, "too many pending calls/choices");
-        back[backtop].s = NULL;
-        back[backtop++].p = p + 1;
-        goto dojmp;
-      }
-      case IOpenCall: {
-        int i;
-        if (postable == 0)  /* grammar still not fixed? */
-          goto fail;  /* to be verified later */
-        for (i = 0; i < backtop; i++) {
-          if (back[i].s == NULL && back[i].p == p + 1)
-            return luaL_error(L, "%s is left recursive", val2str(L, rule));
+                   Instruction *e, int postable, int rule)
+{
+    static const char dummy[] = "";
+    Stack back[MAXBACKVER];
+    int backtop = 0;  /* point to first empty slot in back */
+    while (p != e) {
+        switch ((Opcode)p->i.code) {
+        case IRet: {
+            p = back[--backtop].p;
+            continue;
         }
-        if (backtop >= MAXBACKVER)
-          return luaL_error(L, "too many pending calls/choices");
-        back[backtop].s = NULL;
-        back[backtop++].p = p + 1;
-        p = op + getposition(L, postable, p->i.offset);
-        continue;
-      }
-      case IBackCommit:
-      case ICommit: {
-        assert(backtop > 0 && p->i.offset > 0);
-        backtop--;
-        goto dojmp;
-      }
-      case IPartialCommit: {
-        assert(backtop > 0);
-        if (p->i.offset > 0) goto dojmp;  /* forward jump */
-        else {  /* loop will be detected when checking corresponding rule */
-          assert(postable != 0);
-          backtop--;
-          p++;  /* just go on now */
-          continue;
+        case IChoice: {
+            if (backtop >= MAXBACKVER)
+                return luaL_error(L, "too many pending calls/choices");
+            back[backtop].p = dest(0, p);
+            back[backtop++].s = dummy;
+            p++;
+            continue;
         }
-      }
-      case IBack: {
-        if (p->i.aux == 1 && isfixcheck(p + 1)) {  /* char test? */
-          p++;  /* skip back instruction */
-          p += sizei(p);  /* skip char test */
+        case ICall: {
+            assert((p + 1)->i.code != IRet);  /* no tail call */
+            if (backtop >= MAXBACKVER)
+                return luaL_error(L, "too many pending calls/choices");
+            back[backtop].s = NULL;
+            back[backtop++].p = p + 1;
+            goto dojmp;
         }
-        else {  /* standard lookbehind code */
-          assert((Opcode)(p - 1)->i.code == IChoice);  /* look behind */
-          backtop--;
-          p += (p - 1)->i.offset;
-          assert((Opcode)(p - 1)->i.code == IFail);  /* look behind */
+        case IOpenCall: {
+            int i;
+            if (postable == 0)  /* grammar still not fixed? */
+                goto fail;  /* to be verified later */
+            for (i = 0; i < backtop; i++) {
+                if (back[i].s == NULL && back[i].p == p + 1)
+                    return luaL_error(L, "%s is left recursive", val2str(L, rule));
+            }
+            if (backtop >= MAXBACKVER)
+                return luaL_error(L, "too many pending calls/choices");
+            back[backtop].s = NULL;
+            back[backtop++].p = p + 1;
+            p = op + getposition(L, postable, p->i.offset);
+            continue;
         }
-        continue;
-      }
-      case IAny:
-      case IChar:
-      case ISet: {
-        const Instruction *next = p + sizei(p);
-        if ((Opcode)next->i.code == IBack)
-          p = next + 1;  /* continue after the back instruction */
-        else if (p->i.offset == 0) goto fail;
-        else  /* jump */
-          p += p->i.offset;
-        continue;
-      }
-      case IJmp:
-      dojmp: {
-        p += p->i.offset;
-        continue;
-      }
-      case IFailTwice:  /* 'not' predicate */
-        goto fail;  /* body could have failed; try to backtrack it */
-      case IFail: {
-        if (p > op && (p - 1)->i.code == IBackCommit) {  /* 'and' predicate? */
-          p++;  /* pretend it succeeded and go ahead */
-          continue;
+        case IBackCommit:
+        case ICommit: {
+            assert(backtop > 0 && p->i.offset > 0);
+            backtop--;
+            goto dojmp;
         }
-        /* else failed: go through */
-      }
-      fail: { /* pattern failed: try to backtrack */
-        do {
-          if (backtop-- == 0)
-            return 1;  /* no more backtracking */
-        } while (back[backtop].s == NULL);
-        p = back[backtop].p;
-        continue;
-      }
-      case ISpan:
-      case IOpenCapture: case ICloseCapture:
-      case IEmptyCapture: case IEmptyCaptureIdx:
-      case IFullCapture: {
-        p += sizei(p);
-        continue;
-      }
-      case ICloseRunTime: {
-        goto fail;  /* be liberal in this case */
-      }
-      case IFunc: {
-        const char *r = (p+1)->f(dummy, dummy, dummy, (p+2)->buff);
-        if (r != NULL) { p += funcinstsize(p); }
-        else condfailed(p);
-        continue;
-      }
-      case IEnd:  /* cannot happen (should stop before it) */
-      default: assert(0); return 0;
-    }
-  }
-  assert(backtop == 0);
-  return 0;
+        case IPartialCommit: {
+            assert(backtop > 0);
+            if (p->i.offset > 0) goto dojmp;  /* forward jump */
+            else {  /* loop will be detected when checking corresponding rule */
+                assert(postable != 0);
+                backtop--;
+                p++;  /* just go on now */
+                continue;
+            }
+        }
+        case IBack: {
+            if (p->i.aux == 1 && isfixcheck(p + 1)) {  /* char test? */
+                p++;  /* skip back instruction */
+                p += sizei(p);  /* skip char test */
+            }
+            else {  /* standard lookbehind code */
+                assert((Opcode)(p - 1)->i.code == IChoice);  /* look behind */
+                backtop--;
+                p += (p - 1)->i.offset;
+                assert((Opcode)(p - 1)->i.code == IFail);  /* look behind */
+            }
+            continue;
+        }
+        case IAny:
+        case IChar:
+        case ISet: {
+            const Instruction *next = p + sizei(p);
+            if ((Opcode)next->i.code == IBack)
+                p = next + 1;  /* continue after the back instruction */
+            else if (p->i.offset == 0) goto fail;
+            else  /* jump */
+                p += p->i.offset;
+            continue;
+        }
+        case IJmp:
+dojmp: {
+                p += p->i.offset;
+                continue;
+            }
+        case IFailTwice:  /* 'not' predicate */
+            goto fail;  /* body could have failed; try to backtrack it */
+        case IFail: {
+            if (p > op && (p - 1)->i.code == IBackCommit) {  /* 'and' predicate? */
+                p++;  /* pretend it succeeded and go ahead */
+                continue;
+            }
+            /* else failed: go through */
+        }
+fail: { /* pattern failed: try to backtrack */
+            do {
+                if (backtop-- == 0)
+                    return 1;  /* no more backtracking */
+            } while (back[backtop].s == NULL);
+            p = back[backtop].p;
+            continue;
+        }
+        case ISpan:
+        case IOpenCapture: case ICloseCapture:
+        case IEmptyCapture: case IEmptyCaptureIdx:
+        case IFullCapture: {
+            p += sizei(p);
+            continue;
+        }
+        case ICloseRunTime: {
+            goto fail;  /* be liberal in this case */
+        }
+        case IFunc: {
+            const char *r = (p + 1)->f(dummy, dummy, dummy, (p + 2)->buff);
+            if (r != NULL) { p += funcinstsize(p); }
+            else condfailed(p);
+            continue;
+        }
+        case IEnd:  /* cannot happen (should stop before it) */
+        default: assert(0); return 0;
+        }
+    }
+    assert(backtop == 0);
+    return 0;
 }
 
 
 static void checkrule (lua_State *L, Instruction *op, int from, int to,
-                       int postable, int rule) {
-  int i;
-  int lastopen = 0;  /* more recent OpenCall seen in the code */
-  for (i = from; i < to; i += sizei(op + i)) {
-    if (op[i].i.code == IPartialCommit && op[i].i.offset < 0) {  /* loop? */
-      int start = dest(op, i);
-      assert(op[start - 1].i.code == IChoice && dest(op, start - 1) == i + 1);
-      if (start <= lastopen) {  /* loop does contain an open call? */
-        if (!verify(L, op, op + start, op + i, postable, rule)) /* check body */
-          luaL_error(L, "possible infinite loop in %s", val2str(L, rule));
-      }
+                       int postable, int rule)
+{
+    int i;
+    int lastopen = 0;  /* more recent OpenCall seen in the code */
+    for (i = from; i < to; i += sizei(op + i)) {
+        if (op[i].i.code == IPartialCommit && op[i].i.offset < 0) {  /* loop? */
+            int start = dest(op, i);
+            assert(op[start - 1].i.code == IChoice && dest(op, start - 1) == i + 1);
+            if (start <= lastopen) {  /* loop does contain an open call? */
+                if (!verify(L, op, op + start, op + i, postable, rule)) /* check body */
+                    luaL_error(L, "possible infinite loop in %s", val2str(L, rule));
+            }
+        }
+        else if (op[i].i.code == IOpenCall)
+            lastopen = i;
     }
-    else if (op[i].i.code == IOpenCall)
-      lastopen = i;
-  }
-  assert(op[i - 1].i.code == IRet);
-  verify(L, op, op + from, op + to - 1, postable, rule);
+    assert(op[i - 1].i.code == IRet);
+    verify(L, op, op + from, op + to - 1, postable, rule);
 }
 
 
@@ -802,30 +817,32 @@ static void checkrule (lua_State *L, Instruction *op, int from, int to,
 enum charsetanswer { NOINFO, ISCHARSET, VALIDSTARTS };
 
 typedef struct CharsetTag {
-  enum charsetanswer tag;
-  Charset cs;
+    enum charsetanswer tag;
+    Charset cs;
 } CharsetTag;
 
 
 static Instruction *getpatt (lua_State *L, int idx, int *size);
 
 
-static void check2test (Instruction *p, int n) {
-  assert(ischeck(p) && n != 0);
-  p->i.offset = n;
+static void check2test (Instruction *p, int n)
+{
+    assert(ischeck(p) && n != 0);
+    p->i.offset = n;
 }
 
 
 /*
 ** invert array slice p[0]-p[e] (both inclusive)
 */
-static void invert (Instruction *p, int e) {
-  int i;
-  for (i = 0; i < e; i++, e--) {
-    Instruction temp = p[i];
-    p[i] = p[e];
-    p[e] = temp;
-  }
+static void invert (Instruction *p, int e)
+{
+    int i;
+    for (i = 0; i < e; i++, e--) {
+        Instruction temp = p[i];
+        p[i] = p[e];
+        p[e] = temp;
+    }
 }
 
 
@@ -833,83 +850,89 @@ static void invert (Instruction *p, int e) {
 ** rotate array slice p[0]-p[e] (both inclusive) 'n' steps
 ** to the 'left'
 */
-static void rotate (Instruction *p, int e, int n) {
-  invert(p, n - 1);
-  invert(p + n, e - n);
-  invert(p, e);
+static void rotate (Instruction *p, int e, int n)
+{
+    invert(p, n - 1);
+    invert(p + n, e - n);
+    invert(p, e);
 }
 
 
 #define op_step(p)	((p)->i.code == IAny ? (p)->i.aux : 1)
 
 
-static int skipchecks (Instruction *p, int up, int *pn) {
-  int i, n = 0;
-  for (i = 0; isfixcheck(p + i); i += sizei(p + i)) {
-    int st = op_step(p + i);
-    if (n + st > MAXOFF - up) break;
-    n += st;
-  }
-  *pn = n;
-  return i;
+static int skipchecks (Instruction *p, int up, int *pn)
+{
+    int i, n = 0;
+    for (i = 0; isfixcheck(p + i); i += sizei(p + i)) {
+        int st = op_step(p + i);
+        if (n + st > MAXOFF - up) break;
+        n += st;
+    }
+    *pn = n;
+    return i;
 }
 
 
 #define ismovablecap(op)	(ismovable(op) && getoff(op) < MAXOFF)
 
-static void optimizecaptures (Instruction *p) {
-  int i;
-  int limit = 0;
-  for (i = 0; p[i].i.code != IEnd; i += sizei(p + i)) {
-    if (isjmp(p + i) && dest(p, i) >= limit)
-      limit = dest(p, i) + 1;  /* do not optimize jump targets */
-    else if (i >= limit && ismovablecap(p + i) && isfixcheck(p + i + 1)) {
-      int end, n, j;  /* found a border capture|check */
-      int maxoff = getoff(p + i);
-      int start = i;
-      /* find first capture in the group */
-      while (start > limit && ismovablecap(p + start - 1)) {
-        start--;
-        if (getoff(p + start) > maxoff) maxoff = getoff(p + start);
-      }
-      end = skipchecks(p + i + 1, maxoff, &n) + i;  /* find last check */
-      if (n == 0) continue;  /* first check is too big to move across */
-      assert(n <= MAXOFF && start <= i && i < end);
-      for (j = start; j <= i; j++)
-        p[j].i.aux += (n << 4);  /* correct offset of captures to be moved */
-      rotate(p + start, end - start, i - start + 1);  /* move them up */
-      i = end;
-      assert(isfixcheck(p + start) && iscapture(p + i));
+static void optimizecaptures (Instruction *p)
+{
+    int i;
+    int limit = 0;
+    for (i = 0; p[i].i.code != IEnd; i += sizei(p + i)) {
+        if (isjmp(p + i) && dest(p, i) >= limit)
+            limit = dest(p, i) + 1;  /* do not optimize jump targets */
+        else if (i >= limit && ismovablecap(p + i) && isfixcheck(p + i + 1)) {
+            int end, n, j;  /* found a border capture|check */
+            int maxoff = getoff(p + i);
+            int start = i;
+            /* find first capture in the group */
+            while (start > limit && ismovablecap(p + start - 1)) {
+                start--;
+                if (getoff(p + start) > maxoff) maxoff = getoff(p + start);
+            }
+            end = skipchecks(p + i + 1, maxoff, &n) + i;  /* find last check */
+            if (n == 0) continue;  /* first check is too big to move across */
+            assert(n <= MAXOFF && start <= i && i < end);
+            for (j = start; j <= i; j++)
+                p[j].i.aux += (n << 4);  /* correct offset of captures to be moved */
+            rotate(p + start, end - start, i - start + 1);  /* move them up */
+            i = end;
+            assert(isfixcheck(p + start) && iscapture(p + i));
+        }
     }
-  }
 }
 
 
-static int target (Instruction *p, int i) {
-  while (p[i].i.code == IJmp)  i += p[i].i.offset;
-  return i;
+static int target (Instruction *p, int i)
+{
+    while (p[i].i.code == IJmp)  i += p[i].i.offset;
+    return i;
 }
 
 
-static void optimizejumps (Instruction *p) {
-  int i;
-  for (i = 0; p[i].i.code != IEnd; i += sizei(p + i)) {
-    if (isjmp(p + i))
-      p[i].i.offset = target(p, dest(p, i)) - i;
-  }
+static void optimizejumps (Instruction *p)
+{
+    int i;
+    for (i = 0; p[i].i.code != IEnd; i += sizei(p + i)) {
+        if (isjmp(p + i))
+            p[i].i.offset = target(p, dest(p, i)) - i;
+    }
 }
 
 
-static void optimizechoice (Instruction *p) {
-  assert(p->i.code == IChoice);
-  if (isfixcheck(p + 1)) {
-    int lc = sizei(p + 1);
-    rotate(p, lc, 1);
-    assert(isfixcheck(p) && (p + lc)->i.code == IChoice);
-    (p + lc)->i.aux = op_step(p);
-    check2test(p, (p + lc)->i.offset);
-    (p + lc)->i.offset -= lc;
-  }
+static void optimizechoice (Instruction *p)
+{
+    assert(p->i.code == IChoice);
+    if (isfixcheck(p + 1)) {
+        int lc = sizei(p + 1);
+        rotate(p, lc, 1);
+        assert(isfixcheck(p) && (p + lc)->i.code == IChoice);
+        (p + lc)->i.aux = op_step(p);
+        check2test(p, (p + lc)->i.offset);
+        (p + lc)->i.offset -= lc;
+    }
 }
 
 
@@ -917,12 +940,13 @@ static void optimizechoice (Instruction *p) {
 ** A 'headfail' pattern is a pattern that can only fails in its first
 ** instruction, which must be a check.
 */
-static int isheadfail (Instruction *p) {
-  if (!ischeck(p)) return 0;
-  /* check that other operations cannot fail */
-  for (p += sizei(p); p->i.code != IEnd; p += sizei(p))
-    if (!isnofail(p)) return 0;
-  return 1;
+static int isheadfail (Instruction *p)
+{
+    if (!ischeck(p)) return 0;
+    /* check that other operations cannot fail */
+    for (p += sizei(p); p->i.code != IEnd; p += sizei(p))
+        if (!isnofail(p)) return 0;
+    return 1;
 }
 
 
@@ -934,9 +958,10 @@ static int isheadfail (Instruction *p) {
 ** in Lua 5.2, default "environment" for patterns is nil, not
 ** a table. Treat it as an empty table.
 */
-static int ktablelen (lua_State *L, int idx) {
-  if (!lua_istable(L, idx)) return 0;
-  else return lua_objlen(L, idx);
+static int ktablelen (lua_State *L, int idx)
+{
+    if (!lua_istable(L, idx)) return 0;
+    else return lua_objlen(L, idx);
 }
 
 
@@ -946,35 +971,36 @@ static int ktablelen (lua_State *L, int idx) {
 ** 'p' keeps its original ktable.  If 'p' has no elements, it shares
 ** 'p1' ktable.  Otherwise, this function creates a new ktable for 'p'.
 ** Return the offset of original 'p' elements in the new ktable.
-*/ 
-static int jointable (lua_State *L, int p1) {
-  int n, n1, i;
-  lua_getfenv(L, p1);
-  n1 = ktablelen(L, -1);  /* number of elements in p1's env */
-  lua_getfenv(L, -2);
-  if (n1 == 0 || lua_equal(L, -2, -1)) {
-    lua_pop(L, 2);
-    return 0;  /* no need to change anything */
-  }
-  n = ktablelen(L, -1);  /* number of elements in p's env */
-  if (n == 0) {
-    lua_pop(L, 1);  /* removes p env */
-    lua_setfenv(L, -2);  /* p now shares p1's env */
-    return 0;  /* no need to correct anything */
-  }
-  lua_createtable(L, n + n1, 0);
-  /* stack: p; p1 env; p env; new p env */
-  for (i = 1; i <= n; i++) {
-    lua_rawgeti(L, -2, i);
-    lua_rawseti(L, -2, i);
-  }
-  for (i = 1; i <= n1; i++) {
-    lua_rawgeti(L, -3, i);
-    lua_rawseti(L, -2, n + i);
-  }
-  lua_setfenv(L, -4);  /* new table becomes p env */
-  lua_pop(L, 2);  /* remove p1 env and old p env */
-  return n;
+*/
+static int jointable (lua_State *L, int p1)
+{
+    int n, n1, i;
+    lua_getfenv(L, p1);
+    n1 = ktablelen(L, -1);  /* number of elements in p1's env */
+    lua_getfenv(L, -2);
+    if (n1 == 0 || lua_equal(L, -2, -1)) {
+        lua_pop(L, 2);
+        return 0;  /* no need to change anything */
+    }
+    n = ktablelen(L, -1);  /* number of elements in p's env */
+    if (n == 0) {
+        lua_pop(L, 1);  /* removes p env */
+        lua_setfenv(L, -2);  /* p now shares p1's env */
+        return 0;  /* no need to correct anything */
+    }
+    lua_createtable(L, n + n1, 0);
+    /* stack: p; p1 env; p env; new p env */
+    for (i = 1; i <= n; i++) {
+        lua_rawgeti(L, -2, i);
+        lua_rawseti(L, -2, i);
+    }
+    for (i = 1; i <= n1; i++) {
+        lua_rawgeti(L, -3, i);
+        lua_rawseti(L, -2, n + i);
+    }
+    lua_setfenv(L, -4);  /* new table becomes p env */
+    lua_pop(L, 2);  /* remove p1 env and old p env */
+    return n;
 }
 
 
@@ -983,27 +1009,29 @@ static int jointable (lua_State *L, int p1) {
 #define pattsize(L,idx)		(lua_objlen(L, idx)/sizeof(Instruction) - 1)
 
 
-static int addpatt (lua_State *L, Instruction *p, int p1idx) {
-  Instruction *p1 = (Instruction *)lua_touserdata(L, p1idx);
-  int sz = pattsize(L, p1idx);
-  int corr = jointable(L, p1idx);
-  copypatt(p, p1, sz + 1);
-  if (corr != 0) {
-    Instruction *px;
-    for (px = p; px < p + sz; px += sizei(px)) {
-      if (isfenvoff(px) && px->i.offset != 0)
-        px->i.offset += corr;
+static int addpatt (lua_State *L, Instruction *p, int p1idx)
+{
+    Instruction *p1 = (Instruction *)lua_touserdata(L, p1idx);
+    int sz = pattsize(L, p1idx);
+    int corr = jointable(L, p1idx);
+    copypatt(p, p1, sz + 1);
+    if (corr != 0) {
+        Instruction *px;
+        for (px = p; px < p + sz; px += sizei(px)) {
+            if (isfenvoff(px) && px->i.offset != 0)
+                px->i.offset += corr;
+        }
     }
-  }
-  return sz;
+    return sz;
 }
 
 
-static void setinstaux (Instruction *i, Opcode op, int offset, int aux) {
-  assert(aux <= MAXAUX);
-  i->i.code = op;
-  i->i.offset = offset;
-  i->i.aux = aux;
+static void setinstaux (Instruction *i, Opcode op, int offset, int aux)
+{
+    assert(aux <= MAXAUX);
+    i->i.code = op;
+    i->i.offset = offset;
+    i->i.aux = aux;
 }
 
 #define setinst(i,op,off)	setinstaux(i,op,off,0)
@@ -1015,43 +1043,46 @@ static void setinstaux (Instruction *i, Opcode op, int offset, int aux) {
 ** create a new ktable for pattern at the stack top, mapping
 ** '1' to the value at stack position 'vidx'.
 */
-static int value2fenv (lua_State *L, int vidx) {
-  lua_createtable(L, 1, 0);
-  lua_pushvalue(L, vidx);
-  lua_rawseti(L, -2, 1);
-  lua_setfenv(L, -2);
-  return 1;
+static int value2fenv (lua_State *L, int vidx)
+{
+    lua_createtable(L, 1, 0);
+    lua_pushvalue(L, vidx);
+    lua_rawseti(L, -2, 1);
+    lua_setfenv(L, -2);
+    return 1;
 }
 
 
-static Instruction *newpatt (lua_State *L, size_t n) {
-  Instruction *p;
-  if (n >= MAXPATTSIZE - 1)
-    luaL_error(L, "pattern too big");
-  p = (Instruction *)lua_newuserdata(L, (n + 1) * sizeof(Instruction));
-  luaL_getmetatable(L, PATTERN_T);
-  lua_setmetatable(L, -2);
-  setinst(p + n, IEnd, 0);
-  return p;
+static Instruction *newpatt (lua_State *L, size_t n)
+{
+    Instruction *p;
+    if (n >= MAXPATTSIZE - 1)
+        luaL_error(L, "pattern too big");
+    p = (Instruction *)lua_newuserdata(L, (n + 1) * sizeof(Instruction));
+    luaL_getmetatable(L, PATTERN_T);
+    lua_setmetatable(L, -2);
+    setinst(p + n, IEnd, 0);
+    return p;
 }
 
 
-static void fillcharset (Instruction *p, Charset cs) {
-  switch (p[0].i.code) {
+static void fillcharset (Instruction *p, Charset cs)
+{
+    switch (p[0].i.code) {
     case ISet: {
-      loopset(i, cs[i] = p[1].buff[i]);
-      break;
+        loopset(i, cs[i] = p[1].buff[i]);
+        break;
     }
     case IChar: {
-      loopset(i, cs[i] = 0);
-      setchar(cs, p[0].i.aux);
-      break;
+        loopset(i, cs[i] = 0);
+        setchar(cs, p[0].i.aux);
+        break;
     }
     default: {  /* any char may start unhandled instructions */
-      loopset(i, cs[i] = 0xff);
-      break;
+        loopset(i, cs[i] = 0xff);
+        break;
+    }
     }
-  }
 }
 
 
@@ -1060,253 +1091,266 @@ static void fillcharset (Instruction *p, Charset cs) {
 ** valid start for a pattern.
 */
 
-static enum charsetanswer tocharset (Instruction *p, CharsetTag *c) {
-  if (isfixcheck(p)) {
-    fillcharset(p, c->cs);
-    if ((p + sizei(p))->i.code == IEnd && op_step(p) == 1)
-      c->tag = ISCHARSET;
+static enum charsetanswer tocharset (Instruction *p, CharsetTag *c)
+{
+    if (isfixcheck(p)) {
+        fillcharset(p, c->cs);
+        if ((p + sizei(p))->i.code == IEnd && op_step(p) == 1)
+            c->tag = ISCHARSET;
+        else
+            c->tag = VALIDSTARTS;
+    }
     else
-      c->tag = VALIDSTARTS;
-  }
-  else
-    c->tag = NOINFO;
-  return c->tag;
+        c->tag = NOINFO;
+    return c->tag;
 }
 
 
-static int exclusiveset (Charset c1, Charset c2) {
-  /* non-empty intersection? */
-  loopset(i, {if ((c1[i] & c2[i]) != 0) return 0;});
-  return 1;  /* no intersection */
+static int exclusiveset (Charset c1, Charset c2)
+{
+    /* non-empty intersection? */
+    loopset(i, {if ((c1[i] & c2[i]) != 0) return 0;});
+    return 1;  /* no intersection */
 }
 
 
-static int exclusive (CharsetTag *c1, CharsetTag *c2) {
-  if (c1->tag == NOINFO || c2->tag == NOINFO)
-    return 0;  /* one of them is not filled */
-  else return exclusiveset(c1->cs, c2->cs);
+static int exclusive (CharsetTag *c1, CharsetTag *c2)
+{
+    if (c1->tag == NOINFO || c2->tag == NOINFO)
+        return 0;  /* one of them is not filled */
+    else return exclusiveset(c1->cs, c2->cs);
 }
 
 
-static Instruction *newcharset (lua_State *L) {
-  Instruction *p = newpatt(L, CHARSETINSTSIZE);
-  p[0].i.code = ISet;
-  p[0].i.offset = 0;
-  loopset(i, p[1].buff[i] = 0);
-  return p;
+static Instruction *newcharset (lua_State *L)
+{
+    Instruction *p = newpatt(L, CHARSETINSTSIZE);
+    p[0].i.code = ISet;
+    p[0].i.offset = 0;
+    loopset(i, p[1].buff[i] = 0);
+    return p;
 }
 
 
-static int set_l (lua_State *L) {
-  size_t l;
-  const char *s = luaL_checklstring(L, 1, &l);
-  if (l == 1)
-    getpatt(L, 1, NULL);  /* a unit set is equivalent to a literal */
-  else {
+static int set_l (lua_State *L)
+{
+    size_t l;
+    const char *s = luaL_checklstring(L, 1, &l);
+    if (l == 1)
+        getpatt(L, 1, NULL);  /* a unit set is equivalent to a literal */
+    else {
+        Instruction *p = newcharset(L);
+        while (l--) {
+            setchar(p[1].buff, (byte)(*s));
+            s++;
+        }
+    }
+    return 1;
+}
+
+
+static int range_l (lua_State *L)
+{
+    int arg;
+    int top = lua_gettop(L);
     Instruction *p = newcharset(L);
-    while (l--) {
-      setchar(p[1].buff, (byte)(*s));
-      s++;
+    for (arg = 1; arg <= top; arg++) {
+        int c;
+        size_t l;
+        const char *r = luaL_checklstring(L, arg, &l);
+        luaL_argcheck(L, l == 2, arg, "range must have two characters");
+        for (c = (byte)r[0]; c <= (byte)r[1]; c++)
+            setchar(p[1].buff, c);
     }
-  }
-  return 1;
+    return 1;
 }
 
 
-static int range_l (lua_State *L) {
-  int arg;
-  int top = lua_gettop(L);
-  Instruction *p = newcharset(L);
-  for (arg = 1; arg <= top; arg++) {
-    int c;
-    size_t l;
-    const char *r = luaL_checklstring(L, arg, &l);
-    luaL_argcheck(L, l == 2, arg, "range must have two characters");
-    for (c = (byte)r[0]; c <= (byte)r[1]; c++)
-      setchar(p[1].buff, c);
-  }
-  return 1;
+static int nter_l (lua_State *L)
+{
+    Instruction *p;
+    luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");
+    p = newpatt(L, 1);
+    setinst(p, IOpenCall, value2fenv(L, 1));
+    return 1;
+}
+
+
+
+static int testpattern (lua_State *L, int idx)
+{
+    if (lua_touserdata(L, idx)) {  /* value is a userdata? */
+        if (lua_getmetatable(L, idx)) {  /* does it have a metatable? */
+            luaL_getmetatable(L, PATTERN_T);
+            if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
+                lua_pop(L, 2);  /* remove both metatables */
+                return 1;
+            }
+        }
+    }
+    return 0;
 }
 
 
-static int nter_l (lua_State *L) {
-  Instruction *p;
-  luaL_argcheck(L, !lua_isnoneornil(L, 1), 1, "non-nil value expected");
-  p = newpatt(L, 1);
-  setinst(p, IOpenCall, value2fenv(L, 1));
-  return 1;
+static Instruction *fix_l (lua_State *L, int t)
+{
+    Instruction *p;
+    int i;
+    int totalsize = 2;  /* include initial call and jump */
+    int n = 0;  /* number of rules */
+    int base = lua_gettop(L);
+    lua_newtable(L);  /* to store relative positions of each rule */
+    lua_pushinteger(L, 1);  /* default initial rule */
+    /* collect patterns and compute sizes */
+    lua_pushnil(L);
+    while (lua_next(L, t) != 0) {
+        int l;
+        if (lua_tonumber(L, -2) == 1 && lua_isstring(L, -1)) {
+            lua_replace(L, base + 2);  /* use this value as initial rule */
+            continue;
+        }
+        if (!testpattern(L, -1))
+            luaL_error(L, "%s is not a pattern", val2str(L, -2));
+        l = pattsize(L, -1) + 1;  /* space for pattern + ret */
+        if (totalsize >= MAXPATTSIZE - l)
+            luaL_error(L, "grammar too large");
+        luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules");
+        lua_insert(L, -2);  /* put key on top */
+        lua_pushvalue(L, -1);  /* duplicate key (for lua_next) */
+        lua_pushvalue(L, -1);  /* duplicate key (to index positions table)) */
+        lua_pushinteger(L, totalsize);  /* position for this rule */
+        lua_settable(L, base + 1);  /* store key=>position in positions table */
+        totalsize += l;
+        n++;
+    }
+    luaL_argcheck(L, n > 0, t, "empty grammar");
+    p = newpatt(L, totalsize);  /* create new pattern */
+    p++;  /* save space for call */
+    setinst(p++, IJmp, totalsize - 1);  /* after call, jumps to the end */
+    for (i = 1; i <= n; i++) {  /* copy all rules into new pattern */
+        p += addpatt(L, p, base + 1 + i * 2);
+        setinst(p++, IRet, 0);
+    }
+    p -= totalsize;  /* back to first position */
+    totalsize = 2;  /* go through each rule's position */
+    for (i = 1; i <= n; i++) {  /* check all rules */
+        int l = pattsize(L, base + 1 + i * 2) + 1;
+        checkrule(L, p, totalsize, totalsize + l, base + 1, base + 2 + i * 2);
+        totalsize += l;
+    }
+    lua_pushvalue(L, base + 2);  /* get initial rule */
+    lua_gettable(L, base + 1);  /* get its position in postions table */
+    i = lua_tointeger(L, -1);  /* convert to number */
+    lua_pop(L, 1);
+    if (i == 0)  /* is it defined? */
+        luaL_error(L, "initial rule not defined in given grammar");
+    setinst(p, ICall, i);  /* first instruction calls initial rule */
+    /* correct calls */
+    for (i = 0; i < totalsize; i += sizei(p + i)) {
+        if (p[i].i.code == IOpenCall) {
+            int pos = getposition(L, base + 1, p[i].i.offset);
+            p[i].i.code = (p[target(p, i + 1)].i.code == IRet) ? IJmp : ICall;
+            p[i].i.offset = pos - i;
+        }
+    }
+    optimizejumps(p);
+    lua_replace(L, t);  /* put new pattern in old's position */
+    lua_settop(L, base);  /* remove rules and positions table */
+    return p;
 }
 
 
+static Instruction *any (lua_State *L, int n, int extra, int *offsetp)
+{
+    int offset = offsetp ? *offsetp : 0;
+    Instruction *p = newpatt(L, (n - 1) / UCHAR_MAX + extra + 1);
+    Instruction *p1 = p + offset;
+    for (; n > UCHAR_MAX; n -= UCHAR_MAX)
+        setinstaux(p1++, IAny, 0, UCHAR_MAX);
+    setinstaux(p1++, IAny, 0, n);
+    if (offsetp) *offsetp = p1 - p;
+    return p;
+}
 
-static int testpattern (lua_State *L, int idx) {
-  if (lua_touserdata(L, idx)) {  /* value is a userdata? */
-    if (lua_getmetatable(L, idx)) {  /* does it have a metatable? */
-      luaL_getmetatable(L, PATTERN_T);
-      if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
-        lua_pop(L, 2);  /* remove both metatables */
-        return 1;
-      }
-    }
-  }
-  return 0;
-}
-
-
-static Instruction *fix_l (lua_State *L, int t) {
-  Instruction *p;
-  int i;
-  int totalsize = 2;  /* include initial call and jump */
-  int n = 0;  /* number of rules */
-  int base = lua_gettop(L);
-  lua_newtable(L);  /* to store relative positions of each rule */
-  lua_pushinteger(L, 1);  /* default initial rule */
-  /* collect patterns and compute sizes */
-  lua_pushnil(L);
-  while (lua_next(L, t) != 0) {
-    int l;
-    if (lua_tonumber(L, -2) == 1 && lua_isstring(L, -1)) {
-      lua_replace(L, base + 2);  /* use this value as initial rule */
-      continue;
-    }
-    if (!testpattern(L, -1))
-      luaL_error(L, "%s is not a pattern", val2str(L, -2));
-    l = pattsize(L, -1) + 1;  /* space for pattern + ret */
-    if (totalsize >= MAXPATTSIZE - l)
-      luaL_error(L, "grammar too large");
-    luaL_checkstack(L, LUA_MINSTACK, "grammar has too many rules");
-    lua_insert(L, -2);  /* put key on top */
-    lua_pushvalue(L, -1);  /* duplicate key (for lua_next) */
-    lua_pushvalue(L, -1);  /* duplicate key (to index positions table)) */
-    lua_pushinteger(L, totalsize);  /* position for this rule */
-    lua_settable(L, base + 1);  /* store key=>position in positions table */
-    totalsize += l;
-    n++;
-  }
-  luaL_argcheck(L, n > 0, t, "empty grammar");
-  p = newpatt(L, totalsize);  /* create new pattern */
-  p++;  /* save space for call */
-  setinst(p++, IJmp, totalsize - 1);  /* after call, jumps to the end */
-  for (i = 1; i <= n; i++) {  /* copy all rules into new pattern */
-    p += addpatt(L, p, base + 1 + i*2);
-    setinst(p++, IRet, 0);
-  }
-  p -= totalsize;  /* back to first position */
-  totalsize = 2;  /* go through each rule's position */
-  for (i = 1; i <= n; i++) {  /* check all rules */
-    int l = pattsize(L, base + 1 + i*2) + 1;
-    checkrule(L, p, totalsize, totalsize + l, base + 1, base + 2 + i*2);
-    totalsize += l;
-  }
-  lua_pushvalue(L, base + 2);  /* get initial rule */
-  lua_gettable(L, base + 1);  /* get its position in postions table */
-  i = lua_tointeger(L, -1);  /* convert to number */
-  lua_pop(L, 1);
-  if (i == 0)  /* is it defined? */
-    luaL_error(L, "initial rule not defined in given grammar");
-  setinst(p, ICall, i);  /* first instruction calls initial rule */
-  /* correct calls */
-  for (i = 0; i < totalsize; i += sizei(p + i)) {
-    if (p[i].i.code == IOpenCall) {
-      int pos = getposition(L, base + 1, p[i].i.offset);
-      p[i].i.code = (p[target(p, i + 1)].i.code == IRet) ? IJmp : ICall;
-      p[i].i.offset = pos - i;
-    }
-  }
-  optimizejumps(p);
-  lua_replace(L, t);  /* put new pattern in old's position */
-  lua_settop(L, base);  /* remove rules and positions table */
-  return p;
-}
-
-
-static Instruction *any (lua_State *L, int n, int extra, int *offsetp) {
-  int offset = offsetp ? *offsetp : 0;
-  Instruction *p = newpatt(L, (n - 1)/UCHAR_MAX + extra + 1);
-  Instruction *p1 = p + offset;
-  for (; n > UCHAR_MAX; n -= UCHAR_MAX)
-    setinstaux(p1++, IAny, 0, UCHAR_MAX);
-  setinstaux(p1++, IAny, 0, n);
-  if (offsetp) *offsetp = p1 - p;
-  return p;
-}
-
-
-static Instruction *getpatt (lua_State *L, int idx, int *size) {
-  Instruction *p;
-  switch (lua_type(L, idx)) {
+
+static Instruction *getpatt (lua_State *L, int idx, int *size)
+{
+    Instruction *p;
+    switch (lua_type(L, idx)) {
     case LUA_TSTRING: {
-      size_t i, len;
-      const char *s = lua_tolstring(L, idx, &len);
-      p = newpatt(L, len);
-      for (i = 0; i < len; i++)
-        setinstaux(p + i, IChar, 0, (byte)s[i]);
-      lua_replace(L, idx);
-      break;
+        size_t i, len;
+        const char *s = lua_tolstring(L, idx, &len);
+        p = newpatt(L, len);
+        for (i = 0; i < len; i++)
+            setinstaux(p + i, IChar, 0, (byte)s[i]);
+        lua_replace(L, idx);
+        break;
     }
     case LUA_TNUMBER: {
-      int n = lua_tointeger(L, idx);
-      if (n == 0)  /* empty pattern? */
-        p = newpatt(L, 0);
-      else if (n > 0)
-        p = any(L, n, 0, NULL);
-      else if (-n <= UCHAR_MAX) {
-        p = newpatt(L, 2);
-        setinstaux(p, IAny, 2, -n);
-        setinst(p + 1, IFail, 0);
-      }
-      else {
-        int offset = 2;  /* space for ITestAny & IChoice */
-        p = any(L, -n - UCHAR_MAX, 3, &offset);
-        setinstaux(p, IAny, offset + 1, UCHAR_MAX);
-        setinstaux(p + 1, IChoice, offset, UCHAR_MAX);
-        setinst(p + offset, IFailTwice, 0);
-      }
-      lua_replace(L, idx);
-      break;
+        int n = lua_tointeger(L, idx);
+        if (n == 0)  /* empty pattern? */
+            p = newpatt(L, 0);
+        else if (n > 0)
+            p = any(L, n, 0, NULL);
+        else if (-n <= UCHAR_MAX) {
+            p = newpatt(L, 2);
+            setinstaux(p, IAny, 2, -n);
+            setinst(p + 1, IFail, 0);
+        }
+        else {
+            int offset = 2;  /* space for ITestAny & IChoice */
+            p = any(L, -n - UCHAR_MAX, 3, &offset);
+            setinstaux(p, IAny, offset + 1, UCHAR_MAX);
+            setinstaux(p + 1, IChoice, offset, UCHAR_MAX);
+            setinst(p + offset, IFailTwice, 0);
+        }
+        lua_replace(L, idx);
+        break;
     }
     case LUA_TBOOLEAN: {
-      if (lua_toboolean(L, idx))  /* true? */
-        p = newpatt(L, 0);  /* empty pattern (always succeeds) */
-      else {
-        p = newpatt(L, 1);
-        setinst(p, IFail, 0);
-      }
-      lua_replace(L, idx);
-      break;
+        if (lua_toboolean(L, idx))  /* true? */
+            p = newpatt(L, 0);  /* empty pattern (always succeeds) */
+        else {
+            p = newpatt(L, 1);
+            setinst(p, IFail, 0);
+        }
+        lua_replace(L, idx);
+        break;
     }
     case LUA_TTABLE: {
-      p = fix_l(L, idx);
-      break;
+        p = fix_l(L, idx);
+        break;
     }
     case LUA_TFUNCTION: {
-      p = newpatt(L, 2);
-      setinstcap(p, IOpenCapture, value2fenv(L, idx), Cruntime, 0);
-      setinstcap(p + 1, ICloseRunTime, 0, Cclose, 0);
-      lua_replace(L, idx);
-      break;
+        p = newpatt(L, 2);
+        setinstcap(p, IOpenCapture, value2fenv(L, idx), Cruntime, 0);
+        setinstcap(p + 1, ICloseRunTime, 0, Cclose, 0);
+        lua_replace(L, idx);
+        break;
     }
     default: {
-      p = checkpattern(L, idx);
-      break;
+        p = checkpattern(L, idx);
+        break;
     }
-  }
-  if (size) *size = pattsize(L, idx);
-  return p;
+    }
+    if (size) *size = pattsize(L, idx);
+    return p;
 }
 
 
-static int getpattl (lua_State *L, int idx) {
-  int size;
-  getpatt(L, idx, &size);
-  return size;
+static int getpattl (lua_State *L, int idx)
+{
+    int size;
+    getpatt(L, idx, &size);
+    return size;
 }
 
 
-static int pattern_l (lua_State *L) {
-  lua_settop(L, 1);
-  getpatt(L, 1, NULL);
-  return 1;
+static int pattern_l (lua_State *L)
+{
+    lua_settop(L, 1);
+    getpatt(L, 1, NULL);
+    return 1;
 }
 
 
@@ -1314,386 +1358,407 @@ static int pattern_l (lua_State *L) {
 #define isfail(p)	((p)->i.code == IFail)
 #define issucc(p)	((p)->i.code == IEnd)
 
-static int concat_l (lua_State *L) {
-  /* p1; p2; */
-  int l1, l2;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  Instruction *p2 = getpatt(L, 2, &l2);
-  if (isfail(p1) || issucc(p2))
-    lua_pushvalue(L, 1);  /* fail * x == fail; x * true == x */
-  else if (isfail(p2) || issucc(p1))
-    lua_pushvalue(L, 2);  /* x * fail == fail; true * x == x */
-  else if (isany(p1) && isany(p2))
-    any(L, p1->i.aux + p2->i.aux, 0, NULL);
-  else {
-    Instruction *op = newpatt(L, l1 + l2);
-    Instruction *p = op + addpatt(L, op, 1);
-    addpatt(L, p, 2);
-    optimizecaptures(op);
-  }
-  return 1;
+static int concat_l (lua_State *L)
+{
+    /* p1; p2; */
+    int l1, l2;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    Instruction *p2 = getpatt(L, 2, &l2);
+    if (isfail(p1) || issucc(p2))
+        lua_pushvalue(L, 1);  /* fail * x == fail; x * true == x */
+    else if (isfail(p2) || issucc(p1))
+        lua_pushvalue(L, 2);  /* x * fail == fail; true * x == x */
+    else if (isany(p1) && isany(p2))
+        any(L, p1->i.aux + p2->i.aux, 0, NULL);
+    else {
+        Instruction *op = newpatt(L, l1 + l2);
+        Instruction *p = op + addpatt(L, op, 1);
+        addpatt(L, p, 2);
+        optimizecaptures(op);
+    }
+    return 1;
 }
 
 
-static int diff_l (lua_State *L) {
-  int l1, l2;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  Instruction *p2 = getpatt(L, 2, &l2);
-  CharsetTag st1, st2;
-  if (tocharset(p1, &st1) == ISCHARSET && tocharset(p2, &st2) == ISCHARSET) {
-    Instruction *p = newcharset(L);
-    loopset(i, p[1].buff[i] = st1.cs[i] & ~st2.cs[i]);
-  }
-  else if (isheadfail(p2)) {
-    Instruction *p = newpatt(L, l2 + 1 + l1);
-    p += addpatt(L, p, 2);
-    check2test(p - l2, l2 + 1);
-    setinst(p++, IFail, 0);
-    addpatt(L, p, 1);
-  }
-  else {  /* !e2 . e1 */
-    /* !e -> choice L1; e; failtwice; L1: ... */
-    Instruction *p = newpatt(L, 1 + l2 + 1 + l1);
-    Instruction *pi = p;
-    setinst(p++, IChoice, 1 + l2 + 1);
-    p += addpatt(L, p, 2);
-    setinst(p++, IFailTwice, 0);
-    addpatt(L, p, 1);
-    optimizechoice(pi);
-  }
-  return 1;
-}
-
-
-static int unm_l (lua_State *L) {
-  Instruction *p = getpatt(L, 1, NULL);
-  if (isfail(p)) {  /* -false? */
-    newpatt(L, 0);  /* true */
+static int diff_l (lua_State *L)
+{
+    int l1, l2;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    Instruction *p2 = getpatt(L, 2, &l2);
+    CharsetTag st1, st2;
+    if (tocharset(p1, &st1) == ISCHARSET && tocharset(p2, &st2) == ISCHARSET) {
+        Instruction *p = newcharset(L);
+        loopset(i, p[1].buff[i] = st1.cs[i] & ~st2.cs[i]);
+    }
+    else if (isheadfail(p2)) {
+        Instruction *p = newpatt(L, l2 + 1 + l1);
+        p += addpatt(L, p, 2);
+        check2test(p - l2, l2 + 1);
+        setinst(p++, IFail, 0);
+        addpatt(L, p, 1);
+    }
+    else {  /* !e2 . e1 */
+        /* !e -> choice L1; e; failtwice; L1: ... */
+        Instruction *p = newpatt(L, 1 + l2 + 1 + l1);
+        Instruction *pi = p;
+        setinst(p++, IChoice, 1 + l2 + 1);
+        p += addpatt(L, p, 2);
+        setinst(p++, IFailTwice, 0);
+        addpatt(L, p, 1);
+        optimizechoice(pi);
+    }
     return 1;
-  }
-  else if (issucc(p)) {  /* -true? */
-    Instruction *p1 = newpatt(L, 1);  /* false */
-    setinst(p1, IFail, 0);
+}
+
+
+static int unm_l (lua_State *L)
+{
+    Instruction *p = getpatt(L, 1, NULL);
+    if (isfail(p)) {  /* -false? */
+        newpatt(L, 0);  /* true */
+        return 1;
+    }
+    else if (issucc(p)) {  /* -true? */
+        Instruction *p1 = newpatt(L, 1);  /* false */
+        setinst(p1, IFail, 0);
+        return 1;
+    }
+    else {  /* -A == '' - A */
+        lua_pushliteral(L, "");
+        lua_insert(L, 1);
+        return diff_l(L);
+    }
+}
+
+
+static int pattand_l (lua_State *L)
+{
+    int l1;
+    CharsetTag st1;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    if (isfail(p1) || issucc(p1))
+        lua_pushvalue(L, 1);  /* &fail == fail; &true == true */
+    else if (tocharset(p1, &st1) == ISCHARSET) {
+        Instruction *p = newpatt(L, l1 + 1);
+        copypatt(p, p1, l1); p += l1;
+        setinstaux(p, IBack, 0, 1);
+    }
+    else {  /* Choice L1; p1; BackCommit L2; L1: Fail; L2: */
+        Instruction *p = newpatt(L, 1 + l1 + 2);
+        setinst(p++, IChoice, 1 + l1 + 1);
+        p += addpatt(L, p, 1);
+        setinst(p++, IBackCommit, 2);
+        setinst(p, IFail, 0);
+    }
     return 1;
-  }
-  else {  /* -A == '' - A */
-    lua_pushliteral(L, "");
-    lua_insert(L, 1);
-    return diff_l(L);
-  }
-}
-
-
-static int pattand_l (lua_State *L) {
-  int l1;
-  CharsetTag st1;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  if (isfail(p1) || issucc(p1))
-    lua_pushvalue(L, 1);  /* &fail == fail; &true == true */
-  else if (tocharset(p1, &st1) == ISCHARSET) {
-    Instruction *p = newpatt(L, l1 + 1);
-    copypatt(p, p1, l1); p += l1;
-    setinstaux(p, IBack, 0, 1);
-  }
-  else {  /* Choice L1; p1; BackCommit L2; L1: Fail; L2: */
-    Instruction *p = newpatt(L, 1 + l1 + 2);
-    setinst(p++, IChoice, 1 + l1 + 1);
-    p += addpatt(L, p, 1);
-    setinst(p++, IBackCommit, 2);
-    setinst(p, IFail, 0);
-  }
-  return 1;
-}
-
-
-static int nocalls (const Instruction *p) {
-  for (; (Opcode)p->i.code != IEnd; p += sizei(p))
-    if ((Opcode)p->i.code == IOpenCall) return 0;
-  return 1;
-}
-
-
-static int pattbehind (lua_State *L) {
-  int l1;
-  CharsetTag st1;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  int n = luaL_optint(L, 2, 1);
-  luaL_argcheck(L, n <= MAXAUX, 2, "lookbehind delta too large");
-  if (!nocalls(p1))
-    luaL_error(L, "lookbehind pattern cannot contain non terminals");
-  if (isfail(p1) || issucc(p1))
-    lua_pushvalue(L, 1);  /* <fail == fail; <true == true */
-  else if (n == 1 && tocharset(p1, &st1) == ISCHARSET) {
-    Instruction *p = newpatt(L, 1 + l1);
-    setinstaux(p, IBack, 0, 1); p++;
-    copypatt(p, p1, l1);
-  }
-  else {  /* Choice L1; Back; p1; BackCommit L2; L1: fail; L2: */
-    Instruction *p = newpatt(L, 2 + l1 + 2);
-    setinst(p++, IChoice, 2 + l1 + 1);
-    setinstaux(p++, IBack, 0, n);
-    p += addpatt(L, p, 1);
-    setinst(p++, IBackCommit, 2);
-    setinst(p, IFail, 0);
-  }
-  return 1;
 }
 
 
+static int nocalls (const Instruction *p)
+{
+    for (; (Opcode)p->i.code != IEnd; p += sizei(p))
+        if ((Opcode)p->i.code == IOpenCall) return 0;
+    return 1;
+}
+
 
-static int firstpart (Instruction *p, int l) {
-  if (istest(p)) {
-    int e = p[0].i.offset - 1;
-    if ((p[e].i.code == IJmp || p[e].i.code == ICommit) &&
-        e + p[e].i.offset == l)
-      return e + 1;
-  }
-  else if (p[0].i.code == IChoice) {
-    int e = p[0].i.offset - 1;
-    if (p[e].i.code == ICommit && e + p[e].i.offset == l)
-      return e + 1;
-  }
-  return 0;
+static int pattbehind (lua_State *L)
+{
+    int l1;
+    CharsetTag st1;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    int n = luaL_optint(L, 2, 1);
+    luaL_argcheck(L, n <= MAXAUX, 2, "lookbehind delta too large");
+    if (!nocalls(p1))
+        luaL_error(L, "lookbehind pattern cannot contain non terminals");
+    if (isfail(p1) || issucc(p1))
+        lua_pushvalue(L, 1);  /* <fail == fail; <true == true */
+    else if (n == 1 && tocharset(p1, &st1) == ISCHARSET) {
+        Instruction *p = newpatt(L, 1 + l1);
+        setinstaux(p, IBack, 0, 1); p++;
+        copypatt(p, p1, l1);
+    }
+    else {  /* Choice L1; Back; p1; BackCommit L2; L1: fail; L2: */
+        Instruction *p = newpatt(L, 2 + l1 + 2);
+        setinst(p++, IChoice, 2 + l1 + 1);
+        setinstaux(p++, IBack, 0, n);
+        p += addpatt(L, p, 1);
+        setinst(p++, IBackCommit, 2);
+        setinst(p, IFail, 0);
+    }
+    return 1;
+}
+
+
+
+static int firstpart (Instruction *p, int l)
+{
+    if (istest(p)) {
+        int e = p[0].i.offset - 1;
+        if ((p[e].i.code == IJmp || p[e].i.code == ICommit) &&
+            e + p[e].i.offset == l)
+            return e + 1;
+    }
+    else if (p[0].i.code == IChoice) {
+        int e = p[0].i.offset - 1;
+        if (p[e].i.code == ICommit && e + p[e].i.offset == l)
+            return e + 1;
+    }
+    return 0;
 }
 
 
 static Instruction *auxnew (lua_State *L, Instruction **op, int *size,
-                                         int extra) {
-  *op = newpatt(L, *size + extra);
-  jointable(L, 1);
-  *size += extra;
-  return *op + *size - extra;
+                            int extra)
+{
+    *op = newpatt(L, *size + extra);
+    jointable(L, 1);
+    *size += extra;
+    return *op + *size - extra;
 }
 
 
-static int nofail (Instruction *p, int l) {
-  int i;
-  for (i = 0; i < l; i += sizei(p + i)) {
-    if (!isnofail(p + i)) return 0;
-  }
-  return 1;
+static int nofail (Instruction *p, int l)
+{
+    int i;
+    for (i = 0; i < l; i += sizei(p + i)) {
+        if (!isnofail(p + i)) return 0;
+    }
+    return 1;
 }
 
 
-static int interfere (Instruction *p1, int l1, CharsetTag *st2) {
-  if (nofail(p1, l1))  /* p1 cannot fail? */
-    return 0;  /* nothing can intefere with it */
-  if (st2->tag == NOINFO) return 1;
-  assert(p1->i.offset != 0);
-  switch (p1->i.code) {
+static int interfere (Instruction *p1, int l1, CharsetTag *st2)
+{
+    if (nofail(p1, l1))  /* p1 cannot fail? */
+        return 0;  /* nothing can intefere with it */
+    if (st2->tag == NOINFO) return 1;
+    assert(p1->i.offset != 0);
+    switch (p1->i.code) {
     case IChar: return testchar(st2->cs, p1->i.aux);
     case ISet: return !exclusiveset(st2->cs, (p1 + 1)->buff);
     default: assert(p1->i.code == IAny); return 1;
-  }
+    }
 }
 
 
 static Instruction *basicUnion (lua_State *L, Instruction *p1, int l1,
-                                int l2, int *size, CharsetTag *st2) {
-  Instruction *op;
-  CharsetTag st1;
-  tocharset(p1, &st1);
-  if (st1.tag == ISCHARSET && st2->tag == ISCHARSET) {
-    Instruction *p = auxnew(L, &op, size, CHARSETINSTSIZE);
-    setinst(p, ISet, 0);
-    loopset(i, p[1].buff[i] = st1.cs[i] | st2->cs[i]);
-  }
-  else if (exclusive(&st1, st2) || isheadfail(p1)) {
-    Instruction *p = auxnew(L, &op, size, l1 + 1 + l2);
-    copypatt(p, p1, l1);
-    check2test(p, l1 + 1);
-    p += l1;
-    setinst(p++, IJmp, l2 + 1);
-    addpatt(L, p, 2);
-  }
-  else {
-    /* choice L1; e1; commit L2; L1: e2; L2: ... */
-    Instruction *p = auxnew(L, &op, size, 1 + l1 + 1 + l2);
-    setinst(p++, IChoice, 1 + l1 + 1);
-    copypatt(p, p1, l1); p += l1;
-    setinst(p++, ICommit, 1 + l2);
-    addpatt(L, p, 2);
-    optimizechoice(p - (1 + l1 + 1));
-  }
-  return op;
+                                int l2, int *size, CharsetTag *st2)
+{
+    Instruction *op;
+    CharsetTag st1;
+    tocharset(p1, &st1);
+    if (st1.tag == ISCHARSET && st2->tag == ISCHARSET) {
+        Instruction *p = auxnew(L, &op, size, CHARSETINSTSIZE);
+        setinst(p, ISet, 0);
+        loopset(i, p[1].buff[i] = st1.cs[i] | st2->cs[i]);
+    }
+    else if (exclusive(&st1, st2) || isheadfail(p1)) {
+        Instruction *p = auxnew(L, &op, size, l1 + 1 + l2);
+        copypatt(p, p1, l1);
+        check2test(p, l1 + 1);
+        p += l1;
+        setinst(p++, IJmp, l2 + 1);
+        addpatt(L, p, 2);
+    }
+    else {
+        /* choice L1; e1; commit L2; L1: e2; L2: ... */
+        Instruction *p = auxnew(L, &op, size, 1 + l1 + 1 + l2);
+        setinst(p++, IChoice, 1 + l1 + 1);
+        copypatt(p, p1, l1); p += l1;
+        setinst(p++, ICommit, 1 + l2);
+        addpatt(L, p, 2);
+        optimizechoice(p - (1 + l1 + 1));
+    }
+    return op;
 }
 
 
 static Instruction *separateparts (lua_State *L, Instruction *p1, int l1,
-                                   int l2, int *size, CharsetTag *st2) {
-  int sp = firstpart(p1, l1);
-  if (sp == 0)  /* first part is entire p1? */
-    return basicUnion(L, p1, l1, l2, size, st2);
-  else if ((p1 + sp - 1)->i.code == ICommit || !interfere(p1, sp, st2)) {
-    Instruction *p;
-    int init = *size;
-    int end = init + sp;
-    *size = end;
-    p = separateparts(L, p1 + sp, l1 - sp, l2, size, st2);
-    copypatt(p + init, p1, sp);
-    (p + end - 1)->i.offset = *size - (end - 1);
-    return p;
-  }
-  else {  /* must change back to non-optimized choice */
-    Instruction *p;
-    int init = *size;
-    int end = init + sp + 1;  /* needs one extra instruction (choice) */
-    int sizefirst = sizei(p1);  /* size of p1's first instruction (the test) */
-    *size = end;
-    p = separateparts(L, p1 + sp, l1 - sp, l2, size, st2);
-    copypatt(p + init, p1, sizefirst);  /* copy the test */
-    (p + init)->i.offset++;  /* correct jump (because of new instruction) */
-    init += sizefirst;
-    setinstaux(p + init, IChoice, sp - sizefirst + 1, 1); init++;
-    copypatt(p + init, p1 + sizefirst, sp - sizefirst - 1);
-    init += sp - sizefirst - 1;
-    setinst(p + init, ICommit, *size - (end - 1));
-    return p;
-  }
+                                   int l2, int *size, CharsetTag *st2)
+{
+    int sp = firstpart(p1, l1);
+    if (sp == 0)  /* first part is entire p1? */
+        return basicUnion(L, p1, l1, l2, size, st2);
+    else if ((p1 + sp - 1)->i.code == ICommit || !interfere(p1, sp, st2)) {
+        Instruction *p;
+        int init = *size;
+        int end = init + sp;
+        *size = end;
+        p = separateparts(L, p1 + sp, l1 - sp, l2, size, st2);
+        copypatt(p + init, p1, sp);
+        (p + end - 1)->i.offset = *size - (end - 1);
+        return p;
+    }
+    else {  /* must change back to non-optimized choice */
+        Instruction *p;
+        int init = *size;
+        int end = init + sp + 1;  /* needs one extra instruction (choice) */
+        int sizefirst = sizei(p1);  /* size of p1's first instruction (the test) */
+        *size = end;
+        p = separateparts(L, p1 + sp, l1 - sp, l2, size, st2);
+        copypatt(p + init, p1, sizefirst);  /* copy the test */
+        (p + init)->i.offset++;  /* correct jump (because of new instruction) */
+        init += sizefirst;
+        setinstaux(p + init, IChoice, sp - sizefirst + 1, 1); init++;
+        copypatt(p + init, p1 + sizefirst, sp - sizefirst - 1);
+        init += sp - sizefirst - 1;
+        setinst(p + init, ICommit, *size - (end - 1));
+        return p;
+    }
 }
 
 
-static int union_l (lua_State *L) {
-  int l1, l2;
-  int size = 0;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  Instruction *p2 = getpatt(L, 2, &l2);
-  CharsetTag st2;
-  if (isfail(p1))  /* check for simple identities */
-    lua_pushvalue(L, 2);  /* fail / a == a */
-  else if (isfail(p2) || issucc(p1))
-    lua_pushvalue(L, 1);  /* a / fail == a; true / a == true */
-  else {
-    tocharset(p2, &st2);
-    separateparts(L, p1, l1, l2, &size, &st2);
-  }
-  return 1;
+static int union_l (lua_State *L)
+{
+    int l1, l2;
+    int size = 0;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    Instruction *p2 = getpatt(L, 2, &l2);
+    CharsetTag st2;
+    if (isfail(p1))  /* check for simple identities */
+        lua_pushvalue(L, 2);  /* fail / a == a */
+    else if (isfail(p2) || issucc(p1))
+        lua_pushvalue(L, 1);  /* a / fail == a; true / a == true */
+    else {
+        tocharset(p2, &st2);
+        separateparts(L, p1, l1, l2, &size, &st2);
+    }
+    return 1;
 }
 
 
-static int repeatcharset (lua_State *L, Charset cs, int l1, int n) {
-  /* e; ...; e; span; */
-  int i;
-  Instruction *p = newpatt(L, n*l1 + CHARSETINSTSIZE);
-  for (i = 0; i < n; i++) {
-    p += addpatt(L, p, 1);
-  }
-  setinst(p, ISpan, 0);
-  loopset(k, p[1].buff[k] = cs[k]);
-  return 1;
+static int repeatcharset (lua_State *L, Charset cs, int l1, int n)
+{
+    /* e; ...; e; span; */
+    int i;
+    Instruction *p = newpatt(L, n * l1 + CHARSETINSTSIZE);
+    for (i = 0; i < n; i++) {
+        p += addpatt(L, p, 1);
+    }
+    setinst(p, ISpan, 0);
+    loopset(k, p[1].buff[k] = cs[k]);
+    return 1;
 }
 
 
-static Instruction *repeatheadfail (lua_State *L, int l1, int n) {
-  /* e; ...; e; L2: e'(L1); jump L2; L1: ... */
-  int i;
-  Instruction *p = newpatt(L, (n + 1)*l1 + 1);
-  Instruction *op = p;
-  for (i = 0; i < n; i++) {
-    p += addpatt(L, p, 1);
-  }
-  p += addpatt(L, p, 1);
-  check2test(p - l1, l1 + 1);
-  setinst(p, IJmp, -l1);
-  return op;
-}
-
-
-static Instruction *repeats (lua_State *L, Instruction *p1, int l1, int n) {
-  /* e; ...; e; choice L1; L2: e; partialcommit L2; L1: ... */
-  int i;
-  Instruction *op = newpatt(L, (n + 1)*l1 + 2);
-  Instruction *p = op;
-  if (!verify(L, p1, p1, p1 + l1, 0, 0))
-    luaL_error(L, "loop body may accept empty string");
-  for (i = 0; i < n; i++) {
+static Instruction *repeatheadfail (lua_State *L, int l1, int n)
+{
+    /* e; ...; e; L2: e'(L1); jump L2; L1: ... */
+    int i;
+    Instruction *p = newpatt(L, (n + 1) * l1 + 1);
+    Instruction *op = p;
+    for (i = 0; i < n; i++) {
+        p += addpatt(L, p, 1);
+    }
     p += addpatt(L, p, 1);
-  }
-  setinst(p++, IChoice, 1 + l1 + 1);
-  p += addpatt(L, p, 1);
-  setinst(p, IPartialCommit, -l1);
-  return op;
+    check2test(p - l1, l1 + 1);
+    setinst(p, IJmp, -l1);
+    return op;
 }
 
 
-static void optionalheadfail (lua_State *L, int l1, int n) {
-  Instruction *op = newpatt(L, n * l1);
-  Instruction *p = op;
-  int i;
-  for (i = 0; i < n; i++) {
+static Instruction *repeats (lua_State *L, Instruction *p1, int l1, int n)
+{
+    /* e; ...; e; choice L1; L2: e; partialcommit L2; L1: ... */
+    int i;
+    Instruction *op = newpatt(L, (n + 1) * l1 + 2);
+    Instruction *p = op;
+    if (!verify(L, p1, p1, p1 + l1, 0, 0))
+        luaL_error(L, "loop body may accept empty string");
+    for (i = 0; i < n; i++) {
+        p += addpatt(L, p, 1);
+    }
+    setinst(p++, IChoice, 1 + l1 + 1);
     p += addpatt(L, p, 1);
-    check2test(p - l1, (n - i)*l1);
-  }
+    setinst(p, IPartialCommit, -l1);
+    return op;
 }
 
 
-static void optionals (lua_State *L, int l1, int n) {
-  /* choice L1; e; partialcommit L2; L2: ... e; L1: commit L3; L3: ... */
-  int i;
-  Instruction *op = newpatt(L, n*(l1 + 1) + 1);
-  Instruction *p = op;
-  setinst(p++, IChoice, 1 + n*(l1 + 1));
-  for (i = 0; i < n; i++) {
-    p += addpatt(L, p, 1);
-    setinst(p++, IPartialCommit, 1);
-  }
-  setinst(p - 1, ICommit, 1);  /* correct last commit */
-  optimizechoice(op);
+static void optionalheadfail (lua_State *L, int l1, int n)
+{
+    Instruction *op = newpatt(L, n * l1);
+    Instruction *p = op;
+    int i;
+    for (i = 0; i < n; i++) {
+        p += addpatt(L, p, 1);
+        check2test(p - l1, (n - i)*l1);
+    }
 }
 
 
-static int star_l (lua_State *L) {
-  int l1;
-  int n = luaL_checkint(L, 2);
-  Instruction *p1 = getpatt(L, 1, &l1);
-  if (n >= 0) {
-    CharsetTag st;
-    Instruction *op;
-    if (tocharset(p1, &st) == ISCHARSET)
-      return repeatcharset(L, st.cs, l1, n);
-    if (isheadfail(p1))
-      op = repeatheadfail(L, l1, n);
-    else
-      op = repeats(L, p1, l1, n);
-    optimizecaptures(op);
-    optimizejumps(op);
-  }
-  else {
-    if (isheadfail(p1))
-      optionalheadfail(L, l1, -n);
-    else
-      optionals(L, l1, -n);
-  }
-  return 1;
+static void optionals (lua_State *L, int l1, int n)
+{
+    /* choice L1; e; partialcommit L2; L2: ... e; L1: commit L3; L3: ... */
+    int i;
+    Instruction *op = newpatt(L, n * (l1 + 1) + 1);
+    Instruction *p = op;
+    setinst(p++, IChoice, 1 + n * (l1 + 1));
+    for (i = 0; i < n; i++) {
+        p += addpatt(L, p, 1);
+        setinst(p++, IPartialCommit, 1);
+    }
+    setinst(p - 1, ICommit, 1);  /* correct last commit */
+    optimizechoice(op);
+}
+
+
+static int star_l (lua_State *L)
+{
+    int l1;
+    int n = luaL_checkint(L, 2);
+    Instruction *p1 = getpatt(L, 1, &l1);
+    if (n >= 0) {
+        CharsetTag st;
+        Instruction *op;
+        if (tocharset(p1, &st) == ISCHARSET)
+            return repeatcharset(L, st.cs, l1, n);
+        if (isheadfail(p1))
+            op = repeatheadfail(L, l1, n);
+        else
+            op = repeats(L, p1, l1, n);
+        optimizecaptures(op);
+        optimizejumps(op);
+    }
+    else {
+        if (isheadfail(p1))
+            optionalheadfail(L, l1, -n);
+        else
+            optionals(L, l1, -n);
+    }
+    return 1;
 }
 
 
-static int getlabel (lua_State *L, int labelidx) {
-  if (labelidx == 0) return 0;
-  else return value2fenv(L, labelidx);
+static int getlabel (lua_State *L, int labelidx)
+{
+    if (labelidx == 0) return 0;
+    else return value2fenv(L, labelidx);
 }
 
 
-static int capture_aux (lua_State *L, int kind, int labelidx) {
-  int l1, n;
-  Instruction *p1 = getpatt(L, 1, &l1);
-  int lc = skipchecks(p1, 0, &n);
-  if (lc == l1) {  /* got whole pattern? */
-    /* may use a IFullCapture instruction at its end */
-    Instruction *p = newpatt(L, l1 + 1);
-    int label = getlabel(L, labelidx);
-    p += addpatt(L, p, 1);
-    setinstcap(p, IFullCapture, label, kind, n);
-  }
-  else {  /* must use open-close pair */
-    Instruction *op = newpatt(L, 1 + l1 + 1);
-    Instruction *p = op;
-    setinstcap(p++, IOpenCapture, getlabel(L, labelidx), kind, 0);
-    p += addpatt(L, p, 1);
-    setinstcap(p, ICloseCapture, 0, Cclose, 0);
-    optimizecaptures(op);
-  }
-  return 1;
+static int capture_aux (lua_State *L, int kind, int labelidx)
+{
+    int l1, n;
+    Instruction *p1 = getpatt(L, 1, &l1);
+    int lc = skipchecks(p1, 0, &n);
+    if (lc == l1) {  /* got whole pattern? */
+        /* may use a IFullCapture instruction at its end */
+        Instruction *p = newpatt(L, l1 + 1);
+        int label = getlabel(L, labelidx);
+        p += addpatt(L, p, 1);
+        setinstcap(p, IFullCapture, label, kind, n);
+    }
+    else {  /* must use open-close pair */
+        Instruction *op = newpatt(L, 1 + l1 + 1);
+        Instruction *p = op;
+        setinstcap(p++, IOpenCapture, getlabel(L, labelidx), kind, 0);
+        p += addpatt(L, p, 1);
+        setinstcap(p, ICloseCapture, 0, Cclose, 0);
+        optimizecaptures(op);
+    }
+    return 1;
 }
 
 
@@ -1701,87 +1766,95 @@ static int capture_l (lua_State *L) { return capture_aux(L, Csimple, 0); }
 static int tcapture_l (lua_State *L) { return capture_aux(L, Ctable, 0); }
 static int capsubst_l (lua_State *L) { return capture_aux(L, Csubst, 0); }
 
-static int rcapture_l (lua_State *L) {
-  switch (lua_type(L, 2)) {
+static int rcapture_l (lua_State *L)
+{
+    switch (lua_type(L, 2)) {
     case LUA_TFUNCTION: return capture_aux(L, Cfunction, 2);
     case LUA_TTABLE: return capture_aux(L, Cquery, 2);
     case LUA_TSTRING: return capture_aux(L, Cstring, 2);
     default: return luaL_argerror(L, 2, "invalid replacement value");
-  }
+    }
 }
 
 
-static int fold_l (lua_State *L) {
-  luaL_checktype(L, 2, LUA_TFUNCTION);
-  return capture_aux(L, Cfold, 2);
+static int fold_l (lua_State *L)
+{
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    return capture_aux(L, Cfold, 2);
 }
 
 
-static int group_l (lua_State *L) {
-  if (lua_isnoneornil(L, 2))
-    return capture_aux(L, Cgroup, 0);
-  else {
-    luaL_checkstring(L, 2);
-    return capture_aux(L, Cgroup, 2);
-  }
+static int group_l (lua_State *L)
+{
+    if (lua_isnoneornil(L, 2))
+        return capture_aux(L, Cgroup, 0);
+    else {
+        luaL_checkstring(L, 2);
+        return capture_aux(L, Cgroup, 2);
+    }
 }
 
 
-static int position_l (lua_State *L) {
-  Instruction *p = newpatt(L, 1);
-  setinstcap(p, IEmptyCapture, 0, Cposition, 0);
-  return 1;
+static int position_l (lua_State *L)
+{
+    Instruction *p = newpatt(L, 1);
+    setinstcap(p, IEmptyCapture, 0, Cposition, 0);
+    return 1;
 }
 
 
-static int backref_l (lua_State *L) {
-  Instruction *p = newpatt(L, 1);
-  int n = getlabel(L, 1);
-  setinstcap(p, IEmptyCaptureIdx, n, Cbackref, 0);
-  return 1;
+static int backref_l (lua_State *L)
+{
+    Instruction *p = newpatt(L, 1);
+    int n = getlabel(L, 1);
+    setinstcap(p, IEmptyCaptureIdx, n, Cbackref, 0);
+    return 1;
 }
 
 
-static int argcap_l (lua_State *L) {
-  int n = luaL_checkint(L, 1);
-  Instruction *p = newpatt(L, 1);
-  luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");
-  setinstcap(p, IEmptyCapture, n, Carg, 0);
-  return 1;
+static int argcap_l (lua_State *L)
+{
+    int n = luaL_checkint(L, 1);
+    Instruction *p = newpatt(L, 1);
+    luaL_argcheck(L, 0 < n && n <= SHRT_MAX, 1, "invalid argument index");
+    setinstcap(p, IEmptyCapture, n, Carg, 0);
+    return 1;
 }
 
 
-static int matchtime_l (lua_State *L) {
-  int l1 = getpattl(L, 1);
-  Instruction *op = newpatt(L, 1 + l1 + 1);
-  Instruction *p = op;
-  luaL_checktype(L, 2, LUA_TFUNCTION);
-  setinstcap(p++, IOpenCapture, value2fenv(L, 2), Cruntime, 0);
-  p += addpatt(L, p, 1);
-  setinstcap(p, ICloseRunTime, 0, Cclose, 0);
-  optimizecaptures(op);
-  return 1;
+static int matchtime_l (lua_State *L)
+{
+    int l1 = getpattl(L, 1);
+    Instruction *op = newpatt(L, 1 + l1 + 1);
+    Instruction *p = op;
+    luaL_checktype(L, 2, LUA_TFUNCTION);
+    setinstcap(p++, IOpenCapture, value2fenv(L, 2), Cruntime, 0);
+    p += addpatt(L, p, 1);
+    setinstcap(p, ICloseRunTime, 0, Cclose, 0);
+    optimizecaptures(op);
+    return 1;
 }
 
 
-static int capconst_l (lua_State *L) {
-  int i, j;
-  int n = lua_gettop(L);
-  Instruction *p = newpatt(L, n > 1 ? n + 2 : n);
-  lua_createtable(L, n, 0);  /* new environment for the new pattern */
-  if (n > 1) setinstcap(p++, IOpenCapture, 0, Cgroup, 0);
-  for (i = j = 1; i <= n; i++) {
-    if (lua_isnil(L, i))
-      setinstcap(p++, IEmptyCaptureIdx, 0, Cconst, 0);
-    else {
-      setinstcap(p++, IEmptyCaptureIdx, j, Cconst, 0);
-      lua_pushvalue(L, i);
-      lua_rawseti(L, -2, j++);
+static int capconst_l (lua_State *L)
+{
+    int i, j;
+    int n = lua_gettop(L);
+    Instruction *p = newpatt(L, n > 1 ? n + 2 : n);
+    lua_createtable(L, n, 0);  /* new environment for the new pattern */
+    if (n > 1) setinstcap(p++, IOpenCapture, 0, Cgroup, 0);
+    for (i = j = 1; i <= n; i++) {
+        if (lua_isnil(L, i))
+            setinstcap(p++, IEmptyCaptureIdx, 0, Cconst, 0);
+        else {
+            setinstcap(p++, IEmptyCaptureIdx, j, Cconst, 0);
+            lua_pushvalue(L, i);
+            lua_rawseti(L, -2, j++);
+        }
     }
-  }
-  if (n > 1) setinstcap(p++, ICloseCapture, 0, Cclose, 0);
-  lua_setfenv(L, -2);   /* set environment */
-  return 1;
+    if (n > 1) setinstcap(p++, ICloseCapture, 0, Cclose, 0);
+    lua_setfenv(L, -2);   /* set environment */
+    return 1;
 }
 
 
@@ -1794,15 +1867,16 @@ static int capconst_l (lua_State *L) {
 ** =======================================================
 */
 
-static void l_newpf (lua_State *L, PattFunc f, const void *ud, size_t l) {
-  int n = instsize(l) + 1;
-  Instruction *p = newpatt(L, n);
-  if (n > MAXAUX) luaL_error(L, "pattern data too long");
-  p[0].i.code = IFunc;
-  p[0].i.aux = n - 2;
-  p[0].i.offset = 0;
-  p[1].f = f;
-  memcpy(p[2].buff, ud, l);
+static void l_newpf (lua_State *L, PattFunc f, const void *ud, size_t l)
+{
+    int n = instsize(l) + 1;
+    Instruction *p = newpatt(L, n);
+    if (n > MAXAUX) luaL_error(L, "pattern data too long");
+    p[0].i.code = IFunc;
+    p[0].i.aux = n - 2;
+    p[0].i.offset = 0;
+    p[1].f = f;
+    memcpy(p[2].buff, ud, l);
 }
 
 /* }====================================================== */
@@ -1816,12 +1890,12 @@ static void l_newpf (lua_State *L, PattFunc f, const void *ud, size_t l) {
 
 
 typedef struct CapState {
-  Capture *cap;  /* current capture */
-  Capture *ocap;  /* (original) capture list */
-  lua_State *L;
-  int ptop;  /* index of last argument to 'match' */
-  const char *s;  /* original string */
-  int valuecached;  /* value stored in cache slot */
+    Capture *cap;  /* current capture */
+    Capture *ocap;  /* (original) capture list */
+    lua_State *L;
+    int ptop;  /* index of last argument to 'match' */
+    const char *s;  /* original string */
+    int valuecached;  /* value stored in cache slot */
 } CapState;
 
 
@@ -1842,229 +1916,241 @@ typedef struct CapState {
 #define updatecache(cs,v) { if ((v) != (cs)->valuecached) updatecache_(cs,v); }
 
 
-static void updatecache_ (CapState *cs, int v) {
-  getfromenv(cs, v);
-  lua_replace(cs->L, subscache(cs));
-  cs->valuecached = v;
+static void updatecache_ (CapState *cs, int v)
+{
+    getfromenv(cs, v);
+    lua_replace(cs->L, subscache(cs));
+    cs->valuecached = v;
 }
 
 
 static int pushcapture (CapState *cs);
 
 
-static Capture *findopen (Capture *cap) {
-  int n = 0;
-  for (;;) {
-    cap--;
-    if (isclosecap(cap)) n++;
-    else if (!isfullcap(cap))
-      if (n-- == 0) return cap;
-  }
+static Capture *findopen (Capture *cap)
+{
+    int n = 0;
+    for (;;) {
+        cap--;
+        if (isclosecap(cap)) n++;
+        else if (!isfullcap(cap))
+            if (n-- == 0) return cap;
+    }
+}
+
+
+static Capture *nextcap (Capture *cap)
+{
+    if (isfullcap(cap)) return cap + 1;
+    else {
+        int n = 0;
+        for (;;) {
+            cap++;
+            if (isclosecap(cap)) {
+                if (n-- == 0) return cap + 1;
+            }
+            else if (!isfullcap(cap)) n++;
+        }
+    }
 }
 
 
-static Capture *nextcap (Capture *cap) {
-  if (isfullcap(cap)) return cap + 1;
-  else {
+static int pushallvalues (CapState *cs, int addextra)
+{
+    Capture *co = cs->cap;
     int n = 0;
+    if (isfullcap(cs->cap++)) {
+        pushsubject(cs, co);  /* push whole match */
+        return 1;
+    }
+    while (!isclosecap(cs->cap))
+        n += pushcapture(cs);
+    if (addextra || n == 0) {  /* need extra? */
+        lua_pushlstring(cs->L, co->s, cs->cap->s - co->s);  /* push whole match */
+        n++;
+    }
+    cs->cap++;  /* skip close entry */
+    return n;
+}
+
+
+static Capture *findback (CapState *cs, Capture *cap)
+{
+    lua_State *L = cs->L;
     for (;;) {
-      cap++;
-      if (isclosecap(cap)) {
-        if (n-- == 0) return cap + 1;
-      }
-      else if (!isfullcap(cap)) n++;
+        if (cap == cs->ocap) {  /* not found */
+            const char *s = lua_tostring(L, -1);
+            if (s == NULL) s = lua_pushfstring(L, "(a %s)", luaL_typename(L, -1));
+            luaL_error(L, "back reference '%s' not found", s);
+        }
+        cap--;
+        if (isclosecap(cap))
+            cap = findopen(cap);
+        else if (!isfullcap(cap))
+            continue; /* opening an enclosing capture: skip and get previous */
+        if (captype(cap) == Cgroup) {
+            getfromenv(cs, cap->idx);  /* get group name */
+            if (lua_equal(L, -2, -1)) {  /* right group? */
+                lua_pop(L, 2);  /* remove reference name and group name */
+                return cap;
+            }
+            else lua_pop(L, 1);  /* remove group name */
+        }
     }
-  }
 }
 
 
-static int pushallvalues (CapState *cs, int addextra) {
-  Capture *co = cs->cap;
-  int n = 0;
-  if (isfullcap(cs->cap++)) {
-    pushsubject(cs, co);  /* push whole match */
-    return 1;
-  }
-  while (!isclosecap(cs->cap))
-    n += pushcapture(cs);
-  if (addextra || n == 0) {  /* need extra? */
-    lua_pushlstring(cs->L, co->s, cs->cap->s - co->s);  /* push whole match */
-    n++;
-  }
-  cs->cap++;  /* skip close entry */
-  return n;
-}
-
-
-static Capture *findback (CapState *cs, Capture *cap) {
-  lua_State *L = cs->L;
-  for (;;) {
-    if (cap == cs->ocap) {  /* not found */
-      const char *s = lua_tostring(L, -1);
-      if (s == NULL) s = lua_pushfstring(L, "(a %s)", luaL_typename(L, -1));
-      luaL_error(L, "back reference '%s' not found", s);
-    }
-    cap--;
-    if (isclosecap(cap))
-      cap = findopen(cap);
-    else if (!isfullcap(cap))
-      continue; /* opening an enclosing capture: skip and get previous */
-    if (captype(cap) == Cgroup) {
-      getfromenv(cs, cap->idx);  /* get group name */
-      if (lua_equal(L, -2, -1)) {  /* right group? */
-        lua_pop(L, 2);  /* remove reference name and group name */
-        return cap;
-      }
-      else lua_pop(L, 1);  /* remove group name */
-    }
-  }
-}
-
-
-static int backrefcap (CapState *cs) {
-  int n;
-  Capture *curr = cs->cap;
-  pushluaval(cs);  /* reference name */
-  cs->cap = findback(cs, curr);
-  n = pushallvalues(cs, 0);
-  cs->cap = curr + 1;
-  return n;
-}
-
-
-static int tablecap (CapState *cs) {
-  lua_State *L = cs->L;
-  int n = 0;
-  lua_newtable(L);
-  if (isfullcap(cs->cap++))
-    return 1;  /* table is empty */
-  while (!isclosecap(cs->cap)) {
-    if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) {  /* named group? */
-      int k;
-      pushluaval(cs);  /* push group name */
-      k = pushallvalues(cs, 0);
-      if (k == 0) {  /* no value? */
-        lua_pop(L, 1);  /* remove group name */
-        continue;  /* and go on */
-      }
-      else if (k > 1)
-        lua_pop(L, k - 1);  /* keep just one value */
-      lua_settable(L, -3);
+static int backrefcap (CapState *cs)
+{
+    int n;
+    Capture *curr = cs->cap;
+    pushluaval(cs);  /* reference name */
+    cs->cap = findback(cs, curr);
+    n = pushallvalues(cs, 0);
+    cs->cap = curr + 1;
+    return n;
+}
+
+
+static int tablecap (CapState *cs)
+{
+    lua_State *L = cs->L;
+    int n = 0;
+    lua_newtable(L);
+    if (isfullcap(cs->cap++))
+        return 1;  /* table is empty */
+    while (!isclosecap(cs->cap)) {
+        if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) {  /* named group? */
+            int k;
+            pushluaval(cs);  /* push group name */
+            k = pushallvalues(cs, 0);
+            if (k == 0) {  /* no value? */
+                lua_pop(L, 1);  /* remove group name */
+                continue;  /* and go on */
+            }
+            else if (k > 1)
+                lua_pop(L, k - 1);  /* keep just one value */
+            lua_settable(L, -3);
+        }
+        else {
+            int i;
+            int k = pushcapture(cs);
+            for (i = k; i > 0; i--)
+                lua_rawseti(L, -(i + 1), n + i);
+            n += k;
+        }
     }
-    else {
-      int i;
-      int k = pushcapture(cs);
-      for (i = k; i > 0; i--)
-        lua_rawseti(L, -(i + 1), n + i);
-      n += k;
-    }
-  }
-  cs->cap++;  /* skip close entry */
-  return 1;
-}
-
-
-static int querycap (CapState *cs) {
-  int idx = cs->cap->idx;
-  int n = pushallvalues(cs, 0);
-  if (n > 1)  /* extra captures? */
-    lua_pop(cs->L, n - 1);  /* throw them away */
-  updatecache(cs, idx);
-  lua_gettable(cs->L, subscache(cs));
-  if (!lua_isnil(cs->L, -1))
+    cs->cap++;  /* skip close entry */
     return 1;
-  else {
-    lua_pop(cs->L, 1);  /* remove value */
-    return 0;
-  }
 }
 
 
-static int foldcap (CapState *cs) {
-  int n;
-  lua_State *L = cs->L;
-  int idx = cs->cap->idx;
-  if (isfullcap(cs->cap++) || isclosecap(cs->cap) || (n = pushcapture(cs)) == 0)
-    return luaL_error(L, "no initial value for fold capture");
-  if (n > 1)
-    lua_pop(L, n - 1);  /* leave only one result */
-  while (!isclosecap(cs->cap)) {
+static int querycap (CapState *cs)
+{
+    int idx = cs->cap->idx;
+    int n = pushallvalues(cs, 0);
+    if (n > 1)  /* extra captures? */
+        lua_pop(cs->L, n - 1);  /* throw them away */
     updatecache(cs, idx);
-    lua_pushvalue(L, subscache(cs));  /* get folding function */
-    lua_insert(L, -2);  /* put it before accumulator */
-    n = pushcapture(cs);  /* get other captures */
-    lua_call(L, n + 1, 1);  /* call folding function */
-  }
-  cs->cap++;  /* skip close entry */
-  return 1;
+    lua_gettable(cs->L, subscache(cs));
+    if (!lua_isnil(cs->L, -1))
+        return 1;
+    else {
+        lua_pop(cs->L, 1);  /* remove value */
+        return 0;
+    }
+}
+
+
+static int foldcap (CapState *cs)
+{
+    int n;
+    lua_State *L = cs->L;
+    int idx = cs->cap->idx;
+    if (isfullcap(cs->cap++) || isclosecap(cs->cap) || (n = pushcapture(cs)) == 0)
+        return luaL_error(L, "no initial value for fold capture");
+    if (n > 1)
+        lua_pop(L, n - 1);  /* leave only one result */
+    while (!isclosecap(cs->cap)) {
+        updatecache(cs, idx);
+        lua_pushvalue(L, subscache(cs));  /* get folding function */
+        lua_insert(L, -2);  /* put it before accumulator */
+        n = pushcapture(cs);  /* get other captures */
+        lua_call(L, n + 1, 1);  /* call folding function */
+    }
+    cs->cap++;  /* skip close entry */
+    return 1;
 }
 
 
-static int functioncap (CapState *cs) {
-  int n;
-  int top = lua_gettop(cs->L);
-  pushluaval(cs);
-  n = pushallvalues(cs, 0);
-  lua_call(cs->L, n, LUA_MULTRET);
-  return lua_gettop(cs->L) - top;
+static int functioncap (CapState *cs)
+{
+    int n;
+    int top = lua_gettop(cs->L);
+    pushluaval(cs);
+    n = pushallvalues(cs, 0);
+    lua_call(cs->L, n, LUA_MULTRET);
+    return lua_gettop(cs->L) - top;
 }
 
 
 static int runtimecap (lua_State *L, Capture *close, Capture *ocap,
-                       const char *o, const char *s, int ptop) {
-  CapState cs;
-  int n;
-  Capture *open = findopen(close);
-  assert(captype(open) == Cruntime);
-  close->kind = Cclose;
-  close->s = s;
-  cs.ocap = ocap; cs.cap = open; cs.L = L;
-  cs.s = o; cs.valuecached = 0; cs.ptop = ptop;
-  luaL_checkstack(L, 4, "too many runtime captures");
-  pushluaval(&cs);
-  lua_pushvalue(L, SUBJIDX);  /* push original subject */
-  lua_pushinteger(L, s - o + 1);  /* current position */
-  n = pushallvalues(&cs, 0);
-  lua_call(L, n + 2, LUA_MULTRET);
-  return close - open;
+                       const char *o, const char *s, int ptop)
+{
+    CapState cs;
+    int n;
+    Capture *open = findopen(close);
+    assert(captype(open) == Cruntime);
+    close->kind = Cclose;
+    close->s = s;
+    cs.ocap = ocap; cs.cap = open; cs.L = L;
+    cs.s = o; cs.valuecached = 0; cs.ptop = ptop;
+    luaL_checkstack(L, 4, "too many runtime captures");
+    pushluaval(&cs);
+    lua_pushvalue(L, SUBJIDX);  /* push original subject */
+    lua_pushinteger(L, s - o + 1);  /* current position */
+    n = pushallvalues(&cs, 0);
+    lua_call(L, n + 2, LUA_MULTRET);
+    return close - open;
 }
 
 
 
 typedef struct StrAux {
-  int isstring;
-  union {
-    Capture *cp;
-    struct {
-      const char *s;
-      const char *e;
-    } s;
-  } u;
+    int isstring;
+    union {
+        Capture *cp;
+        struct {
+            const char *s;
+            const char *e;
+        } s;
+    } u;
 } StrAux;
 
 #define MAXSTRCAPS	10
 
-static int getstrcaps (CapState *cs, StrAux *cps, int n) {
-  int k = n++;
-  cps[k].isstring = 1;
-  cps[k].u.s.s = cs->cap->s;
-  if (!isfullcap(cs->cap++)) {
-    while (!isclosecap(cs->cap)) {
-      if (n >= MAXSTRCAPS)  /* too many captures? */
-        cs->cap = nextcap(cs->cap);  /* skip it */
-      else if (captype(cs->cap) == Csimple)
-        n = getstrcaps(cs, cps, n);
-      else {
-        cps[n].isstring = 0;
-        cps[n].u.cp = cs->cap;
-        cs->cap = nextcap(cs->cap);
-        n++;
-      }
+static int getstrcaps (CapState *cs, StrAux *cps, int n)
+{
+    int k = n++;
+    cps[k].isstring = 1;
+    cps[k].u.s.s = cs->cap->s;
+    if (!isfullcap(cs->cap++)) {
+        while (!isclosecap(cs->cap)) {
+            if (n >= MAXSTRCAPS)  /* too many captures? */
+                cs->cap = nextcap(cs->cap);  /* skip it */
+            else if (captype(cs->cap) == Csimple)
+                n = getstrcaps(cs, cps, n);
+            else {
+                cps[n].isstring = 0;
+                cps[n].u.cp = cs->cap;
+                cs->cap = nextcap(cs->cap);
+                n++;
+            }
+        }
+        cs->cap++;  /* skip close */
     }
-    cs->cap++;  /* skip close */
-  }
-  cps[k].u.s.e = closeaddr(cs->cap - 1);
-  return n;
+    cps[k].u.s.e = closeaddr(cs->cap - 1);
+    return n;
 }
 
 
@@ -2074,134 +2160,138 @@ static int getstrcaps (CapState *cs, StrAux *cps, int n) {
 static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
 
 
-static void stringcap (luaL_Buffer *b, CapState *cs) {
-  StrAux cps[MAXSTRCAPS];
-  int n;
-  size_t len, i;
-  const char *c;
-  updatecache(cs, cs->cap->idx);
-  c = lua_tolstring(cs->L, subscache(cs), &len);
-  n = getstrcaps(cs, cps, 0) - 1;
-  for (i = 0; i < len; i++) {
-    if (c[i] != '%' || c[++i] < '0' || c[i] > '9')
-      luaL_addchar(b, c[i]);
+static void stringcap (luaL_Buffer *b, CapState *cs)
+{
+    StrAux cps[MAXSTRCAPS];
+    int n;
+    size_t len, i;
+    const char *c;
+    updatecache(cs, cs->cap->idx);
+    c = lua_tolstring(cs->L, subscache(cs), &len);
+    n = getstrcaps(cs, cps, 0) - 1;
+    for (i = 0; i < len; i++) {
+        if (c[i] != '%' || c[++i] < '0' || c[i] > '9')
+            luaL_addchar(b, c[i]);
+        else {
+            int l = c[i] - '0';
+            if (l > n)
+                luaL_error(cs->L, "invalid capture index (%d)", l);
+            else if (cps[l].isstring)
+                luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
+            else {
+                Capture *curr = cs->cap;
+                cs->cap = cps[l].u.cp;
+                if (addonestring(b, cs, "capture") == 0)
+                    luaL_error(cs->L, "no values in capture index %d", l);
+                cs->cap = curr;
+            }
+        }
+    }
+}
+
+
+static void substcap (luaL_Buffer *b, CapState *cs)
+{
+    const char *curr = cs->cap->s;
+    if (isfullcap(cs->cap))  /* no nested captures? */
+        luaL_addlstring(b, curr, cs->cap->siz - 1);  /* keep original text */
     else {
-      int l = c[i] - '0';
-      if (l > n)
-        luaL_error(cs->L, "invalid capture index (%d)", l);
-      else if (cps[l].isstring)
-        luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
-      else {
-        Capture *curr = cs->cap;
-        cs->cap = cps[l].u.cp;
-        if (addonestring(b, cs, "capture") == 0)
-          luaL_error(cs->L, "no values in capture index %d", l);
-        cs->cap = curr;
-      }
-    }
-  }
-}
-
-
-static void substcap (luaL_Buffer *b, CapState *cs) {
-  const char *curr = cs->cap->s;
-  if (isfullcap(cs->cap))  /* no nested captures? */
-    luaL_addlstring(b, curr, cs->cap->siz - 1);  /* keep original text */
-  else {
-    cs->cap++;
-    while (!isclosecap(cs->cap)) {
-      const char *next = cs->cap->s;
-      luaL_addlstring(b, curr, next - curr);  /* add text up to capture */
-      if (addonestring(b, cs, "replacement") == 0)  /* no capture value? */
-        curr = next;  /* keep original text in final result */
-      else
-        curr = closeaddr(cs->cap - 1);  /* continue after match */
+        cs->cap++;
+        while (!isclosecap(cs->cap)) {
+            const char *next = cs->cap->s;
+            luaL_addlstring(b, curr, next - curr);  /* add text up to capture */
+            if (addonestring(b, cs, "replacement") == 0)  /* no capture value? */
+                curr = next;  /* keep original text in final result */
+            else
+                curr = closeaddr(cs->cap - 1);  /* continue after match */
+        }
+        luaL_addlstring(b, curr, cs->cap->s - curr);  /* add last piece of text */
     }
-    luaL_addlstring(b, curr, cs->cap->s - curr);  /* add last piece of text */
-  }
-  cs->cap++;  /* go to next capture */
+    cs->cap++;  /* go to next capture */
 }
 
 
-static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
-  switch (captype(cs->cap)) {
+static int addonestring (luaL_Buffer *b, CapState *cs, const char *what)
+{
+    switch (captype(cs->cap)) {
     case Cstring:
-      stringcap(b, cs);  /* add capture directly to buffer */
-      return 1;
+        stringcap(b, cs);  /* add capture directly to buffer */
+        return 1;
     case Csubst:
-      substcap(b, cs);  /* add capture directly to buffer */
-      return 1;
+        substcap(b, cs);  /* add capture directly to buffer */
+        return 1;
     default: {
-      lua_State *L = cs->L;
-      int n = pushcapture(cs);
-      if (n > 0) {
-        if (n > 1) lua_pop(L, n - 1);  /* only one result */
-        if (!lua_isstring(L, -1))
-          luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
-        luaL_addvalue(b);
-      }
-      return n;
+        lua_State *L = cs->L;
+        int n = pushcapture(cs);
+        if (n > 0) {
+            if (n > 1) lua_pop(L, n - 1);  /* only one result */
+            if (!lua_isstring(L, -1))
+                luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
+            luaL_addvalue(b);
+        }
+        return n;
+    }
     }
-  }
 }
 
 
-static int pushcapture (CapState *cs) {
-  luaL_checkstack(cs->L, 4, "too many captures");
-  switch (captype(cs->cap)) {
+static int pushcapture (CapState *cs)
+{
+    luaL_checkstack(cs->L, 4, "too many captures");
+    switch (captype(cs->cap)) {
     case Cposition: {
-      lua_pushinteger(cs->L, cs->cap->s - cs->s + 1);
-      cs->cap++;
-      return 1;
+        lua_pushinteger(cs->L, cs->cap->s - cs->s + 1);
+        cs->cap++;
+        return 1;
     }
     case Cconst: {
-      pushluaval(cs);
-      cs->cap++;
-      return 1;
+        pushluaval(cs);
+        cs->cap++;
+        return 1;
     }
     case Carg: {
-      int arg = (cs->cap++)->idx;
-      if (arg + FIXEDARGS > cs->ptop)
-        return luaL_error(cs->L, "reference to absent argument #%d", arg);
-      lua_pushvalue(cs->L, arg + FIXEDARGS);
-      return 1;
+        int arg = (cs->cap++)->idx;
+        if (arg + FIXEDARGS > cs->ptop)
+            return luaL_error(cs->L, "reference to absent argument #%d", arg);
+        lua_pushvalue(cs->L, arg + FIXEDARGS);
+        return 1;
     }
     case Csimple: {
-      int k = pushallvalues(cs, 1);
-      if (k > 1)
-        lua_insert(cs->L, -k);  /* whole match is first result */
-      return k;
+        int k = pushallvalues(cs, 1);
+        if (k > 1)
+            lua_insert(cs->L, -k);  /* whole match is first result */
+        return k;
     }
     case Cruntime: {
-      int n = 0;
-      while (!isclosecap(cs->cap++)) {
-        luaL_checkstack(cs->L, 4, "too many captures");
-        lua_pushvalue(cs->L, (cs->cap - 1)->idx);
-        n++;
-      }
-      return n;
+        int n = 0;
+        while (!isclosecap(cs->cap++)) {
+            luaL_checkstack(cs->L, 4, "too many captures");
+            lua_pushvalue(cs->L, (cs->cap - 1)->idx);
+            n++;
+        }
+        return n;
     }
     case Cstring: {
-      luaL_Buffer b;
-      luaL_buffinit(cs->L, &b);
-      stringcap(&b, cs);
-      luaL_pushresult(&b);
-      return 1;
+        luaL_Buffer b;
+        luaL_buffinit(cs->L, &b);
+        stringcap(&b, cs);
+        luaL_pushresult(&b);
+        return 1;
     }
     case Csubst: {
-      luaL_Buffer b;
-      luaL_buffinit(cs->L, &b);
-      substcap(&b, cs);
-      luaL_pushresult(&b);
-      return 1;
+        luaL_Buffer b;
+        luaL_buffinit(cs->L, &b);
+        substcap(&b, cs);
+        luaL_pushresult(&b);
+        return 1;
     }
     case Cgroup: {
-      if (cs->cap->idx == 0)  /* anonymous group? */
-        return pushallvalues(cs, 0);  /* add all nested values */
-      else {  /* named group: add no values */
-        cs->cap = nextcap(cs->cap);  /* skip capture */
-        return 0;
-      }
+        if (cs->cap->idx == 0)  /* anonymous group? */
+            return pushallvalues(cs, 0);  /* add all nested values */
+        else {  /* named group: add no values */
+            cs->cap = nextcap(cs->cap);  /* skip capture */
+            return 0;
+        }
     }
     case Cbackref: return backrefcap(cs);
     case Ctable: return tablecap(cs);
@@ -2209,181 +2299,190 @@ static int pushcapture (CapState *cs) {
     case Cquery: return querycap(cs);
     case Cfold: return foldcap(cs);
     default: assert(0); return 0;
-  }
+    }
 }
 
 
-static int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
-  Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
-  int n = 0;
-  if (!isclosecap(capture)) {  /* is there any capture? */
-    CapState cs;
-    cs.ocap = cs.cap = capture; cs.L = L;
-    cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
-    do {  /* collect their values */
-      n += pushcapture(&cs);
-    } while (!isclosecap(cs.cap));
-  }
-  if (n == 0) {  /* no capture values? */
-    lua_pushinteger(L, r - s + 1);  /* return only end position */
-    n = 1;
-  }
-  return n;
+static int getcaptures (lua_State *L, const char *s, const char *r, int ptop)
+{
+    Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
+    int n = 0;
+    if (!isclosecap(capture)) {  /* is there any capture? */
+        CapState cs;
+        cs.ocap = cs.cap = capture; cs.L = L;
+        cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
+        do {  /* collect their values */
+            n += pushcapture(&cs);
+        } while (!isclosecap(cs.cap));
+    }
+    if (n == 0) {  /* no capture values? */
+        lua_pushinteger(L, r - s + 1);  /* return only end position */
+        n = 1;
+    }
+    return n;
 }
 
 /* }====================================================== */
 
 
-static int version_l (lua_State *L) {
-  lua_pushstring(L, VERSION);
-  return 1;
+static int version_l (lua_State *L)
+{
+    lua_pushstring(L, VERSION);
+    return 1;
 }
 
 
-static int type_l (lua_State *L) {
-  if (testpattern(L, 1))
-    lua_pushliteral(L, "pattern");
-  else
-    lua_pushnil(L);
-  return 1;
+static int type_l (lua_State *L)
+{
+    if (testpattern(L, 1))
+        lua_pushliteral(L, "pattern");
+    else
+        lua_pushnil(L);
+    return 1;
 }
 
 
-static void createcat (lua_State *L, const char *catname, int (catf) (int)) {
-  Instruction *p = newcharset(L);
-  int i;
-  for (i = 0; i < CHAR_MAX; i++)
-    if (catf(i)) setchar(p[1].buff, i);
-  lua_setfield(L, -2, catname);
+static void createcat (lua_State *L, const char *catname, int (catf) (int))
+{
+    Instruction *p = newcharset(L);
+    int i;
+    for (i = 0; i < CHAR_MAX; i++)
+        if (catf(i)) setchar(p[1].buff, i);
+    lua_setfield(L, -2, catname);
 }
 
 
-static int locale_l (lua_State *L) {
-  if (lua_isnoneornil(L, 1)) {
-    lua_settop(L, 0);
-    lua_createtable(L, 0, 12);
-  }
-  else {
-    luaL_checktype(L, 1, LUA_TTABLE);
-    lua_settop(L, 1);
-  }
-  createcat(L, "alnum", isalnum);
-  createcat(L, "alpha", isalpha);
-  createcat(L, "cntrl", iscntrl);
-  createcat(L, "digit", isdigit);
-  createcat(L, "graph", isgraph);
-  createcat(L, "lower", islower);
-  createcat(L, "print", isprint);
-  createcat(L, "punct", ispunct);
-  createcat(L, "space", isspace);
-  createcat(L, "upper", isupper);
-  createcat(L, "xdigit", isxdigit);
-  return 1;
-}
-
-
-static int setmax (lua_State *L) {
-  luaL_optinteger(L, 1, -1);
-  lua_settop(L, 1);
-  lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
-  return 0;
-}
-
-
-static int printpat_l (lua_State *L) {
-  Instruction *p = getpatt(L, 1, NULL);
-  int n, i;
-  lua_getfenv(L, 1);
-  n = ktablelen(L, -1);
-  printf("[");
-  for (i = 1; i <= n; i++) {
-    printf("%d = ", i);
-    lua_rawgeti(L, -1, i);
-    if (lua_isstring(L, -1))
-      printf("%s  ", lua_tostring(L, -1));
-    else
-      printf("%s  ", lua_typename(L, lua_type(L, -1)));
-    lua_pop(L, 1);
-  }
-  printf("]\n");
-  printpatt(p);
-  return 0;
-}
-
-
-static int matchl (lua_State *L) {
-  Capture capture[INITCAPSIZE];
-  const char *r;
-  size_t l;
-  Instruction *p = getpatt(L, 1, NULL);
-  const char *s = luaL_checklstring(L, SUBJIDX, &l);
-  int ptop = lua_gettop(L);
-  lua_Integer ii = luaL_optinteger(L, 3, 1);
-  size_t i = (ii > 0) ?
-             (((size_t)ii <= l) ? (size_t)ii - 1 : l) :
-             (((size_t)-ii <= l) ? l - ((size_t)-ii) : 0);
-  lua_pushnil(L);  /* subscache */
-  lua_pushlightuserdata(L, capture);  /* caplistidx */
-  lua_getfenv(L, 1);  /* penvidx */
-  r = match(L, s, s + i, s + l, p, capture, ptop);
-  if (r == NULL) {
-    lua_pushnil(L);
+static int locale_l (lua_State *L)
+{
+    if (lua_isnoneornil(L, 1)) {
+        lua_settop(L, 0);
+        lua_createtable(L, 0, 12);
+    }
+    else {
+        luaL_checktype(L, 1, LUA_TTABLE);
+        lua_settop(L, 1);
+    }
+    createcat(L, "alnum", isalnum);
+    createcat(L, "alpha", isalpha);
+    createcat(L, "cntrl", iscntrl);
+    createcat(L, "digit", isdigit);
+    createcat(L, "graph", isgraph);
+    createcat(L, "lower", islower);
+    createcat(L, "print", isprint);
+    createcat(L, "punct", ispunct);
+    createcat(L, "space", isspace);
+    createcat(L, "upper", isupper);
+    createcat(L, "xdigit", isxdigit);
     return 1;
-  }
-  return getcaptures(L, s, r, ptop);
+}
+
+
+static int setmax (lua_State *L)
+{
+    luaL_optinteger(L, 1, -1);
+    lua_settop(L, 1);
+    lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+    return 0;
+}
+
+
+static int printpat_l (lua_State *L)
+{
+    Instruction *p = getpatt(L, 1, NULL);
+    int n, i;
+    lua_getfenv(L, 1);
+    n = ktablelen(L, -1);
+    printf("[");
+    for (i = 1; i <= n; i++) {
+        printf("%d = ", i);
+        lua_rawgeti(L, -1, i);
+        if (lua_isstring(L, -1))
+            printf("%s  ", lua_tostring(L, -1));
+        else
+            printf("%s  ", lua_typename(L, lua_type(L, -1)));
+        lua_pop(L, 1);
+    }
+    printf("]\n");
+    printpatt(p);
+    return 0;
+}
+
+
+static int matchl (lua_State *L)
+{
+    Capture capture[INITCAPSIZE];
+    const char *r;
+    size_t l;
+    Instruction *p = getpatt(L, 1, NULL);
+    const char *s = luaL_checklstring(L, SUBJIDX, &l);
+    int ptop = lua_gettop(L);
+    lua_Integer ii = luaL_optinteger(L, 3, 1);
+    size_t i = (ii > 0) ?
+               (((size_t)ii <= l) ? (size_t)ii - 1 : l) :
+               (((size_t) - ii <= l) ? l - ((size_t) - ii) : 0);
+    lua_pushnil(L);  /* subscache */
+    lua_pushlightuserdata(L, capture);  /* caplistidx */
+    lua_getfenv(L, 1);  /* penvidx */
+    r = match(L, s, s + i, s + l, p, capture, ptop);
+    if (r == NULL) {
+        lua_pushnil(L);
+        return 1;
+    }
+    return getcaptures(L, s, r, ptop);
 }
 
 
 static struct luaL_reg pattreg[] = {
-  {"match", matchl},
-  {"print", printpat_l},
-  {"locale", locale_l},
-  {"setmaxstack", setmax},
-  {"B", pattbehind},
-  {"C", capture_l},
-  {"Cf", fold_l},
-  {"Cc", capconst_l},
-  {"Cg", group_l},
-  {"Cp", position_l},
-  {"Cb", backref_l},
-  {"Carg", argcap_l},
-  {"Cmt", matchtime_l},
-  {"Cs", capsubst_l},
-  {"Ct", tcapture_l},
-  {"P", pattern_l},
-  {"R", range_l},
-  {"S", set_l},
-  {"V", nter_l},
-  {"type", type_l},
-  {"version", version_l},
-  {NULL, NULL}
+    {"match", matchl},
+    {"print", printpat_l},
+    {"locale", locale_l},
+    {"setmaxstack", setmax},
+    {"B", pattbehind},
+    {"C", capture_l},
+    {"Cf", fold_l},
+    {"Cc", capconst_l},
+    {"Cg", group_l},
+    {"Cp", position_l},
+    {"Cb", backref_l},
+    {"Carg", argcap_l},
+    {"Cmt", matchtime_l},
+    {"Cs", capsubst_l},
+    {"Ct", tcapture_l},
+    {"P", pattern_l},
+    {"R", range_l},
+    {"S", set_l},
+    {"V", nter_l},
+    {"type", type_l},
+    {"version", version_l},
+    {NULL, NULL}
 };
 
 
 static struct luaL_reg metapattreg[] = {
-  {"__add", union_l},
-  {"__pow", star_l},
-  {"__sub", diff_l},
-  {"__mul", concat_l},
-  {"__div", rcapture_l},
-  {"__unm", unm_l},
-  {"__len", pattand_l},
-  {NULL, NULL}
+    {"__add", union_l},
+    {"__pow", star_l},
+    {"__sub", diff_l},
+    {"__mul", concat_l},
+    {"__div", rcapture_l},
+    {"__unm", unm_l},
+    {"__len", pattand_l},
+    {NULL, NULL}
 };
 
 
 int luaopen_lpeg (lua_State *L);
-int luaopen_lpeg (lua_State *L) {
-  lua_pushcfunction(L, (lua_CFunction)&l_newpf);  /* new-pattern function */
-  lua_setfield(L, LUA_REGISTRYINDEX, KEYNEWPATT);  /* register it */
-  luaL_newmetatable(L, PATTERN_T);
-  lua_pushnumber(L, MAXBACK);
-  lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
-  luaL_register(L, NULL, metapattreg);
-  luaL_register(L, "lpeg", pattreg);
-  lua_pushliteral(L, "__index");
-  lua_pushvalue(L, -2);
-  lua_settable(L, -4);
-  return 1;
+int luaopen_lpeg (lua_State *L)
+{
+    lua_pushcfunction(L, (lua_CFunction)&l_newpf);  /* new-pattern function */
+    lua_setfield(L, LUA_REGISTRYINDEX, KEYNEWPATT);  /* register it */
+    luaL_newmetatable(L, PATTERN_T);
+    lua_pushnumber(L, MAXBACK);
+    lua_setfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
+    luaL_register(L, NULL, metapattreg);
+    luaL_register(L, "lpeg", pattreg);
+    lua_pushliteral(L, "__index");
+    lua_pushvalue(L, -2);
+    lua_settable(L, -4);
+    return 1;
 }
 
diff --git a/libaegisub/lua/modules/re.cpp b/libaegisub/lua/modules/re.cpp
index 66efe0f89d6e80e23c434a456b8c098dec16d850..f338721574f029e0ea9bd5a01c7d235b430d5418 100644
--- a/libaegisub/lua/modules/re.cpp
+++ b/libaegisub/lua/modules/re.cpp
@@ -25,117 +25,125 @@ namespace {
 // an int pair without an extra heap allocation each time (LuaJIT can't compile
 // ffi calls which return aggregates by value)
 struct agi_re_match {
-	boost::cmatch m;
-	int range[2];
+    boost::cmatch m;
+    int range[2];
 };
 
 struct agi_re_flag {
-	const char *name;
-	int value;
+    const char *name;
+    int value;
 };
 }
 
 namespace agi {
-	AGI_DEFINE_TYPE_NAME(u32regex);
-	AGI_DEFINE_TYPE_NAME(agi_re_match);
-	AGI_DEFINE_TYPE_NAME(agi_re_flag);
+AGI_DEFINE_TYPE_NAME(u32regex);
+AGI_DEFINE_TYPE_NAME(agi_re_match);
+AGI_DEFINE_TYPE_NAME(agi_re_flag);
 }
 
 namespace {
 using match = agi_re_match;
-bool search(u32regex& re, const char *str, size_t len, int start, boost::cmatch& result) {
-	return u32regex_search(str + start, str + len, result, re,
-		start > 0 ? boost::match_prev_avail | boost::match_not_bob : boost::match_default);
+bool search(u32regex &re, const char *str, size_t len, int start, boost::cmatch &result)
+{
+    return u32regex_search(str + start, str + len, result, re,
+                           start > 0 ? boost::match_prev_avail | boost::match_not_bob : boost::match_default);
 }
 
-match *regex_match(u32regex& re, const char *str, size_t len, int start) {
-	auto result = agi::make_unique<match>();
-	if (!search(re, str, len, start, result->m))
-		return nullptr;
-	return result.release();
+match *regex_match(u32regex &re, const char *str, size_t len, int start)
+{
+    auto result = agi::make_unique<match>();
+    if (!search(re, str, len, start, result->m))
+        return nullptr;
+    return result.release();
 }
 
-int *regex_get_match(match& match, size_t idx) {
-	if (idx > match.m.size() || !match.m[idx].matched)
-		return nullptr;
-	match.range[0] = std::distance(match.m.prefix().first, match.m[idx].first + 1);
-	match.range[1] = std::distance(match.m.prefix().first, match.m[idx].second);
-	return match.range;
+int *regex_get_match(match &match, size_t idx)
+{
+    if (idx > match.m.size() || !match.m[idx].matched)
+        return nullptr;
+    match.range[0] = std::distance(match.m.prefix().first, match.m[idx].first + 1);
+    match.range[1] = std::distance(match.m.prefix().first, match.m[idx].second);
+    return match.range;
 }
 
-int *regex_search(u32regex& re, const char *str, size_t len, size_t start) {
-	boost::cmatch result;
-	if (!search(re, str, len, start, result))
-		return nullptr;
+int *regex_search(u32regex &re, const char *str, size_t len, size_t start)
+{
+    boost::cmatch result;
+    if (!search(re, str, len, start, result))
+        return nullptr;
 
-	auto ret = static_cast<int *>(malloc(sizeof(int) * 2));
-	ret[0] = start + result.position() + 1;
-	ret[1] = start + result.position() + result.length();
-	return ret;
+    auto ret = static_cast<int *>(malloc(sizeof(int) * 2));
+    ret[0] = start + result.position() + 1;
+    ret[1] = start + result.position() + result.length();
+    return ret;
 }
 
-char *regex_replace(u32regex& re, const char *replacement, const char *str, size_t len, int max_count) {
-	// Can't just use regex_replace here since it can only do one or infinite replacements
-	auto match = boost::u32regex_iterator<const char *>(str, str + len, re);
-	auto end_it = boost::u32regex_iterator<const char *>();
-
-	auto suffix = str;
-
-	std::string ret;
-	auto out = back_inserter(ret);
-	while (match != end_it && max_count > 0) {
-		copy(suffix, match->prefix().second, out);
-		match->format(out, replacement);
-		suffix = match->suffix().first;
-		++match;
-		--max_count;
-	}
-
-	ret += suffix;
-	return agi::lua::strndup(ret);
+char *regex_replace(u32regex &re, const char *replacement, const char *str, size_t len, int max_count)
+{
+    // Can't just use regex_replace here since it can only do one or infinite replacements
+    auto match = boost::u32regex_iterator<const char *>(str, str + len, re);
+    auto end_it = boost::u32regex_iterator<const char *>();
+
+    auto suffix = str;
+
+    std::string ret;
+    auto out = back_inserter(ret);
+    while (match != end_it && max_count > 0) {
+        copy(suffix, match->prefix().second, out);
+        match->format(out, replacement);
+        suffix = match->suffix().first;
+        ++match;
+        --max_count;
+    }
+
+    ret += suffix;
+    return agi::lua::strndup(ret);
 }
 
-u32regex *regex_compile(const char *pattern, int flags, char **err) {
-	auto re = agi::make_unique<u32regex>();
-	try {
-		*re = boost::make_u32regex(pattern, boost::u32regex::perl | flags);
-		return re.release();
-	}
-	catch (std::exception const& e) {
-		*err = strdup(e.what());
-		return nullptr;
-	}
+u32regex *regex_compile(const char *pattern, int flags, char **err)
+{
+    auto re = agi::make_unique<u32regex>();
+    try {
+        *re = boost::make_u32regex(pattern, boost::u32regex::perl | flags);
+        return re.release();
+    }
+    catch (std::exception const &e) {
+        *err = strdup(e.what());
+        return nullptr;
+    }
 }
 
 void regex_free(u32regex *re) { delete re; }
 void match_free(match *m) { delete m; }
 
-const agi_re_flag *get_regex_flags() {
-	static const agi_re_flag flags[] = {
-		{"ICASE", boost::u32regex::icase},
-		{"NOSUB", boost::u32regex::nosubs},
-		{"COLLATE", boost::u32regex::collate},
-		{"NEWLINE_ALT", boost::u32regex::newline_alt},
-		{"NO_MOD_M", boost::u32regex::no_mod_m},
-		{"NO_MOD_S", boost::u32regex::no_mod_s},
-		{"MOD_S", boost::u32regex::mod_s},
-		{"MOD_X", boost::u32regex::mod_x},
-		{"NO_EMPTY_SUBEXPRESSIONS", boost::u32regex::no_empty_expressions},
-		{nullptr, 0}
-	};
-	return flags;
+const agi_re_flag *get_regex_flags()
+{
+    static const agi_re_flag flags[] = {
+        {"ICASE", boost::u32regex::icase},
+        {"NOSUB", boost::u32regex::nosubs},
+        {"COLLATE", boost::u32regex::collate},
+        {"NEWLINE_ALT", boost::u32regex::newline_alt},
+        {"NO_MOD_M", boost::u32regex::no_mod_m},
+        {"NO_MOD_S", boost::u32regex::no_mod_s},
+        {"MOD_S", boost::u32regex::mod_s},
+        {"MOD_X", boost::u32regex::mod_x},
+        {"NO_EMPTY_SUBEXPRESSIONS", boost::u32regex::no_empty_expressions},
+        {nullptr, 0}
+    };
+    return flags;
 }
 }
 
-extern "C" int luaopen_re_impl(lua_State *L) {
-	agi::lua::register_lib_table(L, {"agi_re_match", "u32regex"},
-		"search", regex_search,
-		"match", regex_match,
-		"get_match", regex_get_match,
-		"replace", regex_replace,
-		"compile", regex_compile,
-		"get_flags", get_regex_flags,
-		"match_free", match_free,
-		"regex_free", regex_free);
-	return 1;
+extern "C" int luaopen_re_impl(lua_State *L)
+{
+    agi::lua::register_lib_table(L, {"agi_re_match", "u32regex"},
+                                 "search", regex_search,
+                                 "match", regex_match,
+                                 "get_match", regex_get_match,
+                                 "replace", regex_replace,
+                                 "compile", regex_compile,
+                                 "get_flags", get_regex_flags,
+                                 "match_free", match_free,
+                                 "regex_free", regex_free);
+    return 1;
 }
diff --git a/libaegisub/lua/modules/unicode.cpp b/libaegisub/lua/modules/unicode.cpp
index d9a6c2248bb0810858b0da4df65c1c9501fee499..beccc8897661616b2642d35918dc8880cad194a2 100644
--- a/libaegisub/lua/modules/unicode.cpp
+++ b/libaegisub/lua/modules/unicode.cpp
@@ -19,21 +19,24 @@
 #include <boost/locale/conversion.hpp>
 
 namespace {
-template<std::string (*func)(const char *, std::locale const&)>
-char *wrap(const char *str, char **err) {
-	try {
-		return agi::lua::strndup(func(str, std::locale()));
-	} catch (std::exception const& e) {
-		*err = strdup(e.what());
-		return nullptr;
-	}
+template<std::string (*func)(const char *, std::locale const &)>
+char *wrap(const char *str, char **err)
+{
+    try {
+        return agi::lua::strndup(func(str, std::locale()));
+    }
+    catch (std::exception const &e) {
+        *err = strdup(e.what());
+        return nullptr;
+    }
 }
 }
 
-extern "C" int luaopen_unicode_impl(lua_State *L) {
-	agi::lua::register_lib_table(L, {},
-		"to_upper_case", wrap<boost::locale::to_upper<char>>,
-		"to_lower_case", wrap<boost::locale::to_lower<char>>,
-		"to_fold_case", wrap<boost::locale::fold_case<char>>);
-	return 1;
+extern "C" int luaopen_unicode_impl(lua_State *L)
+{
+    agi::lua::register_lib_table(L, {},
+                                 "to_upper_case", wrap<boost::locale::to_upper<char>>,
+                                 "to_lower_case", wrap<boost::locale::to_lower<char>>,
+                                 "to_fold_case", wrap<boost::locale::fold_case<char>>);
+    return 1;
 }
diff --git a/libaegisub/lua/script_reader.cpp b/libaegisub/lua/script_reader.cpp
index 7730697fac1acddc99ee21cbb77dc92089f5a2bf..e5f508ca4a4570e987b8c4144f1d6f515eb454ee 100644
--- a/libaegisub/lua/script_reader.cpp
+++ b/libaegisub/lua/script_reader.cpp
@@ -24,134 +24,139 @@
 #include <boost/algorithm/string/replace.hpp>
 #include <lauxlib.h>
 
-namespace agi { namespace lua {
-	bool LoadFile(lua_State *L, agi::fs::path const& raw_filename) {
-		auto filename = raw_filename;
-		try {
-			filename = agi::fs::Canonicalize(raw_filename);
-		}
-		catch (agi::fs::FileSystemUnknownError const& e) {
-			LOG_E("auto4/lua") << "Error canonicalizing path: " << e.GetMessage();
-		}
-
-		agi::read_file_mapping file(filename);
-		auto buff = file.read();
-		size_t size = static_cast<size_t>(file.size());
-
-		// Discard the BOM if present
-		if (size >= 3 && buff[0] == -17 && buff[1] == -69 && buff[2] == -65) {
-			buff += 3;
-			size -= 3;
-		}
-
-		if (!agi::fs::HasExtension(filename, "moon"))
-			return luaL_loadbuffer(L, buff, size, filename.string().c_str()) == 0;
-
-		// We have a MoonScript file, so we need to load it with that
-		// It might be nice to have a dedicated lua state for compiling
-		// MoonScript to Lua
-		lua_getfield(L, LUA_REGISTRYINDEX, "moonscript");
-
-		// Save the text we'll be loading for the line number rewriting in the
-		// error handling
-		lua_pushlstring(L, buff, size);
-		lua_pushvalue(L, -1);
-		lua_setfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + filename.string()).c_str());
-
-		push_value(L, filename);
-		if (lua_pcall(L, 2, 2, 0))
-			return false; // Leaves error message on stack
-
-		// loadstring returns nil, error on error or a function on success
-		if (lua_isnil(L, 1)) {
-			lua_remove(L, 1);
-			return false;
-		}
-
-		lua_pop(L, 1); // Remove the extra nil for the stackchecker
-		return true;
-	}
-
-	static int module_loader(lua_State *L) {
-		int pretop = lua_gettop(L);
-		std::string module(check_string(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_getfield(L, -1, "path");
-		std::string package_paths(check_string(L, -1));
-		lua_pop(L, 2);
-
-		for (auto tok : agi::Split(package_paths, ';')) {
-			std::string filename;
-			boost::replace_all_copy(std::back_inserter(filename), tok, "?", module);
-
-			// If there's a .moon file at that path, load it instead of the
-			// .lua file
-			agi::fs::path path = filename;
-			if (agi::fs::HasExtension(path, "lua")) {
-				agi::fs::path moonpath = path;
-				moonpath.replace_extension("moon");
-				if (agi::fs::FileExists(moonpath))
-					path = moonpath;
-			}
-
-			if (!agi::fs::FileExists(path))
-				continue;
-
-			try {
-				if (!LoadFile(L, path))
-					return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), check_string(L, 1).c_str());
-				break;
-			}
-			catch (agi::fs::FileNotFound const&) {
-				// Not an error so swallow and continue on
-			}
-			catch (agi::fs::NotAFile const&) {
-				// Not an error so swallow and continue on
-			}
-			catch (agi::Exception const& e) {
-				return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), e.GetMessage().c_str());
-			}
-		}
-
-		return lua_gettop(L) - pretop;
-	}
-
-	bool Install(lua_State *L, std::vector<fs::path> const& include_path) {
-		// set the module load path to include_path
-		lua_getglobal(L, "package");
-		push_value(L, "path");
-
-		push_value(L, "");
-		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);
-		}
+namespace agi {
+namespace lua {
+bool LoadFile(lua_State *L, agi::fs::path const &raw_filename)
+{
+    auto filename = raw_filename;
+    try {
+        filename = agi::fs::Canonicalize(raw_filename);
+    }
+    catch (agi::fs::FileSystemUnknownError const &e) {
+        LOG_E("auto4/lua") << "Error canonicalizing path: " << e.GetMessage();
+    }
+
+    agi::read_file_mapping file(filename);
+    auto buff = file.read();
+    size_t size = static_cast<size_t>(file.size());
+
+    // Discard the BOM if present
+    if (size >= 3 && buff[0] == -17 && buff[1] == -69 && buff[2] == -65) {
+        buff += 3;
+        size -= 3;
+    }
+
+    if (!agi::fs::HasExtension(filename, "moon"))
+        return luaL_loadbuffer(L, buff, size, filename.string().c_str()) == 0;
+
+    // We have a MoonScript file, so we need to load it with that
+    // It might be nice to have a dedicated lua state for compiling
+    // MoonScript to Lua
+    lua_getfield(L, LUA_REGISTRYINDEX, "moonscript");
+
+    // Save the text we'll be loading for the line number rewriting in the
+    // error handling
+    lua_pushlstring(L, buff, size);
+    lua_pushvalue(L, -1);
+    lua_setfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + filename.string()).c_str());
+
+    push_value(L, filename);
+    if (lua_pcall(L, 2, 2, 0))
+        return false; // Leaves error message on stack
+
+    // loadstring returns nil, error on error or a function on success
+    if (lua_isnil(L, 1)) {
+        lua_remove(L, 1);
+        return false;
+    }
+
+    lua_pop(L, 1); // Remove the extra nil for the stackchecker
+    return true;
+}
+
+static int module_loader(lua_State *L)
+{
+    int pretop = lua_gettop(L);
+    std::string module(check_string(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_getfield(L, -1, "path");
+    std::string package_paths(check_string(L, -1));
+    lua_pop(L, 2);
+
+    for (auto tok : agi::Split(package_paths, ';')) {
+        std::string filename;
+        boost::replace_all_copy(std::back_inserter(filename), tok, "?", module);
+
+        // If there's a .moon file at that path, load it instead of the
+        // .lua file
+        agi::fs::path path = filename;
+        if (agi::fs::HasExtension(path, "lua")) {
+            agi::fs::path moonpath = path;
+            moonpath.replace_extension("moon");
+            if (agi::fs::FileExists(moonpath))
+                path = moonpath;
+        }
+
+        if (!agi::fs::FileExists(path))
+            continue;
+
+        try {
+            if (!LoadFile(L, path))
+                return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), check_string(L, 1).c_str());
+            break;
+        }
+        catch (agi::fs::FileNotFound const &) {
+            // Not an error so swallow and continue on
+        }
+        catch (agi::fs::NotAFile const &) {
+            // Not an error so swallow and continue on
+        }
+        catch (agi::Exception const &e) {
+            return error(L, "Error loading Lua module \"%s\":\n%s", path.string().c_str(), e.GetMessage().c_str());
+        }
+    }
+
+    return lua_gettop(L) - pretop;
+}
+
+bool Install(lua_State *L, std::vector<fs::path> const &include_path)
+{
+    // set the module load path to include_path
+    lua_getglobal(L, "package");
+    push_value(L, "path");
+
+    push_value(L, "");
+    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);
+    }
 
 #ifndef _WIN32
-		// No point in checking any of the default locations on Windows since
-		// there won't be anything there
-		push_value(L, "path");
-		lua_gettable(L, -4);
-		lua_concat(L, 2);
+    // No point in checking any of the default locations on Windows since
+    // there won't be anything there
+    push_value(L, "path");
+    lua_gettable(L, -4);
+    lua_concat(L, 2);
 #endif
 
-		lua_settable(L, -3);
+    lua_settable(L, -3);
 
-		// Replace the default lua module loader with our unicode compatible one
-		lua_getfield(L, -1, "loaders");
-		push_value(L, exception_wrapper<module_loader>);
-		lua_rawseti(L, -2, 2);
-		lua_pop(L, 2); // loaders, package
+    // Replace the default lua module loader with our unicode compatible one
+    lua_getfield(L, -1, "loaders");
+    push_value(L, exception_wrapper<module_loader>);
+    lua_rawseti(L, -2, 2);
+    lua_pop(L, 2); // loaders, package
 
-		luaL_loadstring(L, "return require('moonscript').loadstring");
-		if (lua_pcall(L, 0, 1, 0)) {
-			return false; // leave error message
-		}
-		lua_setfield(L, LUA_REGISTRYINDEX, "moonscript");
+    luaL_loadstring(L, "return require('moonscript').loadstring");
+    if (lua_pcall(L, 0, 1, 0)) {
+        return false; // leave error message
+    }
+    lua_setfield(L, LUA_REGISTRYINDEX, "moonscript");
 
-		return true;
-	}
-} }
+    return true;
+}
+}
+}
diff --git a/libaegisub/lua/utils.cpp b/libaegisub/lua/utils.cpp
index 2cde75f4631d1d5f66f4b10abe1df4390c908f6d..b3dddecaeb91f3a3d6ee2d2ecf7838287c2bd8a5 100644
--- a/libaegisub/lua/utils.cpp
+++ b/libaegisub/lua/utils.cpp
@@ -29,234 +29,251 @@
 #pragma warning(disable: 4645 4646)
 #endif
 
-namespace agi { namespace lua {
-std::string get_string_or_default(lua_State *L, int idx) {
-	size_t len = 0;
-	const char *str = lua_tolstring(L, idx, &len);
-	if (!str)
-		return "<not a string>";
-	return std::string(str, len);
+namespace agi {
+namespace lua {
+std::string get_string_or_default(lua_State *L, int idx)
+{
+    size_t len = 0;
+    const char *str = lua_tolstring(L, idx, &len);
+    if (!str)
+        return "<not a string>";
+    return std::string(str, len);
 }
 
-std::string get_string(lua_State *L, int idx) {
-	size_t len = 0;
-	const char *str = lua_tolstring(L, idx, &len);
-	return std::string(str ? str : "", len);
+std::string get_string(lua_State *L, int idx)
+{
+    size_t len = 0;
+    const char *str = lua_tolstring(L, idx, &len);
+    return std::string(str ? str : "", len);
 }
 
-std::string get_global_string(lua_State *L, const char *name) {
-	lua_getglobal(L, name);
-	std::string ret;
-	if (lua_isstring(L, -1))
-		ret = lua_tostring(L, -1);
-	lua_pop(L, 1);
-	return ret;
+std::string get_global_string(lua_State *L, const char *name)
+{
+    lua_getglobal(L, name);
+    std::string ret;
+    if (lua_isstring(L, -1))
+        ret = lua_tostring(L, -1);
+    lua_pop(L, 1);
+    return ret;
 }
 
-std::string check_string(lua_State *L, int idx) {
-	size_t len = 0;
-	const char *str = lua_tolstring(L, idx, &len);
-	if (!str) typerror(L, idx, "string");
-	return std::string(str, len);
+std::string check_string(lua_State *L, int idx)
+{
+    size_t len = 0;
+    const char *str = lua_tolstring(L, idx, &len);
+    if (!str) typerror(L, idx, "string");
+    return std::string(str, len);
 }
 
-int check_int(lua_State *L, int idx) {
-	auto v = lua_tointeger(L, idx);
-	if (v == 0 && !lua_isnumber(L, idx))
-		typerror(L, idx, "number");
-	return v;
+int check_int(lua_State *L, int idx)
+{
+    auto v = lua_tointeger(L, idx);
+    if (v == 0 && !lua_isnumber(L, idx))
+        typerror(L, idx, "number");
+    return v;
 }
 
-size_t check_uint(lua_State *L, int idx) {
-	auto v = lua_tointeger(L, idx);
-	if (v == 0 && !lua_isnumber(L, idx))
-		typerror(L, idx, "number");
-	if (v < 0)
-		argerror(L, idx, "must be >= 0");
-	return static_cast<size_t>(v);
+size_t check_uint(lua_State *L, int idx)
+{
+    auto v = lua_tointeger(L, idx);
+    if (v == 0 && !lua_isnumber(L, idx))
+        typerror(L, idx, "number");
+    if (v < 0)
+        argerror(L, idx, "must be >= 0");
+    return static_cast<size_t>(v);
 }
 
-void *check_udata(lua_State *L, int idx, const char *mt) {
-	void *p = lua_touserdata(L, idx);
-	if (!p) typerror(L, idx, mt);
-	if (!lua_getmetatable(L, idx)) typerror(L, idx, mt);
+void *check_udata(lua_State *L, int idx, const char *mt)
+{
+    void *p = lua_touserdata(L, idx);
+    if (!p) typerror(L, idx, mt);
+    if (!lua_getmetatable(L, idx)) typerror(L, idx, mt);
 
-	lua_getfield(L, LUA_REGISTRYINDEX, mt);
-	if (!lua_rawequal(L, -1, -2)) typerror(L, idx, mt);
+    lua_getfield(L, LUA_REGISTRYINDEX, mt);
+    if (!lua_rawequal(L, -1, -2)) typerror(L, idx, mt);
 
-	lua_pop(L, 2);
-	return p;
+    lua_pop(L, 2);
+    return p;
 }
 
-static int moon_line(lua_State *L, int lua_line, std::string const& file) {
-	if (luaL_dostring(L, "return require 'moonscript.line_tables'")) {
-		lua_pop(L, 1); // pop error message
-		return lua_line;
-	}
-
-	push_value(L, file);
-	lua_rawget(L, -2);
-
-	if (!lua_istable(L, -1)) {
-		lua_pop(L, 2);
-		return lua_line;
-	}
-
-	lua_rawgeti(L, -1, lua_line);
-	if (!lua_isnumber(L, -1)) {
-		lua_pop(L, 3);
-		return lua_line;
-	}
-
-	auto char_pos = static_cast<size_t>(lua_tonumber(L, -1));
-	lua_pop(L, 3);
-
-	// The moonscript line tables give us a character offset into the file,
-	// so now we need to map that to a line number
-	lua_getfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + file).c_str());
-	if (!lua_isstring(L, -1)) {
-		lua_pop(L, 1);
-		return lua_line;
-	}
-
-	size_t moon_len;
-	auto moon = lua_tolstring(L, -1, &moon_len);
-	return std::count(moon, moon + std::min(moon_len, char_pos), '\n') + 1;
+static int moon_line(lua_State *L, int lua_line, std::string const &file)
+{
+    if (luaL_dostring(L, "return require 'moonscript.line_tables'")) {
+        lua_pop(L, 1); // pop error message
+        return lua_line;
+    }
+
+    push_value(L, file);
+    lua_rawget(L, -2);
+
+    if (!lua_istable(L, -1)) {
+        lua_pop(L, 2);
+        return lua_line;
+    }
+
+    lua_rawgeti(L, -1, lua_line);
+    if (!lua_isnumber(L, -1)) {
+        lua_pop(L, 3);
+        return lua_line;
+    }
+
+    auto char_pos = static_cast<size_t>(lua_tonumber(L, -1));
+    lua_pop(L, 3);
+
+    // The moonscript line tables give us a character offset into the file,
+    // so now we need to map that to a line number
+    lua_getfield(L, LUA_REGISTRYINDEX, ("raw moonscript: " + file).c_str());
+    if (!lua_isstring(L, -1)) {
+        lua_pop(L, 1);
+        return lua_line;
+    }
+
+    size_t moon_len;
+    auto moon = lua_tolstring(L, -1, &moon_len);
+    return std::count(moon, moon + std::min(moon_len, char_pos), '\n') + 1;
 }
 
-int add_stack_trace(lua_State *L) {
-	int level = 1;
-	if (lua_isnumber(L, 2)) {
-		level = (int)lua_tointeger(L, 2);
-		lua_pop(L, 1);
-	}
-
-	const char *err = lua_tostring(L, 1);
-	if (!err) return 1;
-
-	std::string message = err;
-	if (lua_gettop(L))
-		lua_pop(L, 1);
-
-	// Strip the location from the error message since it's redundant with
-	// the stack trace
-	boost::regex location(R"(^\[string ".*"\]:[0-9]+: )");
-	message = regex_replace(message, location, "", boost::format_first_only);
-
-	std::vector<std::string> frames;
-	frames.emplace_back(std::move(message));
-
-	lua_Debug ar;
-	while (lua_getstack(L, level++, &ar)) {
-		lua_getinfo(L, "Snl", &ar);
-
-		if (ar.what[0] == 't')
-			frames.emplace_back("(tail call)");
-		else {
-			bool is_moon = false;
-			std::string file = ar.source;
-			if (file == "=[C]")
-				file = "<C function>";
-			else if (boost::ends_with(file, ".moon"))
-				is_moon = true;
-
-			auto real_line = [&](int line) {
-				return is_moon ? moon_line(L, line, file) : line;
-			};
-
-			std::string function = ar.name ? ar.name : "";
-			if (*ar.what == 'm')
-				function = "<main>";
-			else if (*ar.what == 'C')
-				function = '?';
-			else if (!*ar.namewhat)
-				function = agi::format("<anonymous function at lines %d-%d>", real_line(ar.linedefined), real_line(ar.lastlinedefined - 1));
-
-			frames.emplace_back(agi::format("    File \"%s\", line %d\n%s", file, real_line(ar.currentline), function));
-		}
-	}
-
-	push_value(L, join(frames | boost::adaptors::reversed, "\n"));
-
-	return 1;
+int add_stack_trace(lua_State *L)
+{
+    int level = 1;
+    if (lua_isnumber(L, 2)) {
+        level = (int)lua_tointeger(L, 2);
+        lua_pop(L, 1);
+    }
+
+    const char *err = lua_tostring(L, 1);
+    if (!err) return 1;
+
+    std::string message = err;
+    if (lua_gettop(L))
+        lua_pop(L, 1);
+
+    // Strip the location from the error message since it's redundant with
+    // the stack trace
+    boost::regex location(R"(^\[string ".*"\]:[0-9]+: )");
+    message = regex_replace(message, location, "", boost::format_first_only);
+
+    std::vector<std::string> frames;
+    frames.emplace_back(std::move(message));
+
+    lua_Debug ar;
+    while (lua_getstack(L, level++, &ar)) {
+        lua_getinfo(L, "Snl", &ar);
+
+        if (ar.what[0] == 't')
+            frames.emplace_back("(tail call)");
+        else {
+            bool is_moon = false;
+            std::string file = ar.source;
+            if (file == "=[C]")
+                file = "<C function>";
+            else if (boost::ends_with(file, ".moon"))
+                is_moon = true;
+
+            auto real_line = [&](int line) {
+                return is_moon ? moon_line(L, line, file) : line;
+            };
+
+            std::string function = ar.name ? ar.name : "";
+            if (*ar.what == 'm')
+                function = "<main>";
+            else if (*ar.what == 'C')
+                function = '?';
+            else if (!*ar.namewhat)
+                function = agi::format("<anonymous function at lines %d-%d>", real_line(ar.linedefined), real_line(ar.lastlinedefined - 1));
+
+            frames.emplace_back(agi::format("    File \"%s\", line %d\n%s", file, real_line(ar.currentline), function));
+        }
+    }
+
+    push_value(L, join(frames | boost::adaptors::reversed, "\n"));
+
+    return 1;
 }
 
-int BOOST_NORETURN error(lua_State *L, const char *fmt, ...) {
-	va_list argp;
-	va_start(argp, fmt);
-	luaL_where(L, 1);
-	lua_pushvfstring(L, fmt, argp);
-	va_end(argp);
-	lua_concat(L, 2);
-	throw error_tag();
+int BOOST_NORETURN error(lua_State *L, const char *fmt, ...)
+{
+    va_list argp;
+    va_start(argp, fmt);
+    luaL_where(L, 1);
+    lua_pushvfstring(L, fmt, argp);
+    va_end(argp);
+    lua_concat(L, 2);
+    throw error_tag();
 }
 
-int BOOST_NORETURN argerror(lua_State *L, int narg, const char *extramsg) {
-	lua_Debug ar;
-	if (!lua_getstack(L, 0, &ar))
-		error(L, "bad argument #%d (%s)", narg, extramsg);
-	lua_getinfo(L, "n", &ar);
-	if (strcmp(ar.namewhat, "method") == 0 && --narg == 0)
-		error(L, "calling '%s' on bad self (%s)", ar.name, extramsg);
-	if (!ar.name) ar.name = "?";
-	error(L, "bad argument #%d to '%s' (%s)",
-		narg, ar.name, extramsg);
+int BOOST_NORETURN argerror(lua_State *L, int narg, const char *extramsg)
+{
+    lua_Debug ar;
+    if (!lua_getstack(L, 0, &ar))
+        error(L, "bad argument #%d (%s)", narg, extramsg);
+    lua_getinfo(L, "n", &ar);
+    if (strcmp(ar.namewhat, "method") == 0 && --narg == 0)
+        error(L, "calling '%s' on bad self (%s)", ar.name, extramsg);
+    if (!ar.name) ar.name = "?";
+    error(L, "bad argument #%d to '%s' (%s)",
+          narg, ar.name, extramsg);
 }
 
-int BOOST_NORETURN typerror(lua_State *L, int narg, const char *tname) {
-	const char *msg = lua_pushfstring(L, "%s expected, got %s",
-		tname, luaL_typename(L, narg));
-	argerror(L, narg, msg);
+int BOOST_NORETURN typerror(lua_State *L, int narg, const char *tname)
+{
+    const char *msg = lua_pushfstring(L, "%s expected, got %s",
+                                      tname, luaL_typename(L, narg));
+    argerror(L, narg, msg);
 }
 
-void argcheck(lua_State *L, bool cond, int narg, const char *msg) {
-	if (!cond) argerror(L, narg, msg);
+void argcheck(lua_State *L, bool cond, int narg, const char *msg)
+{
+    if (!cond) argerror(L, narg, msg);
 }
 
-int exception_wrapper(lua_State *L, int (*func)(lua_State *L)) {
-	try {
-		return func(L);
-	}
-	catch (agi::Exception const& e) {
-		push_value(L, e.GetMessage());
-		return lua_error(L);
-	}
-	catch (std::exception const& e) {
-		push_value(L, e.what());
-		return lua_error(L);
-	}
-	catch (error_tag) {
-		// Error message is already on the stack
-		return lua_error(L);
-	}
-	catch (...) {
-		std::terminate();
-	}
+int exception_wrapper(lua_State *L, int (*func)(lua_State *L))
+{
+    try {
+        return func(L);
+    }
+    catch (agi::Exception const &e) {
+        push_value(L, e.GetMessage());
+        return lua_error(L);
+    }
+    catch (std::exception const &e) {
+        push_value(L, e.what());
+        return lua_error(L);
+    }
+    catch (error_tag) {
+        // Error message is already on the stack
+        return lua_error(L);
+    }
+    catch (...) {
+        std::terminate();
+    }
 }
 
 #ifdef _DEBUG
-void LuaStackcheck::check_stack(int additional) {
-	int top = lua_gettop(L);
-	if (top - additional != startstack) {
-		LOG_D("automation/lua") << "lua stack size mismatch.";
-		dump();
-		assert(top - additional == startstack);
-	}
+void LuaStackcheck::check_stack(int additional)
+{
+    int top = lua_gettop(L);
+    if (top - additional != startstack) {
+        LOG_D("automation/lua") << "lua stack size mismatch.";
+        dump();
+        assert(top - additional == startstack);
+    }
 }
 
-void LuaStackcheck::dump() {
-	int top = lua_gettop(L);
-	LOG_D("automation/lua/stackdump") << "--- dumping lua stack...";
-	for (int i = top; i > 0; i--) {
-		lua_pushvalue(L, i);
-		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
-			LOG_D("automation/lua/stackdump") << type;
-		lua_pop(L, 1);
-	}
-	LOG_D("automation/lua") << "--- end dump";
+void LuaStackcheck::dump()
+{
+    int top = lua_gettop(L);
+    LOG_D("automation/lua/stackdump") << "--- dumping lua stack...";
+    for (int i = top; i > 0; i--) {
+        lua_pushvalue(L, i);
+        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
+            LOG_D("automation/lua/stackdump") << type;
+        lua_pop(L, 1);
+    }
+    LOG_D("automation/lua") << "--- end dump";
 }
 #endif
 
diff --git a/libaegisub/unix/access.cpp b/libaegisub/unix/access.cpp
index 73da19d224b9fa159afaad4dfeee231e1790ded1..faf8a1c6a81749fd089a6e7e0e01e818cd0b3572 100644
--- a/libaegisub/unix/access.cpp
+++ b/libaegisub/unix/access.cpp
@@ -27,48 +27,49 @@
 #include <boost/filesystem/path.hpp>
 
 namespace agi {
-	namespace acs {
+namespace acs {
 
-void Check(agi::fs::path const& file, acs::Type type) {
-	struct stat file_stat;
+void Check(agi::fs::path const &file, acs::Type type)
+{
+    struct stat file_stat;
 
-	int 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 fs::FileNotFound(file);
-			case EACCES:
-				throw fs::ReadDenied(file);
-			case EIO:
-				throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string());
-		}
-	}
+    if (file_status != 0) {
+        switch (errno) {
+        case ENOENT:
+            throw fs::FileNotFound(file);
+        case EACCES:
+            throw fs::ReadDenied(file);
+        case EIO:
+            throw fs::FileSystemUnknownError("Fatal I/O error in 'stat' on path: " + file.string());
+        }
+    }
 
-	switch (type) {
-		case FileRead:
-		case FileWrite:
-			if ((file_stat.st_mode & S_IFREG) == 0)
-				throw fs::NotAFile(file);
-		break;
+    switch (type) {
+    case FileRead:
+    case FileWrite:
+        if ((file_stat.st_mode & S_IFREG) == 0)
+            throw fs::NotAFile(file);
+        break;
 
-		case DirRead:
-		case DirWrite:
-			if ((file_stat.st_mode & S_IFDIR) == 0)
-				throw fs::NotADirectory(file);
-		break;
-	}
+    case DirRead:
+    case DirWrite:
+        if ((file_stat.st_mode & S_IFDIR) == 0)
+            throw fs::NotADirectory(file);
+        break;
+    }
 
-	file_status = access(file.c_str(), R_OK);
-	if (file_status != 0)
-		throw fs::ReadDenied(file);
+    file_status = access(file.c_str(), R_OK);
+    if (file_status != 0)
+        throw fs::ReadDenied(file);
 
-	if (type == DirWrite || type == FileWrite) {
-		file_status = access(file.c_str(), W_OK);
-		if (file_status != 0)
-			throw fs::WriteDenied(file);
-	}
+    if (type == DirWrite || type == FileWrite) {
+        file_status = access(file.c_str(), W_OK);
+        if (file_status != 0)
+            throw fs::WriteDenied(file);
+    }
 }
 
-	} // namespace acs
+} // namespace acs
 } // namespace agi
diff --git a/libaegisub/unix/fs.cpp b/libaegisub/unix/fs.cpp
index 8f6ead6a896b2ac3aad7fb72e09e0e99d19597e5..7e4d0f56280614a4fc018f87d86f2203a2ecfd24 100644
--- a/libaegisub/unix/fs.cpp
+++ b/libaegisub/unix/fs.cpp
@@ -27,77 +27,84 @@
 
 namespace bfs = boost::filesystem;
 
-namespace agi { namespace fs {
-std::string ShortName(path const& p) {
-	return p.string();
+namespace agi {
+namespace fs {
+std::string ShortName(path const &p)
+{
+    return p.string();
 }
 
-void Touch(path const& file) {
-	CreateDirectory(file.parent_path());
+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);
-	}
+    int fd = open(file.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0644);
+    if (fd >= 0) {
+        futimes(fd, nullptr);
+        close(fd);
+    }
 }
 
-void Copy(fs::path const& from, fs::path const& to) {
-	acs::CheckFileRead(from);
-	CreateDirectory(to.parent_path());
-	acs::CheckDirWrite(to.parent_path());
+void Copy(fs::path const &from, fs::path const &to)
+{
+    acs::CheckFileRead(from);
+    CreateDirectory(to.parent_path());
+    acs::CheckDirWrite(to.parent_path());
 
-	auto in = io::Open(from, true);
-	io::Save(to).Get() << in->rdbuf();
+    auto in = io::Open(from, true);
+    io::Save(to).Get() << in->rdbuf();
 }
 
 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));
-	}
+    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))
+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();
+    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();
+bool DirectoryIterator::operator==(DirectoryIterator const &rhs) const
+{
+    return privdata.get() == rhs.privdata.get();
 }
 
-DirectoryIterator& DirectoryIterator::operator++() {
-	if (!privdata) return *this;
+DirectoryIterator &DirectoryIterator::operator++()
+{
+    if (!privdata) return *this;
 
-	++privdata->it;
+    ++privdata->it;
 
-	while (privdata->bad()) {
-		if (privdata->it == bfs::directory_iterator()) {
-			privdata.reset();
-			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();
+    value = privdata->it->path().filename().string();
 
-	return *this;
+    return *this;
 }
 
 DirectoryIterator::~DirectoryIterator() { }
 
-} }
+}
+}
diff --git a/libaegisub/unix/log.cpp b/libaegisub/unix/log.cpp
index 82109dd2b64e48ed913d5d03deb79f511bc75d4f..24ccb9fd7796f14c9e086226b6dc8160375d8e3a 100644
--- a/libaegisub/unix/log.cpp
+++ b/libaegisub/unix/log.cpp
@@ -18,26 +18,29 @@
 #include <ctime>
 #include <unistd.h>
 
-namespace agi { namespace log {
-void EmitSTDOUT::log(SinkMessage const& sm) {
-	time_t time = sm.time / 1000000000;
-	tm tmtime;
-	localtime_r(&time, &tmtime);
+namespace agi {
+namespace log {
+void EmitSTDOUT::log(SinkMessage const &sm)
+{
+    time_t time = sm.time / 1000000000;
+    tm tmtime;
+    localtime_r(&time, &tmtime);
 
-	printf("%c %02d:%02d:%02d %-9ld <%-25s> [%s:%s:%d]  %.*s\n",
-		Severity_ID[sm.severity],
-		tmtime.tm_hour,
-		tmtime.tm_min,
-		tmtime.tm_sec,
-		(long)(sm.time % 1000000000),
-		sm.section,
-		sm.file,
-		sm.func,
-		sm.line,
-		(int)sm.message.size(),
-		sm.message.c_str());
+    printf("%c %02d:%02d:%02d %-9ld <%-25s> [%s:%s:%d]  %.*s\n",
+           Severity_ID[sm.severity],
+           tmtime.tm_hour,
+           tmtime.tm_min,
+           tmtime.tm_sec,
+           (long)(sm.time % 1000000000),
+           sm.section,
+           sm.file,
+           sm.func,
+           sm.line,
+           (int)sm.message.size(),
+           sm.message.c_str());
 
-	if (!isatty(fileno(stdout)))
-		fflush(stdout);
+    if (!isatty(fileno(stdout)))
+        fflush(stdout);
+}
+}
 }
-} }
diff --git a/libaegisub/unix/path.cpp b/libaegisub/unix/path.cpp
index 0541e0dbe416f30d7b4cb07677001856165f0ba2..dc080528c82c5f8e36b29f8892caf9f701b92bab 100644
--- a/libaegisub/unix/path.cpp
+++ b/libaegisub/unix/path.cpp
@@ -24,36 +24,38 @@
 
 namespace {
 #ifndef __APPLE__
-std::string home_dir() {
-	const char *env = getenv("HOME");
-	if (env) return env;
+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;
-	}
+    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.");
+    throw agi::EnvironmentError("Could not get home directory. Make sure HOME is set.");
 }
 #endif
 }
 
 namespace agi {
-void Path::FillPlatformSpecificPaths() {
+void Path::FillPlatformSpecificPaths()
+{
 #ifndef __APPLE__
-	agi::fs::path home = home_dir();
-	SetToken("?user", home/".aegisub");
-	SetToken("?local", home/".aegisub");
-	SetToken("?data", P_DATA);
-	SetToken("?dictionary", "/usr/share/hunspell");
+    agi::fs::path home = home_dir();
+    SetToken("?user", home / ".aegisub");
+    SetToken("?local", home / ".aegisub");
+    SetToken("?data", P_DATA);
+    SetToken("?dictionary", "/usr/share/hunspell");
 #else
-	agi::fs::path app_support = agi::util::GetApplicationSupportDirectory();
-	SetToken("?user", app_support/"Aegisub");
-	SetToken("?local", app_support/"Aegisub");
-	SetToken("?data", agi::util::GetBundleSharedSupportDirectory());
-	SetToken("?dictionary", agi::util::GetBundleSharedSupportDirectory() + "/dictionaries");
+    agi::fs::path app_support = agi::util::GetApplicationSupportDirectory();
+    SetToken("?user", app_support / "Aegisub");
+    SetToken("?local", app_support / "Aegisub");
+    SetToken("?data", agi::util::GetBundleSharedSupportDirectory());
+    SetToken("?dictionary", agi::util::GetBundleSharedSupportDirectory() + "/dictionaries");
 #endif
-	SetToken("?temp", boost::filesystem::temp_directory_path());
+    SetToken("?temp", boost::filesystem::temp_directory_path());
 }
 
 }
diff --git a/libaegisub/unix/util.cpp b/libaegisub/unix/util.cpp
index 7a88bfecbcafc4c13cce0723d679c02be8f61f7d..638107a498189c2d4e8627556326949dc30d504a 100644
--- a/libaegisub/unix/util.cpp
+++ b/libaegisub/unix/util.cpp
@@ -22,11 +22,14 @@
 #include <boost/thread.hpp>
 #endif
 
-namespace agi { namespace util {
+namespace agi {
+namespace util {
 void SetThreadName(const char *) { }
 
-void sleep_for(int ms) {
-	boost::this_thread::sleep_for(boost::chrono::milliseconds(ms));
+void sleep_for(int ms)
+{
+    boost::this_thread::sleep_for(boost::chrono::milliseconds(ms));
 }
 
-} }
+}
+}
diff --git a/libaegisub/windows/access.cpp b/libaegisub/windows/access.cpp
index d817ef553ca890be8cdab2b8cee64a627589eeae..881f5bb3f88e5a7d26666724bb8e2875797fd73d 100644
--- a/libaegisub/windows/access.cpp
+++ b/libaegisub/windows/access.cpp
@@ -29,24 +29,25 @@
 #include <windows.h>
 
 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;
-
-		GENERIC_MAPPING generic_mapping;
-		MapGenericMask(&access_check, &generic_mapping);
-
-		PRIVILEGE_SET priv_set;
-		DWORD priv_set_size = sizeof(PRIVILEGE_SET);
-		DWORD access;
-		BOOL access_ok;
-		if(!AccessCheck(sd, client_token, access_check, &generic_mapping, &priv_set, &priv_set_size, &access, &access_ok))
-			LOG_W("acs/check") << "AccessCheck failed: " << agi::util::ErrorString(GetLastError());
-		return !!access;
-	}
+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;
+
+    GENERIC_MAPPING generic_mapping;
+    MapGenericMask(&access_check, &generic_mapping);
+
+    PRIVILEGE_SET priv_set;
+    DWORD priv_set_size = sizeof(PRIVILEGE_SET);
+    DWORD access;
+    BOOL access_ok;
+    if (!AccessCheck(sd, client_token, access_check, &generic_mapping, &priv_set, &priv_set_size, &access, &access_ok))
+        LOG_W("acs/check") << "AccessCheck failed: " << agi::util::ErrorString(GetLastError());
+    return !!access;
+}
 }
 
 namespace agi {
-	namespace acs {
+namespace acs {
 
 /*
 This function is still a proof of concept, it's probably rife with bugs, below
@@ -54,55 +55,56 @@ 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(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 fs::FileNotFound(file);
-			case ERROR_ACCESS_DENIED:
-				throw fs::ReadDenied(file);
-			default:
-				throw fs::FileSystemUnknownError(agi::format("Unexpected error when getting attributes for \"%s\": %s", file, util::ErrorString(GetLastError())));
-		}
-	}
-
-	switch (type) {
-		case FileRead:
-		case FileWrite:
-			if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
-				throw fs::NotAFile(file);
-			break;
-		case DirRead:
-		case DirWrite:
-			if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
-				throw fs::NotADirectory(file);
-			break;
-	}
-
-	SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
-	DWORD len = 0;
-	GetFileSecurity(file.c_str(), info, nullptr, 0, &len);
-	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
-		LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError());
-
-	std::vector<uint8_t> sd_buff(len);
-	SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0];
-
-	if (!GetFileSecurity(file.c_str(), info, sd, len, &len))
-		LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError());
-
-	ImpersonateSelf(SecurityImpersonation);
-	HANDLE client_token;
-	if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &client_token))
-		LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError());
-
-	if (!check_permission(true, sd, client_token))
-		throw fs::ReadDenied(file);
-	if ((type == DirWrite || type == FileWrite) && !check_permission(false, sd, client_token))
-		throw fs::WriteDenied(file);
+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 fs::FileNotFound(file);
+        case ERROR_ACCESS_DENIED:
+            throw fs::ReadDenied(file);
+        default:
+            throw fs::FileSystemUnknownError(agi::format("Unexpected error when getting attributes for \"%s\": %s", file, util::ErrorString(GetLastError())));
+        }
+    }
+
+    switch (type) {
+    case FileRead:
+    case FileWrite:
+        if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
+            throw fs::NotAFile(file);
+        break;
+    case DirRead:
+    case DirWrite:
+        if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
+            throw fs::NotADirectory(file);
+        break;
+    }
+
+    SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+    DWORD len = 0;
+    GetFileSecurity(file.c_str(), info, nullptr, 0, &len);
+    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        LOG_W("acs/check") << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError());
+
+    std::vector<uint8_t> sd_buff(len);
+    SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *)&sd_buff[0];
+
+    if (!GetFileSecurity(file.c_str(), info, sd, len, &len))
+        LOG_W("acs/check") << "GetFileSecurity failed: " << util::ErrorString(GetLastError());
+
+    ImpersonateSelf(SecurityImpersonation);
+    HANDLE client_token;
+    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &client_token))
+        LOG_W("acs/check") << "OpenThreadToken failed: " << util::ErrorString(GetLastError());
+
+    if (!check_permission(true, sd, client_token))
+        throw fs::ReadDenied(file);
+    if ((type == DirWrite || type == FileWrite) && !check_permission(false, sd, client_token))
+        throw fs::WriteDenied(file);
 }
 
-	} // namespace Access
+} // namespace Access
 } // namespace agi
diff --git a/libaegisub/windows/charset_conv_win.cpp b/libaegisub/windows/charset_conv_win.cpp
index 67385001a2501cde05c0d0a67c17f58e5e152289..0f787a20719a2b1162be6d55e50ace113d3846fc 100644
--- a/libaegisub/windows/charset_conv_win.cpp
+++ b/libaegisub/windows/charset_conv_win.cpp
@@ -19,39 +19,43 @@
 #include <libaegisub/charset_conv_win.h>
 
 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<const char *>(source.c_str());
-	size_t len = w32Conv.RequiredBufferSize(src, srcLen);
-	dest.resize(len);
-	w32Conv.Convert(src, srcLen, &dest[0], len);
-	return dest;
+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<const char *>(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 {
+namespace charset {
 
-std::wstring ConvertW(std::string const& source) {
-	static IconvWrapper w32Conv("utf-8", "utf-16le", false);
+std::wstring ConvertW(std::string const &source)
+{
+    static IconvWrapper w32Conv("utf-8", "utf-16le", false);
 
-	std::wstring dest;
-	size_t len = w32Conv.RequiredBufferSize(source);
-	dest.resize(len / sizeof(wchar_t));
-	w32Conv.Convert(source.data(), source.size(), reinterpret_cast<char *>(&dest[0]), len);
-	return dest;
+    std::wstring dest;
+    size_t len = w32Conv.RequiredBufferSize(source);
+    dest.resize(len / sizeof(wchar_t));
+    w32Conv.Convert(source.data(), source.size(), reinterpret_cast<char *>(&dest[0]), len);
+    return dest;
 }
 
-std::string ConvertW(std::wstring const& source) {
-	static IconvWrapper w32Conv("utf-16le", "utf-8", false);
-	return from_w(w32Conv, source);
+std::string ConvertW(std::wstring const &source)
+{
+    static IconvWrapper w32Conv("utf-16le", "utf-8", false);
+    return from_w(w32Conv, source);
 }
 
-std::string ConvertLocal(std::wstring const& source) {
-	static IconvWrapper w32Conv("utf-16le", "char", false);
-	return from_w(w32Conv, source);
+std::string ConvertLocal(std::wstring const &source)
+{
+    static IconvWrapper w32Conv("utf-16le", "char", false);
+    return from_w(w32Conv, source);
 }
 
-	}
+}
 }
diff --git a/libaegisub/windows/fs.cpp b/libaegisub/windows/fs.cpp
index 8102c6ee36b7fec56ab87bb84ba0c3fcb877c097..c2c681ce92cca379d9f960662bce4c7ba92a2ef3 100644
--- a/libaegisub/windows/fs.cpp
+++ b/libaegisub/windows/fs.cpp
@@ -33,84 +33,91 @@ 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);
+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<HANDLE, BOOL (__stdcall *)(HANDLE)>
-		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()));
+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<HANDLE, BOOL (__stdcall *)(HANDLE)>
+    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()));
 }
 
-void Copy(fs::path const& from, fs::path const& to) {
-	acs::CheckFileRead(from);
-	CreateDirectory(to.parent_path());
-	acs::CheckDirWrite(to.parent_path());
-
-	if (!CopyFile(from.wstring().c_str(), to.wstring().c_str(), false)) {
-		switch (GetLastError()) {
-		case ERROR_FILE_NOT_FOUND:
-			throw FileNotFound(from);
-		case ERROR_ACCESS_DENIED:
-			throw fs::WriteDenied("Could not overwrite " + to.string());
-		default:
-			throw fs::WriteDenied("Could not copy: " + util::ErrorString(GetLastError()));
-		}
-	}
+void Copy(fs::path const &from, fs::path const &to)
+{
+    acs::CheckFileRead(from);
+    CreateDirectory(to.parent_path());
+    acs::CheckDirWrite(to.parent_path());
+
+    if (!CopyFile(from.wstring().c_str(), to.wstring().c_str(), false)) {
+        switch (GetLastError()) {
+        case ERROR_FILE_NOT_FOUND:
+            throw FileNotFound(from);
+        case ERROR_ACCESS_DENIED:
+            throw fs::WriteDenied("Could not overwrite " + to.string());
+        default:
+            throw fs::WriteDenied("Could not copy: " + util::ErrorString(GetLastError()));
+        }
+    }
 }
 
 struct DirectoryIterator::PrivData {
-	scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)> h{INVALID_HANDLE_VALUE, FindClose};
+    scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)> h{INVALID_HANDLE_VALUE, FindClose};
 };
 
 DirectoryIterator::DirectoryIterator() { }
-DirectoryIterator::DirectoryIterator(path const& p, std::string const& filter)
-: privdata(new PrivData)
+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;
+    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();
+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::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/libaegisub/windows/log_win.cpp b/libaegisub/windows/log_win.cpp
index 91017a8f52ec280c3ff091f6efb61282e3b7ac5c..8b761c0efe22e8bc03db9d31c5cf87d7ed16f9d5 100644
--- a/libaegisub/windows/log_win.cpp
+++ b/libaegisub/windows/log_win.cpp
@@ -20,25 +20,28 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
-namespace agi { namespace log {
-void EmitSTDOUT::log(SinkMessage const& sm) {
-	tm tmtime;
-	time_t time = sm.time / 1000000000;
-	localtime_s(&tmtime, &time);
+namespace agi {
+namespace log {
+void EmitSTDOUT::log(SinkMessage const &sm)
+{
+    tm tmtime;
+    time_t time = sm.time / 1000000000;
+    localtime_s(&tmtime, &time);
 
-	char buff[65536];
-	_snprintf_s(buff, _TRUNCATE, "%s (%d): %c %02d:%02d:%02d.%-3ld <%-25s> [%s]  %.*s\n",
-		sm.file,
-		sm.line,
-		Severity_ID[sm.severity],
-		tmtime.tm_hour,
-		tmtime.tm_min,
-		tmtime.tm_sec,
-		(long)(sm.time % 1000000000 / 1000000),
-		sm.section,
-		sm.func,
-		(int)sm.message.size(),
-		sm.message.c_str());
-	OutputDebugStringW(charset::ConvertW(buff).c_str());
+    char buff[65536];
+    _snprintf_s(buff, _TRUNCATE, "%s (%d): %c %02d:%02d:%02d.%-3ld <%-25s> [%s]  %.*s\n",
+                sm.file,
+                sm.line,
+                Severity_ID[sm.severity],
+                tmtime.tm_hour,
+                tmtime.tm_min,
+                tmtime.tm_sec,
+                (long)(sm.time % 1000000000 / 1000000),
+                sm.section,
+                sm.func,
+                (int)sm.message.size(),
+                sm.message.c_str());
+    OutputDebugStringW(charset::ConvertW(buff).c_str());
+}
+}
 }
-} }
diff --git a/libaegisub/windows/path_win.cpp b/libaegisub/windows/path_win.cpp
index 387cf1cdd3c763f471f505a17dceb63884703617..4a42465268aca1ede3206a0a5325d6f91cea76f0 100644
--- a/libaegisub/windows/path_win.cpp
+++ b/libaegisub/windows/path_win.cpp
@@ -29,28 +29,30 @@
 #include <Shellapi.h>
 
 namespace {
-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;
+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() {
-	SetToken("?temp", boost::filesystem::temp_directory_path());
+void Path::FillPlatformSpecificPaths()
+{
+    SetToken("?temp", boost::filesystem::temp_directory_path());
 
-	SetToken("?user", WinGetFolderPath(CSIDL_APPDATA)/"Aegisub");
-	SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA)/"Aegisub");
+    SetToken("?user", WinGetFolderPath(CSIDL_APPDATA) / "Aegisub");
+    SetToken("?local", WinGetFolderPath(CSIDL_LOCAL_APPDATA) / "Aegisub");
 
-	std::wstring filename(MAX_PATH + 1, L'\0');
-	while (static_cast<DWORD>(filename.size()) == GetModuleFileNameW(nullptr, &filename[0], filename.size()))
-		filename.resize(filename.size() * 2);
-	SetToken("?data", filename);
+    std::wstring filename(MAX_PATH + 1, L'\0');
+    while (static_cast<DWORD>(filename.size()) == GetModuleFileNameW(nullptr, &filename[0], filename.size()))
+        filename.resize(filename.size() * 2);
+    SetToken("?data", filename);
 
-	SetToken("?dictionary", Decode("?data/dictionaries"));
+    SetToken("?dictionary", Decode("?data/dictionaries"));
 }
 
 }
diff --git a/libaegisub/windows/util_win.cpp b/libaegisub/windows/util_win.cpp
index 7e91c0421e98dfdef0e5af0cb2c140605722b305..ab72d15c3b80771761e42ed944aa3991b929040c 100644
--- a/libaegisub/windows/util_win.cpp
+++ b/libaegisub/windows/util_win.cpp
@@ -22,48 +22,51 @@
 #include <windows.h>
 
 namespace agi {
-	namespace util {
+namespace util {
 
 using agi::charset::ConvertW;
 
-std::string ErrorString(int error) {
-	LPWSTR lpstr = nullptr;
+std::string ErrorString(int error)
+{
+    LPWSTR lpstr = nullptr;
 
-	if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 0, reinterpret_cast<LPWSTR>(&lpstr), 0, nullptr) == 0) {
-		/// @todo Return the actual 'unknown error' string from windows.
-		return "Unknown Error";
-	}
+    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error, 0, reinterpret_cast<LPWSTR>(&lpstr), 0, nullptr) == 0) {
+        /// @todo Return the actual 'unknown error' string from windows.
+        return "Unknown Error";
+    }
 
-	std::string str = ConvertW(lpstr);
-	LocalFree(lpstr);
-	return str;
+    std::string str = ConvertW(lpstr);
+    LocalFree(lpstr);
+    return str;
 }
 
 #define MS_VC_EXCEPTION 0x406d1388
 
 /// Parameters for setting the thread name
 struct THREADNAME_INFO {
-	DWORD dwType;     ///< must be 0x1000
-	LPCSTR szName;    ///< pointer to name (in same addr space)
-	DWORD dwThreadID; ///< thread ID (-1 caller thread)
-	DWORD dwFlags;    ///< reserved for future use, most be zero
+    DWORD dwType;     ///< must be 0x1000
+    LPCSTR szName;    ///< pointer to name (in same addr space)
+    DWORD dwThreadID; ///< thread ID (-1 caller thread)
+    DWORD dwFlags;    ///< reserved for future use, most be zero
 };
 
-void SetThreadName(LPCSTR szThreadName) {
-	THREADNAME_INFO info;
-	info.dwType = 0x1000;
-	info.szName = szThreadName;
-	info.dwThreadID = -1;
-	info.dwFlags = 0;
-	__try {
-		RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
-	}
-	__except (EXCEPTION_CONTINUE_EXECUTION) {}
+void SetThreadName(LPCSTR szThreadName)
+{
+    THREADNAME_INFO info;
+    info.dwType = 0x1000;
+    info.szName = szThreadName;
+    info.dwThreadID = -1;
+    info.dwFlags = 0;
+    __try {
+        RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info);
+    }
+    __except (EXCEPTION_CONTINUE_EXECUTION) {}
 }
 
-void sleep_for(int ms) {
-	std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+void sleep_for(int ms)
+{
+    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
 }
 
-	} // namespace io
+} // namespace io
 } // namespace agi
diff --git a/src/MatroskaParser.c b/src/MatroskaParser.c
index c50f494f63b4f1b40e80f84c9e33dfca17af36d3..f2120da1908655886a3b1b08489aff2c7b061f4d 100644
--- a/src/MatroskaParser.c
+++ b/src/MatroskaParser.c
@@ -1,29 +1,29 @@
 /*
  * Copyright (c) 2004-2009 Mike Matsnev.  All Rights Reserved.
  *
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions 
- * are met: 
- * 
- * 1. Redistributions of source code must retain the above copyright 
- *    notice immediately at the beginning of the file, without modification, 
- *    this list of conditions, and the following disclaimer. 
- * 2. 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. 
- * 3. Absolutely no warranty of function or purpose is made by the author 
- *    Mike Matsnev. 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice immediately at the beginning of the file, without modification,
+ *    this list of conditions, and the following disclaimer.
+ * 2. 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.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ *    Mike Matsnev.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/
 
@@ -84,53 +84,55 @@
 #define	ONE		      ULL(1)
 
 // compatibility
-static char  *mystrdup(struct InputStream *is,const char *src) {
-  size_t  len;
-  char	  *dst;
+static char  *mystrdup(struct InputStream *is, const char *src)
+{
+    size_t  len;
+    char	  *dst;
 
-  if (src==NULL)
-    return NULL;
+    if (src == NULL)
+        return NULL;
 
-  len = strlen(src);
-  dst = is->memalloc(is,len+1);
-  if (dst==NULL)
-    return NULL;
+    len = strlen(src);
+    dst = is->memalloc(is, len + 1);
+    if (dst == NULL)
+        return NULL;
 
-  memcpy(dst,src,len+1);
+    memcpy(dst, src, len + 1);
 
-  return dst;
+    return dst;
 }
 
-static void  mystrlcpy(char *dst,const char *src,unsigned size) {
-  unsigned  i;
+static void  mystrlcpy(char *dst, const char *src, unsigned size)
+{
+    unsigned  i;
 
-  for (i=0;i+1<size && src[i];++i)
-    dst[i] = src[i];
-  if (i<size)
-    dst[i] = 0;
+    for (i = 0; i + 1 < size && src[i]; ++i)
+        dst[i] = src[i];
+    if (i < size)
+        dst[i] = 0;
 }
 
 struct Cue {
-  uint64_t	Time;
-  uint64_t	Position;
-  uint64_t	Block;
-  unsigned char	Track;
+    uint64_t	Time;
+    uint64_t	Position;
+    uint64_t	Block;
+    unsigned char	Track;
 };
 
 struct QueueEntry {
-  struct QueueEntry *next;
-  unsigned int	    Length;
+    struct QueueEntry *next;
+    unsigned int	    Length;
 
-  uint64_t	    Start;
-  uint64_t	    End;
-  uint64_t	    Position;
+    uint64_t	    Start;
+    uint64_t	    End;
+    uint64_t	    Position;
 
-  unsigned int	    flags;
+    unsigned int	    flags;
 };
 
 struct Queue {
-  struct QueueEntry *head;
-  struct QueueEntry *tail;
+    struct QueueEntry *head;
+    struct QueueEntry *tail;
 };
 
 #define	MPF_ERROR 0x10000
@@ -139,318 +141,322 @@ struct Queue {
 #define	RBRESYNC  1
 
 struct MatroskaFile {
-  // parser config
-  unsigned    flags;
-
-  // input
-  InputStream *cache;
-
-  // internal buffering
-  char	      inbuf[IBSZ];
-  uint64_t   bufbase; // file offset of the first byte in buffer
-  int	      bufpos; // current read position in buffer
-  int	      buflen; // valid bytes in buffer
-
-  void	      *cpbuf;
-
-  // error reporting
-  char	      errmsg[128];
-  jmp_buf     jb;
-
-  // pointers to key elements
-  uint64_t   pSegment;
-  uint64_t   pSeekHead;
-  uint64_t   pSegmentInfo;
-  uint64_t   pCluster;
-  uint64_t   pTracks;
-  uint64_t   pCues;
-  uint64_t   pAttachments;
-  uint64_t   pChapters;
-  uint64_t   pTags;
-
-  // flags for key elements
-  struct {
-    unsigned int  SegmentInfo:1;
-    unsigned int  Cluster:1;
-    unsigned int  Tracks:1;
-    unsigned int  Cues:1;
-    unsigned int  Attachments:1;
-    unsigned int  Chapters:1;
-    unsigned int  Tags:1;
-  } seen;
-
-  // file info
-  uint64_t   firstTimecode;
-
-  // SegmentInfo
-  struct SegmentInfo  Seg;
-
-  // Tracks
-  unsigned int	    nTracks,nTracksSize;
-  struct TrackInfo  **Tracks;
-
-  // Queues
-  struct QueueEntry *QFreeList;
-  unsigned int	    nQBlocks,nQBlocksSize;
-  struct QueueEntry **QBlocks;
-  struct Queue	    *Queues;
-  uint64_t	    readPosition;
-  unsigned int	    trackMask;
-  uint64_t	    pSegmentTop;  // offset of next byte after the segment
-  uint64_t	    tcCluster;    // current cluster timecode
-
-  // Cues
-  unsigned int	    nCues,nCuesSize;
-  struct Cue	    *Cues;
-
-  // Attachments
-  unsigned int	    nAttachments,nAttachmentsSize;
-  struct Attachment *Attachments;
-
-  // Chapters
-  unsigned int	    nChapters,nChaptersSize;
-  struct Chapter    *Chapters;
-
-  // Tags
-  unsigned int	    nTags,nTagsSize;
-  struct Tag	    *Tags;
+    // parser config
+    unsigned    flags;
+
+    // input
+    InputStream *cache;
+
+    // internal buffering
+    char	      inbuf[IBSZ];
+    uint64_t   bufbase; // file offset of the first byte in buffer
+    int	      bufpos; // current read position in buffer
+    int	      buflen; // valid bytes in buffer
+
+    void	      *cpbuf;
+
+    // error reporting
+    char	      errmsg[128];
+    jmp_buf     jb;
+
+    // pointers to key elements
+    uint64_t   pSegment;
+    uint64_t   pSeekHead;
+    uint64_t   pSegmentInfo;
+    uint64_t   pCluster;
+    uint64_t   pTracks;
+    uint64_t   pCues;
+    uint64_t   pAttachments;
+    uint64_t   pChapters;
+    uint64_t   pTags;
+
+    // flags for key elements
+    struct {
+        unsigned int  SegmentInfo: 1;
+        unsigned int  Cluster: 1;
+        unsigned int  Tracks: 1;
+        unsigned int  Cues: 1;
+        unsigned int  Attachments: 1;
+        unsigned int  Chapters: 1;
+        unsigned int  Tags: 1;
+    } seen;
+
+    // file info
+    uint64_t   firstTimecode;
+
+    // SegmentInfo
+    struct SegmentInfo  Seg;
+
+    // Tracks
+    unsigned int	    nTracks, nTracksSize;
+    struct TrackInfo  **Tracks;
+
+    // Queues
+    struct QueueEntry *QFreeList;
+    unsigned int	    nQBlocks, nQBlocksSize;
+    struct QueueEntry **QBlocks;
+    struct Queue	    *Queues;
+    uint64_t	    readPosition;
+    unsigned int	    trackMask;
+    uint64_t	    pSegmentTop;  // offset of next byte after the segment
+    uint64_t	    tcCluster;    // current cluster timecode
+
+    // Cues
+    unsigned int	    nCues, nCuesSize;
+    struct Cue	    *Cues;
+
+    // Attachments
+    unsigned int	    nAttachments, nAttachmentsSize;
+    struct Attachment *Attachments;
+
+    // Chapters
+    unsigned int	    nChapters, nChaptersSize;
+    struct Chapter    *Chapters;
+
+    // Tags
+    unsigned int	    nTags, nTagsSize;
+    struct Tag	    *Tags;
 };
 
 ///////////////////////////////////////////////////////////////////////////
 // error reporting
-static void   myvsnprintf_string(char **pdest,char *de,const char *str) {
-  char	*dest = *pdest;
+static void   myvsnprintf_string(char **pdest, char *de, const char *str)
+{
+    char	*dest = *pdest;
 
-  while (dest < de && *str)
-    *dest++ = *str++;
+    while (dest < de && *str)
+        *dest++ = *str++;
 
-  *pdest = dest;
+    *pdest = dest;
 }
 
-static void   myvsnprintf_uint_impl(char **pdest,char *de,int width,int zero,
-				    int neg,unsigned base,int letter,
-				    int ms,uint64_t val)
+static void   myvsnprintf_uint_impl(char **pdest, char *de, int width, int zero,
+                                    int neg, unsigned base, int letter,
+                                    int ms, uint64_t val)
 {
-  char	*dest = *pdest;
-  char	tmp[21]; /* enough for 64 bit ints */
-  char	*np = tmp + sizeof(tmp);
-  int	rw,pad,trail;
-  char	pc = zero ? '0' : ' ';
-
-  *--np = '\0';
-  if (val == 0)
-    *--np = '0';
-  else
-    while (val != 0) {
-      int	  rem = (int)(val % base);
-      val = val / base;
+    char	*dest = *pdest;
+    char	tmp[21]; /* enough for 64 bit ints */
+    char	*np = tmp + sizeof(tmp);
+    int	rw, pad, trail;
+    char	pc = zero ? '0' : ' ';
+
+    *--np = '\0';
+    if (val == 0)
+        *--np = '0';
+    else
+        while (val != 0) {
+            int	  rem = (int)(val % base);
+            val = val / base;
 
-      *--np = (char)(rem < 10 ? rem + '0' : rem - 10 + letter);
-    }
+            *--np = (char)(rem < 10 ? rem + '0' : rem - 10 + letter);
+        }
 
-  rw = (int)(tmp - np + sizeof(tmp) - 1);
-  if (ms)
-    ++rw;
+    rw = (int)(tmp - np + sizeof(tmp) - 1);
+    if (ms)
+        ++rw;
 
-  pad = trail = 0;
+    pad = trail = 0;
 
-  if (rw < width)
-    pad = width - rw;
+    if (rw < width)
+        pad = width - rw;
 
-  if (neg)
-    trail = pad, pad = 0;
+    if (neg)
+        trail = pad, pad = 0;
 
-  if (dest < de && ms)
-    *dest++ = '-';
+    if (dest < de && ms)
+        *dest++ = '-';
 
-  while (dest < de && pad--)
-    *dest++ = pc;
+    while (dest < de && pad--)
+        *dest++ = pc;
 
-  while (dest < de && *np)
-    *dest++ = *np++;
+    while (dest < de && *np)
+        *dest++ = *np++;
 
-  while (dest < de && trail--)
-    *dest++ = ' ';
+    while (dest < de && trail--)
+        *dest++ = ' ';
 
-  *pdest = dest;
+    *pdest = dest;
 }
 
-static void   myvsnprintf_uint(char **pdest,char *de,int width,int zero,
-			       int neg,unsigned base,int letter,
-			       uint64_t val)
+static void   myvsnprintf_uint(char **pdest, char *de, int width, int zero,
+                               int neg, unsigned base, int letter,
+                               uint64_t val)
 {
-  myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
+    myvsnprintf_uint_impl(pdest, de, width, zero, neg, base, letter, 0, val);
 }
 
-static void   myvsnprintf_int(char **pdest,char *de,int width,int zero,
-			      int neg,unsigned base,int letter,
-			      int64_t val)
+static void   myvsnprintf_int(char **pdest, char *de, int width, int zero,
+                              int neg, unsigned base, int letter,
+                              int64_t val)
 {
-  if (val < 0)
-    myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,1,-val);
-  else
-    myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
+    if (val < 0)
+        myvsnprintf_uint_impl(pdest, de, width, zero, neg, base, letter, 1, -val);
+    else
+        myvsnprintf_uint_impl(pdest, de, width, zero, neg, base, letter, 0, val);
 }
 
-static void   myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap) {
-  // s,d,x,u,ll
-  char	    *de = dest + dsize - 1;
-  int	    state = 0, width, zero, neg, ll;
-
-  if (dsize <= 1) {
-    if (dsize > 0)
-      *dest = '\0';
-    return;
-  }
-
-  while (*fmt && dest < de)
-    switch (state) {
-      case 0:
-	if (*fmt == '%') {
-	  ++fmt;
-	  state = 1;
-	  width = zero = neg = ll = 0;
-	} else
-	  *dest++ = *fmt++;
-	break;
-      case 1:
-	if (*fmt == '-') {
-	  neg = 1;
-	  ++fmt;
-	  state = 2;
-	  break;
-	}
-	if (*fmt == '0')
-	  zero = 1;
-	state = 2;
-      case 2:
-	if (*fmt >= '0' && *fmt <= '9') {
-	  width = width * 10 + *fmt++ - '0';
-	  break;
-	}
-	state = 3;
-      case 3:
-	if (*fmt == 'l') {
-	  ++ll;
-	  ++fmt;
-	  break;
-	}
-	state = 4;
-      case 4:
-	switch (*fmt) {
-	  case 's':
-	    myvsnprintf_string(&dest,de,va_arg(ap,const char *));
-	    break;
-	  case 'd':
-	    switch (ll) {
-	      case 0:
-		myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int));
-		break;
-	      case 1:
-		myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,long));
-		break;
-	      case 2:
-		myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int64_t));
-		break;
-	    }
-	    break;
-	  case 'u':
-	    switch (ll) {
-	      case 0:
-		myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned int));
-		break;
-	      case 1:
-		myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned long));
-		break;
-	      case 2:
-		myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,uint64_t));
-		break;
-	    }
-	    break;
-	  case 'x':
-	    switch (ll) {
-	      case 0:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned int));
-		break;
-	      case 1:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned long));
-		break;
-	      case 2:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,uint64_t));
-		break;
-	    }
-	    break;
-	  case 'X':
-	    switch (ll) {
-	      case 0:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned int));
-		break;
-	      case 1:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned long));
-		break;
-	      case 2:
-		myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,uint64_t));
-		break;
-	    }
-	    break;
-	  default:
-	    break;
-	}
-	++fmt;
-	state = 0;
-	break;
-      default:
-	state = 0;
-	break;
+static void   myvsnprintf(char *dest, unsigned dsize, const char *fmt, va_list ap)
+{
+    // s,d,x,u,ll
+    char	    *de = dest + dsize - 1;
+    int	    state = 0, width, zero, neg, ll;
+
+    if (dsize <= 1) {
+        if (dsize > 0)
+            *dest = '\0';
+        return;
     }
-  *dest = '\0';
+
+    while (*fmt && dest < de)
+        switch (state) {
+        case 0:
+            if (*fmt == '%') {
+                ++fmt;
+                state = 1;
+                width = zero = neg = ll = 0;
+            }
+            else
+                *dest++ = *fmt++;
+            break;
+        case 1:
+            if (*fmt == '-') {
+                neg = 1;
+                ++fmt;
+                state = 2;
+                break;
+            }
+            if (*fmt == '0')
+                zero = 1;
+            state = 2;
+        case 2:
+            if (*fmt >= '0' && *fmt <= '9') {
+                width = width * 10 + *fmt++ - '0';
+                break;
+            }
+            state = 3;
+        case 3:
+            if (*fmt == 'l') {
+                ++ll;
+                ++fmt;
+                break;
+            }
+            state = 4;
+        case 4:
+            switch (*fmt) {
+            case 's':
+                myvsnprintf_string(&dest, de, va_arg(ap, const char *));
+                break;
+            case 'd':
+                switch (ll) {
+                case 0:
+                    myvsnprintf_int(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, int));
+                    break;
+                case 1:
+                    myvsnprintf_int(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, long));
+                    break;
+                case 2:
+                    myvsnprintf_int(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, int64_t));
+                    break;
+                }
+                break;
+            case 'u':
+                switch (ll) {
+                case 0:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, unsigned int));
+                    break;
+                case 1:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, unsigned long));
+                    break;
+                case 2:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 10, 'a', va_arg(ap, uint64_t));
+                    break;
+                }
+                break;
+            case 'x':
+                switch (ll) {
+                case 0:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'a', va_arg(ap, unsigned int));
+                    break;
+                case 1:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'a', va_arg(ap, unsigned long));
+                    break;
+                case 2:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'a', va_arg(ap, uint64_t));
+                    break;
+                }
+                break;
+            case 'X':
+                switch (ll) {
+                case 0:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'A', va_arg(ap, unsigned int));
+                    break;
+                case 1:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'A', va_arg(ap, unsigned long));
+                    break;
+                case 2:
+                    myvsnprintf_uint(&dest, de, width, zero, neg, 16, 'A', va_arg(ap, uint64_t));
+                    break;
+                }
+                break;
+            default:
+                break;
+            }
+            ++fmt;
+            state = 0;
+            break;
+        default:
+            state = 0;
+            break;
+        }
+    *dest = '\0';
 }
 
-static void   errorjmp(MatroskaFile *mf,const char *fmt, ...) {
-  va_list   ap;
+static void   errorjmp(MatroskaFile *mf, const char *fmt, ...)
+{
+    va_list   ap;
 
-  mf->cache->memfree(mf->cache, mf->cpbuf);
-  mf->cpbuf = NULL;
+    mf->cache->memfree(mf->cache, mf->cpbuf);
+    mf->cpbuf = NULL;
 
-  va_start(ap, fmt);
-  myvsnprintf(mf->errmsg,sizeof(mf->errmsg),fmt,ap);
-  va_end(ap);
+    va_start(ap, fmt);
+    myvsnprintf(mf->errmsg, sizeof(mf->errmsg), fmt, ap);
+    va_end(ap);
 
-  mf->flags |= MPF_ERROR;
+    mf->flags |= MPF_ERROR;
 
-  longjmp(mf->jb,1);
+    longjmp(mf->jb, 1);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // arrays
-static void *ArrayAlloc(MatroskaFile *mf,void **base,
-			unsigned *cur,unsigned *max,unsigned elem_size)
+static void *ArrayAlloc(MatroskaFile *mf, void **base,
+                        unsigned *cur, unsigned *max, unsigned elem_size)
 {
-  if (*cur>=*max) {
-    void      *np;
-    unsigned  newsize = *max * 2;
-    if (newsize==0)
-      newsize = 1;
-
-    np = mf->cache->memrealloc(mf->cache,*base,newsize*elem_size);
-    if (np==NULL)
-      errorjmp(mf,"Out of memory in ArrayAlloc");
-
-    *base = np;
-    *max = newsize;
-  }
+    if (*cur >= *max) {
+        void      *np;
+        unsigned  newsize = *max * 2;
+        if (newsize == 0)
+            newsize = 1;
+
+        np = mf->cache->memrealloc(mf->cache, *base, newsize * elem_size);
+        if (np == NULL)
+            errorjmp(mf, "Out of memory in ArrayAlloc");
+
+        *base = np;
+        *max = newsize;
+    }
 
-  return (char*)*base + elem_size * (*cur)++;
+    return (char *)*base + elem_size * (*cur)++;
 }
 
-static void ArrayReleaseMemory(MatroskaFile *mf,void **base,
-			       unsigned cur,unsigned *max,unsigned elem_size)
+static void ArrayReleaseMemory(MatroskaFile *mf, void **base,
+                               unsigned cur, unsigned *max, unsigned elem_size)
 {
-  if (cur<*max) {
-    void  *np = mf->cache->memrealloc(mf->cache,*base,cur*elem_size);
-    *base = np;
-    *max = cur;
-  }
+    if (cur < *max) {
+        void  *np = mf->cache->memrealloc(mf->cache, *base, cur * elem_size);
+        *base = np;
+        *max = cur;
+    }
 }
 
 
@@ -460,414 +466,439 @@ static void ArrayReleaseMemory(MatroskaFile *mf,void **base,
 
 ///////////////////////////////////////////////////////////////////////////
 // queues
-static struct QueueEntry *QPut(struct Queue *q,struct QueueEntry *qe) {
-  if (q->tail)
-    q->tail->next = qe;
-  qe->next = NULL;
-  q->tail = qe;
-  if (q->head==NULL)
-    q->head = qe;
-
-  return qe;
-}
-
-static struct QueueEntry  *QGet(struct Queue *q) {
-  struct QueueEntry   *qe = q->head;
-  if (qe == NULL)
-    return NULL;
-  q->head = qe->next;
-  if (q->tail == qe)
-    q->tail = NULL;
-  return qe;
+static struct QueueEntry *QPut(struct Queue *q, struct QueueEntry *qe)
+{
+    if (q->tail)
+        q->tail->next = qe;
+    qe->next = NULL;
+    q->tail = qe;
+    if (q->head == NULL)
+        q->head = qe;
+
+    return qe;
+}
+
+static struct QueueEntry  *QGet(struct Queue *q)
+{
+    struct QueueEntry   *qe = q->head;
+    if (qe == NULL)
+        return NULL;
+    q->head = qe->next;
+    if (q->tail == qe)
+        q->tail = NULL;
+    return qe;
 }
 
-static struct QueueEntry  *QAlloc(MatroskaFile *mf) {
-  struct QueueEntry   *qe,**qep;
-  if (mf->QFreeList == NULL) {
-    unsigned	      i;
+static struct QueueEntry  *QAlloc(MatroskaFile *mf)
+{
+    struct QueueEntry   *qe, **qep;
+    if (mf->QFreeList == NULL) {
+        unsigned	      i;
 
-    qep = AGET(mf,QBlocks);
+        qep = AGET(mf, QBlocks);
 
-    *qep = mf->cache->memalloc(mf->cache,QSEGSIZE * sizeof(*qe));
-    if (*qep == NULL)
-      errorjmp(mf,"Ouf of memory");
+        *qep = mf->cache->memalloc(mf->cache, QSEGSIZE * sizeof(*qe));
+        if (*qep == NULL)
+            errorjmp(mf, "Ouf of memory");
 
-    qe = *qep;
+        qe = *qep;
 
-    for (i=0;i<QSEGSIZE-1;++i)
-      qe[i].next = qe+i+1;
-    qe[QSEGSIZE-1].next = NULL;
+        for (i = 0; i < QSEGSIZE - 1; ++i)
+            qe[i].next = qe + i + 1;
+        qe[QSEGSIZE - 1].next = NULL;
 
-    mf->QFreeList = qe;
-  }
+        mf->QFreeList = qe;
+    }
 
-  qe = mf->QFreeList;
-  mf->QFreeList = qe->next;
+    qe = mf->QFreeList;
+    mf->QFreeList = qe->next;
 
-  return qe;
+    return qe;
 }
 
-static inline void QFree(MatroskaFile *mf,struct QueueEntry *qe) {
-  qe->next = mf->QFreeList;
-  mf->QFreeList = qe;
+static inline void QFree(MatroskaFile *mf, struct QueueEntry *qe)
+{
+    qe->next = mf->QFreeList;
+    mf->QFreeList = qe;
 }
 
 // fill the buffer at current position
-static void fillbuf(MatroskaFile *mf) {
-  int	    rd;
+static void fillbuf(MatroskaFile *mf)
+{
+    int	    rd;
 
-  // advance buffer pointers
-  mf->bufbase += mf->buflen;
-  mf->buflen = mf->bufpos = 0;
+    // advance buffer pointers
+    mf->bufbase += mf->buflen;
+    mf->buflen = mf->bufpos = 0;
 
-  // get the relevant page
-  rd = mf->cache->read(mf->cache, mf->bufbase, mf->inbuf, IBSZ);
-  if (rd<0)
-    errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
+    // get the relevant page
+    rd = mf->cache->read(mf->cache, mf->bufbase, mf->inbuf, IBSZ);
+    if (rd < 0)
+        errorjmp(mf, "I/O Error: %s", mf->cache->geterror(mf->cache));
 
-  mf->buflen = rd;
+    mf->buflen = rd;
 }
 
 // fill the buffer and return next char
-static int  nextbuf(MatroskaFile *mf) {
-  fillbuf(mf);
+static int  nextbuf(MatroskaFile *mf)
+{
+    fillbuf(mf);
 
-  if (mf->bufpos < mf->buflen)
-    return (unsigned char)(mf->inbuf[mf->bufpos++]);
+    if (mf->bufpos < mf->buflen)
+        return (unsigned char)(mf->inbuf[mf->bufpos++]);
 
-  return EOF;
+    return EOF;
 }
 
-static inline int readch(MatroskaFile *mf) {
-  return mf->bufpos < mf->buflen ? (unsigned char)(mf->inbuf[mf->bufpos++]) : nextbuf(mf);
+static inline int readch(MatroskaFile *mf)
+{
+    return mf->bufpos < mf->buflen ? (unsigned char)(mf->inbuf[mf->bufpos++]) : nextbuf(mf);
 }
 
-static inline uint64_t	filepos(MatroskaFile *mf) {
-  return mf->bufbase + mf->bufpos;
+static inline uint64_t	filepos(MatroskaFile *mf)
+{
+    return mf->bufbase + mf->bufpos;
 }
 
-static void   readbytes(MatroskaFile *mf,void *buffer,int len) {
-  char	*cp = buffer;
-  int	nb = mf->buflen - mf->bufpos;
-
-  if (nb > len)
-    nb = len;
-
-  memcpy(cp, mf->inbuf + mf->bufpos, nb);
-  mf->bufpos += nb;
-  len -= nb;
-  cp += nb;
-
-  if (len>0) {
-    mf->bufbase += mf->buflen;
-    mf->bufpos = mf->buflen = 0;
-
-    nb = mf->cache->read(mf->cache, mf->bufbase, cp, len);
-    if (nb<0)
-      errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
-    if (nb != len)
-      errorjmp(mf,"Short read: got %d bytes of %d",nb,len);
-    mf->bufbase += len;
-  }
+static void   readbytes(MatroskaFile *mf, void *buffer, int len)
+{
+    char	*cp = buffer;
+    int	nb = mf->buflen - mf->bufpos;
+
+    if (nb > len)
+        nb = len;
+
+    memcpy(cp, mf->inbuf + mf->bufpos, nb);
+    mf->bufpos += nb;
+    len -= nb;
+    cp += nb;
+
+    if (len > 0) {
+        mf->bufbase += mf->buflen;
+        mf->bufpos = mf->buflen = 0;
+
+        nb = mf->cache->read(mf->cache, mf->bufbase, cp, len);
+        if (nb < 0)
+            errorjmp(mf, "I/O Error: %s", mf->cache->geterror(mf->cache));
+        if (nb != len)
+            errorjmp(mf, "Short read: got %d bytes of %d", nb, len);
+        mf->bufbase += len;
+    }
 }
 
-static void   skipbytes(MatroskaFile *mf,uint64_t len) {
-  unsigned int	    nb = mf->buflen - mf->bufpos;
+static void   skipbytes(MatroskaFile *mf, uint64_t len)
+{
+    unsigned int	    nb = mf->buflen - mf->bufpos;
 
-  if (nb > len)
-    nb = (int)len;
+    if (nb > len)
+        nb = (int)len;
 
-  mf->bufpos += nb;
-  len -= nb;
+    mf->bufpos += nb;
+    len -= nb;
 
-  if (len>0) {
-    mf->bufbase += mf->buflen;
-    mf->bufpos = mf->buflen = 0;
+    if (len > 0) {
+        mf->bufbase += mf->buflen;
+        mf->bufpos = mf->buflen = 0;
 
-    mf->bufbase += len;
-  }
+        mf->bufbase += len;
+    }
 }
 
-static void seek(MatroskaFile *mf,uint64_t pos) {
-  // see if pos is inside buffer
-  if (pos>=mf->bufbase && pos<mf->bufbase+mf->buflen)
-    mf->bufpos = (unsigned)(pos - mf->bufbase);
-  else {
-    // invalidate buffer and set pointer
-    mf->bufbase = pos;
-    mf->buflen = mf->bufpos = 0;
-  }
+static void seek(MatroskaFile *mf, uint64_t pos)
+{
+    // see if pos is inside buffer
+    if (pos >= mf->bufbase && pos < mf->bufbase + mf->buflen)
+        mf->bufpos = (unsigned)(pos - mf->bufbase);
+    else {
+        // invalidate buffer and set pointer
+        mf->bufbase = pos;
+        mf->buflen = mf->bufpos = 0;
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // floating point
-static inline MKFLOAT	mkfi(int i) {
+static inline MKFLOAT	mkfi(int i)
+{
 #ifdef MATROSKA_INTEGER_ONLY
-  MKFLOAT  f;
-  f.v = (int64_t)i << 32;
-  return f;
+    MKFLOAT  f;
+    f.v = (int64_t)i << 32;
+    return f;
 #else
-  return i;
+    return i;
 #endif
 }
 
-static inline int64_t mul3(MKFLOAT scale,int64_t tc) {
+static inline int64_t mul3(MKFLOAT scale, int64_t tc)
+{
 #ifdef MATROSKA_INTEGER_ONLY
-  //             x1 x0
-  //             y1 y0
-  //    --------------
-  //             x0*y0
-  //          x1*y0
-  //          x0*y1
-  //       x1*y1
-  //    --------------
-  //       .. r1 r0 ..
-  //
-  //    r = ((x0*y0) >> 32) + (x1*y0) + (x0*y1) + ((x1*y1) << 32)
-  unsigned    x0,x1,y0,y1;
-  uint64_t   p;
-  char	      sign = 0;
-
-  if (scale.v < 0)
-    sign = !sign, scale.v = -scale.v;
-  if (tc < 0)
-    sign = !sign, tc = -tc;
-
-  x0 = (unsigned)scale.v;
-  x1 = (unsigned)((uint64_t)scale.v >> 32);
-  y0 = (unsigned)tc;
-  y1 = (unsigned)((uint64_t)tc >> 32);
-
-  p = (uint64_t)x0*y0 >> 32;
-  p += (uint64_t)x0*y1;
-  p += (uint64_t)x1*y0;
-  p += (uint64_t)(x1*y1) << 32;
-
-  return p;
+    //             x1 x0
+    //             y1 y0
+    //    --------------
+    //             x0*y0
+    //          x1*y0
+    //          x0*y1
+    //       x1*y1
+    //    --------------
+    //       .. r1 r0 ..
+    //
+    //    r = ((x0*y0) >> 32) + (x1*y0) + (x0*y1) + ((x1*y1) << 32)
+    unsigned    x0, x1, y0, y1;
+    uint64_t   p;
+    char	      sign = 0;
+
+    if (scale.v < 0)
+        sign = !sign, scale.v = -scale.v;
+    if (tc < 0)
+        sign = !sign, tc = -tc;
+
+    x0 = (unsigned)scale.v;
+    x1 = (unsigned)((uint64_t)scale.v >> 32);
+    y0 = (unsigned)tc;
+    y1 = (unsigned)((uint64_t)tc >> 32);
+
+    p = (uint64_t)x0 * y0 >> 32;
+    p += (uint64_t)x0 * y1;
+    p += (uint64_t)x1 * y0;
+    p += (uint64_t)(x1 * y1) << 32;
+
+    return p;
 #else
-  return (int64_t)(scale * tc);
+    return (int64_t)(scale * tc);
 #endif
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // EBML support
-static int   readID(MatroskaFile *mf) {
-  int	c1,c2,c3,c4;
+static int   readID(MatroskaFile *mf)
+{
+    int	c1, c2, c3, c4;
 
-  c1 = readch(mf);
-  if (c1 == EOF)
-    return EOF;
+    c1 = readch(mf);
+    if (c1 == EOF)
+        return EOF;
 
-  if (c1 & 0x80)
-    return c1;
+    if (c1 & 0x80)
+        return c1;
 
-  if ((c1 & 0xf0) == 0)
-    errorjmp(mf,"Invalid first byte of EBML ID: %02X",c1);
+    if ((c1 & 0xf0) == 0)
+        errorjmp(mf, "Invalid first byte of EBML ID: %02X", c1);
 
-  c2 = readch(mf);
-  if (c2 == EOF)
+    c2 = readch(mf);
+    if (c2 == EOF)
 fail:
-    errorjmp(mf,"Got EOF while reading EBML ID");
+        errorjmp(mf, "Got EOF while reading EBML ID");
 
-  if ((c1 & 0xc0) == 0x40)
-    return (c1<<8) | c2;
+    if ((c1 & 0xc0) == 0x40)
+        return (c1 << 8) | c2;
 
-  c3 = readch(mf);
-  if (c3 == EOF)
-    goto fail;
+    c3 = readch(mf);
+    if (c3 == EOF)
+        goto fail;
 
-  if ((c1 & 0xe0) == 0x20)
-    return (c1<<16) | (c2<<8) | c3;
+    if ((c1 & 0xe0) == 0x20)
+        return (c1 << 16) | (c2 << 8) | c3;
 
-  c4 = readch(mf);
-  if (c4 == EOF)
-    goto fail;
+    c4 = readch(mf);
+    if (c4 == EOF)
+        goto fail;
 
-  if ((c1 & 0xf0) == 0x10)
-    return (c1<<24) | (c2<<16) | (c3<<8) | c4;
+    if ((c1 & 0xf0) == 0x10)
+        return (c1 << 24) | (c2 << 16) | (c3 << 8) | c4;
 
-  return 0; // NOT REACHED
+    return 0; // NOT REACHED
 }
 
-static uint64_t readVLUIntImp(MatroskaFile *mf,int *mask) {
-  int		c,d,m;
-  uint64_t	v = 0;
+static uint64_t readVLUIntImp(MatroskaFile *mf, int *mask)
+{
+    int		c, d, m;
+    uint64_t	v = 0;
 
-  c = readch(mf);
-  if (c == EOF)
-    return 0; // XXX should errorjmp()?
+    c = readch(mf);
+    if (c == EOF)
+        return 0; // XXX should errorjmp()?
 
-  if (c == 0)
-    errorjmp(mf,"Invalid first byte of EBML integer: 0");
+    if (c == 0)
+        errorjmp(mf, "Invalid first byte of EBML integer: 0");
 
-  for (m=0;;++m) {
-    if (c & (0x80 >> m)) {
-      c &= 0x7f >> m;
-      if (mask)
-	*mask = m;
-      return v | ((uint64_t)c << m*8);
+    for (m = 0;; ++m) {
+        if (c & (0x80 >> m)) {
+            c &= 0x7f >> m;
+            if (mask)
+                *mask = m;
+            return v | ((uint64_t)c << m * 8);
+        }
+        d = readch(mf);
+        if (d == EOF)
+            errorjmp(mf, "Got EOF while reading EBML unsigned integer");
+        v = (v << 8) | d;
     }
-    d = readch(mf);
-    if (d == EOF)
-      errorjmp(mf,"Got EOF while reading EBML unsigned integer");
-    v = (v<<8) | d;
-  }
-  // NOT REACHED
+    // NOT REACHED
 }
 
-static inline uint64_t	readVLUInt(MatroskaFile *mf) {
-  return readVLUIntImp(mf,NULL);
+static inline uint64_t	readVLUInt(MatroskaFile *mf)
+{
+    return readVLUIntImp(mf, NULL);
 }
 
-static uint64_t	readSize(MatroskaFile *mf) {
-  int	    m;
-  uint64_t v = readVLUIntImp(mf,&m);
+static uint64_t	readSize(MatroskaFile *mf)
+{
+    int	    m;
+    uint64_t v = readVLUIntImp(mf, &m);
 
-  // see if it's unspecified
-  if (v == (MAXU64 >> (57-m*7)))
-    errorjmp(mf,"Unspecified element size is not supported here.");
+    // see if it's unspecified
+    if (v == (MAXU64 >> (57 - m * 7)))
+        errorjmp(mf, "Unspecified element size is not supported here.");
 
-  return v;
+    return v;
 }
 
-static inline int64_t	readVLSInt(MatroskaFile *mf) {
-  static int64_t bias[8] = { (ONE<<6)-1, (ONE<<13)-1, (ONE<<20)-1, (ONE<<27)-1,
-			      (ONE<<34)-1, (ONE<<41)-1, (ONE<<48)-1, (ONE<<55)-1 };
+static inline int64_t	readVLSInt(MatroskaFile *mf)
+{
+    static int64_t bias[8] = { (ONE << 6) - 1, (ONE << 13) - 1, (ONE << 20) - 1, (ONE << 27) - 1,
+                               (ONE << 34) - 1, (ONE << 41) - 1, (ONE << 48) - 1, (ONE << 55) - 1
+                             };
 
-  int	    m;
-  int64_t  v = readVLUIntImp(mf,&m);
+    int	    m;
+    int64_t  v = readVLUIntImp(mf, &m);
 
-  return v - bias[m];
+    return v - bias[m];
 }
 
-static uint64_t  readUInt(MatroskaFile *mf,unsigned int len) {
-  int		c;
-  unsigned int	m = len;
-  uint64_t	v = 0;
+static uint64_t  readUInt(MatroskaFile *mf, unsigned int len)
+{
+    int		c;
+    unsigned int	m = len;
+    uint64_t	v = 0;
 
-  if (len==0)
-    return v;
-  if (len>8)
-    errorjmp(mf,"Unsupported integer size in readUInt: %u",len);
+    if (len == 0)
+        return v;
+    if (len > 8)
+        errorjmp(mf, "Unsupported integer size in readUInt: %u", len);
 
-  do {
-    c = readch(mf);
-    if (c == EOF)
-      errorjmp(mf,"Got EOF while reading EBML unsigned integer");
-    v = (v<<8) | c;
-  } while (--m);
+    do {
+        c = readch(mf);
+        if (c == EOF)
+            errorjmp(mf, "Got EOF while reading EBML unsigned integer");
+        v = (v << 8) | c;
+    } while (--m);
 
-  return v;
+    return v;
 }
 
-static inline int64_t	readSInt(MatroskaFile *mf,unsigned int len) {
-  int64_t	v = readUInt(mf,(unsigned)len);
-  int		s = 64 - (len<<3);
-  return (v << s) >> s;
+static inline int64_t	readSInt(MatroskaFile *mf, unsigned int len)
+{
+    int64_t	v = readUInt(mf, (unsigned)len);
+    int		s = 64 - (len << 3);
+    return (v << s) >> s;
 }
 
-static MKFLOAT readFloat(MatroskaFile *mf,unsigned int len) {
+static MKFLOAT readFloat(MatroskaFile *mf, unsigned int len)
+{
 #ifdef MATROSKA_INTEGER_ONLY
-  MKFLOAT	  f;
-  int		  shift;
+    MKFLOAT	  f;
+    int		  shift;
 #else
-  union {
-    unsigned int  ui;
-    uint64_t	  ull;
-    float	  f;
-    double	  d;
-  } u;
+    union {
+        unsigned int  ui;
+        uint64_t	  ull;
+        float	  f;
+        double	  d;
+    } u;
 #endif
 
-  if (len!=4 && len!=8)
-    errorjmp(mf,"Invalid float size in readFloat: %u",len);
+    if (len != 4 && len != 8)
+        errorjmp(mf, "Invalid float size in readFloat: %u", len);
 
 #ifdef MATROSKA_INTEGER_ONLY
-  if (len == 4) {
-    unsigned  ui = (unsigned)readUInt(mf,(unsigned)len);
-    f.v = (ui & 0x7fffff) | 0x800000;
-    if (ui & 0x80000000)
-      f.v = -f.v;
-    shift = (ui >> 23) & 0xff;
-    if (shift == 0) // assume 0
+    if (len == 4) {
+        unsigned  ui = (unsigned)readUInt(mf, (unsigned)len);
+        f.v = (ui & 0x7fffff) | 0x800000;
+        if (ui & 0x80000000)
+            f.v = -f.v;
+        shift = (ui >> 23) & 0xff;
+        if (shift == 0) // assume 0
 zero:
-      shift = 0, f.v = 0;
-    else if (shift == 255)
+            shift = 0, f.v = 0;
+        else if (shift == 255)
 inf:
-      if (ui & 0x80000000)
-	f.v = LL(0x8000000000000000);
-      else
-	f.v = LL(0x7fffffffffffffff);
-    else {
-      shift += -127 + 9;
-      if (shift > 39)
-	goto inf;
+            if (ui & 0x80000000)
+                f.v = LL(0x8000000000000000);
+            else
+                f.v = LL(0x7fffffffffffffff);
+        else {
+            shift += -127 + 9;
+            if (shift > 39)
+                goto inf;
 shift:
-      if (shift < 0)
-	f.v = f.v >> -shift;
-      else if (shift > 0)
-	f.v = f.v << shift;
-    }
-  } else if (len == 8) {
-    uint64_t  ui = readUInt(mf,(unsigned)len);
-    f.v = (ui & LL(0xfffffffffffff)) | LL(0x10000000000000);
-    if (ui & 0x80000000)
-      f.v = -f.v;
-    shift = (int)((ui >> 52) & 0x7ff);
-    if (shift == 0) // assume 0
-      goto zero;
-    else if (shift == 2047)
-      goto inf;
-    else {
-      shift += -1023 - 20;
-      if (shift > 10)
-	goto inf;
-      goto shift;
+            if (shift < 0)
+                f.v = f.v >> -shift;
+            else if (shift > 0)
+                f.v = f.v << shift;
+        }
+    }
+    else if (len == 8) {
+        uint64_t  ui = readUInt(mf, (unsigned)len);
+        f.v = (ui & LL(0xfffffffffffff)) | LL(0x10000000000000);
+        if (ui & 0x80000000)
+            f.v = -f.v;
+        shift = (int)((ui >> 52) & 0x7ff);
+        if (shift == 0) // assume 0
+            goto zero;
+        else if (shift == 2047)
+            goto inf;
+        else {
+            shift += -1023 - 20;
+            if (shift > 10)
+                goto inf;
+            goto shift;
+        }
     }
-  }
 
-  return f;
+    return f;
 #else
-  if (len==4) {
-    u.ui = (unsigned int)readUInt(mf,(unsigned)len);
-    return u.f;
-  }
+    if (len == 4) {
+        u.ui = (unsigned int)readUInt(mf, (unsigned)len);
+        return u.f;
+    }
 
-  if (len==8) {
-    u.ull = readUInt(mf,(unsigned)len);
-    return u.d;
-  }
+    if (len == 8) {
+        u.ull = readUInt(mf, (unsigned)len);
+        return u.d;
+    }
 
-  return 0;
+    return 0;
 #endif
 }
 
-static void readString(MatroskaFile *mf,uint64_t len,char *buffer,int buflen) {
-  unsigned int	  nread;
+static void readString(MatroskaFile *mf, uint64_t len, char *buffer, int buflen)
+{
+    unsigned int	  nread;
 
-  if (buflen<1)
-    errorjmp(mf,"Invalid buffer size in readString: %d",buflen);
+    if (buflen < 1)
+        errorjmp(mf, "Invalid buffer size in readString: %d", buflen);
 
-  nread = buflen - 1;
+    nread = buflen - 1;
 
-  if (nread > len)
-    nread = (int)len;
+    if (nread > len)
+        nread = (int)len;
 
-  readbytes(mf,buffer,nread);
-  len -= nread;
+    readbytes(mf, buffer, nread);
+    len -= nread;
 
-  if (len>0)
-    skipbytes(mf,len);
+    if (len > 0)
+        skipbytes(mf, len);
 
-  buffer[nread] = '\0';
+    buffer[nread] = '\0';
 }
 
-static void readLangCC(MatroskaFile *mf, uint64_t len, char lcc[4]) {
-  unsigned  todo = len > 3 ? 3 : (int)len;
+static void readLangCC(MatroskaFile *mf, uint64_t len, char lcc[4])
+{
+    unsigned  todo = len > 3 ? 3 : (int)len;
 
-  lcc[0] = lcc[1] = lcc[2] = lcc[3] = 0;
-  readbytes(mf, lcc, todo);
-  skipbytes(mf, len - todo);
+    lcc[0] = lcc[1] = lcc[2] = lcc[3] = 0;
+    readbytes(mf, lcc, todo);
+    skipbytes(mf, len - todo);
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -927,2247 +958,2300 @@ static int  IsWritingApp(MatroskaFile *mf,const char *str) {
   return !*str;
 }
 */
-static void parseEBML(MatroskaFile *mf,uint64_t toplen) {
-  uint64_t v;
-  char	    buf[32];
-
-  FOREACH(mf,toplen)
-    case 0x4286: // Version
-      v = readUInt(mf,(unsigned)len);
-      break;
-    case 0x42f7: // ReadVersion
-      v = readUInt(mf,(unsigned)len);
-      if (v > EBML_VERSION)
-	errorjmp(mf,"File requires version %d EBML parser",(int)v);
-      break;
-    case 0x42f2: // MaxIDLength
-      v = readUInt(mf,(unsigned)len);
-      if (v > EBML_MAX_ID_LENGTH)
-	errorjmp(mf,"File has identifiers longer than %d",(int)v);
-      break;
-    case 0x42f3: // MaxSizeLength
-      v = readUInt(mf,(unsigned)len);
-      if (v > EBML_MAX_SIZE_LENGTH)
-	errorjmp(mf,"File has integers longer than %d",(int)v);
-      break;
-    case 0x4282: // DocType
-      readString(mf,len,buf,sizeof(buf));
-      if (strcmp(buf,MATROSKA_DOCTYPE))
-	errorjmp(mf,"Unsupported DocType: %s",buf);
-      break;
-    case 0x4287: // DocTypeVersion
-      v = readUInt(mf,(unsigned)len);
-      break;
-    case 0x4285: // DocTypeReadVersion
-      v = readUInt(mf,(unsigned)len);
-      if (v > MATROSKA_VERSION)
-	errorjmp(mf,"File requires version %d Matroska parser",(int)v);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseSeekEntry(MatroskaFile *mf,uint64_t toplen) {
-  int	    seekid = 0;
-  uint64_t pos = (uint64_t)-1;
-
-  FOREACH(mf,toplen)
-    case 0x53ab: // SeekID
-      if (len>EBML_MAX_ID_LENGTH)
-	errorjmp(mf,"Invalid ID size in parseSeekEntry: %d\n",(int)len);
-      seekid = (int)readUInt(mf,(unsigned)len);
-      break;
-    case 0x53ac: // SeekPos
-      pos = readUInt(mf,(unsigned)len);
-      break;
-  ENDFOR(mf);
-
-  if (pos == (uint64_t)-1)
-    errorjmp(mf,"Invalid element position in parseSeekEntry");
-
-  pos += mf->pSegment;
-  switch (seekid) {
+static void parseEBML(MatroskaFile *mf, uint64_t toplen)
+{
+    uint64_t v;
+    char	    buf[32];
+
+    FOREACH(mf, toplen)
+case 0x4286: // Version
+    v = readUInt(mf, (unsigned)len);
+    break;
+case 0x42f7: // ReadVersion
+    v = readUInt(mf, (unsigned)len);
+    if (v > EBML_VERSION)
+        errorjmp(mf, "File requires version %d EBML parser", (int)v);
+    break;
+case 0x42f2: // MaxIDLength
+    v = readUInt(mf, (unsigned)len);
+    if (v > EBML_MAX_ID_LENGTH)
+        errorjmp(mf, "File has identifiers longer than %d", (int)v);
+    break;
+case 0x42f3: // MaxSizeLength
+    v = readUInt(mf, (unsigned)len);
+    if (v > EBML_MAX_SIZE_LENGTH)
+        errorjmp(mf, "File has integers longer than %d", (int)v);
+    break;
+case 0x4282: // DocType
+    readString(mf, len, buf, sizeof(buf));
+    if (strcmp(buf, MATROSKA_DOCTYPE))
+        errorjmp(mf, "Unsupported DocType: %s", buf);
+    break;
+case 0x4287: // DocTypeVersion
+    v = readUInt(mf, (unsigned)len);
+    break;
+case 0x4285: // DocTypeReadVersion
+    v = readUInt(mf, (unsigned)len);
+    if (v > MATROSKA_VERSION)
+        errorjmp(mf, "File requires version %d Matroska parser", (int)v);
+    break;
+    ENDFOR(mf);
+}
+
+static void parseSeekEntry(MatroskaFile *mf, uint64_t toplen)
+{
+    int	    seekid = 0;
+    uint64_t pos = (uint64_t) -1;
+
+    FOREACH(mf, toplen)
+case 0x53ab: // SeekID
+    if (len > EBML_MAX_ID_LENGTH)
+        errorjmp(mf, "Invalid ID size in parseSeekEntry: %d\n", (int)len);
+    seekid = (int)readUInt(mf, (unsigned)len);
+    break;
+case 0x53ac: // SeekPos
+    pos = readUInt(mf, (unsigned)len);
+    break;
+    ENDFOR(mf);
+
+    if (pos == (uint64_t) -1)
+        errorjmp(mf, "Invalid element position in parseSeekEntry");
+
+    pos += mf->pSegment;
+    switch (seekid) {
     case 0x114d9b74: // next SeekHead
-      if (mf->pSeekHead)
-	errorjmp(mf,"SeekHead contains more than one SeekHead pointer");
-      mf->pSeekHead = pos;
-      break;
+        if (mf->pSeekHead)
+            errorjmp(mf, "SeekHead contains more than one SeekHead pointer");
+        mf->pSeekHead = pos;
+        break;
     case 0x1549a966: // SegmentInfo
-      mf->pSegmentInfo = pos;
-      break;
+        mf->pSegmentInfo = pos;
+        break;
     case 0x1f43b675: // Cluster
-      if (!mf->pCluster)
-	mf->pCluster = pos;
-      break;
+        if (!mf->pCluster)
+            mf->pCluster = pos;
+        break;
     case 0x1654ae6b: // Tracks
-      mf->pTracks = pos;
-      break;
+        mf->pTracks = pos;
+        break;
     case 0x1c53bb6b: // Cues
-      mf->pCues = pos;
-      break;
+        mf->pCues = pos;
+        break;
     case 0x1941a469: // Attachments
-      mf->pAttachments = pos;
-      break;
+        mf->pAttachments = pos;
+        break;
     case 0x1043a770: // Chapters
-      mf->pChapters = pos;
-      break;
+        mf->pChapters = pos;
+        break;
     case 0x1254c367: // tags
-      mf->pTags = pos;
-      break;
-  }
+        mf->pTags = pos;
+        break;
+    }
 }
 
-static void parseSeekHead(MatroskaFile *mf,uint64_t toplen) {
-  FOREACH(mf,toplen)
-    case 0x4dbb:
-      parseSeekEntry(mf,len);
-      break;
-  ENDFOR(mf);
+static void parseSeekHead(MatroskaFile *mf, uint64_t toplen)
+{
+    FOREACH(mf, toplen)
+case 0x4dbb:
+    parseSeekEntry(mf, len);
+    break;
+    ENDFOR(mf);
 }
 
-static void parseSegmentInfo(MatroskaFile *mf,uint64_t toplen) {
-  MKFLOAT     duration = mkfi(0);
+static void parseSegmentInfo(MatroskaFile *mf, uint64_t toplen)
+{
+    MKFLOAT     duration = mkfi(0);
+
+    if (mf->seen.SegmentInfo) {
+        skipbytes(mf, toplen);
+        return;
+    }
+
+    mf->seen.SegmentInfo = 1;
+    mf->Seg.TimecodeScale = 1000000; // Default value
+
+    FOREACH(mf, toplen)
+case 0x73a4: // SegmentUID
+    if (len != sizeof(mf->Seg.UID))
+        errorjmp(mf, "SegmentUID size is not %d bytes", mf->Seg.UID);
+    readbytes(mf, mf->Seg.UID, sizeof(mf->Seg.UID));
+    break;
+case 0x7384: // SegmentFilename
+    STRGETM(mf, mf->Seg.Filename, len);
+    break;
+case 0x3cb923: // PrevUID
+    if (len != sizeof(mf->Seg.PrevUID))
+        errorjmp(mf, "PrevUID size is not %d bytes", mf->Seg.PrevUID);
+    readbytes(mf, mf->Seg.PrevUID, sizeof(mf->Seg.PrevUID));
+    break;
+case 0x3c83ab: // PrevFilename
+    STRGETM(mf, mf->Seg.PrevFilename, len);
+    break;
+case 0x3eb923: // NextUID
+    if (len != sizeof(mf->Seg.NextUID))
+        errorjmp(mf, "NextUID size is not %d bytes", mf->Seg.NextUID);
+    readbytes(mf, mf->Seg.NextUID, sizeof(mf->Seg.NextUID));
+    break;
+case 0x3e83bb: // NextFilename
+    STRGETM(mf, mf->Seg.NextFilename, len);
+    break;
+case 0x2ad7b1: // TimecodeScale
+    mf->Seg.TimecodeScale = readUInt(mf, (unsigned)len);
+    if (mf->Seg.TimecodeScale == 0)
+        errorjmp(mf, "Segment timecode scale is zero");
+    break;
+case 0x4489: // Duration
+    duration = readFloat(mf, (unsigned)len);
+    break;
+case 0x4461: // DateUTC
+    mf->Seg.DateUTC = readUInt(mf, (unsigned)len);
+    mf->Seg.DateUTCValid = 1;
+    break;
+case 0x7ba9: // Title
+    STRGETM(mf, mf->Seg.Title, len);
+    break;
+case 0x4d80: // MuxingApp
+    STRGETM(mf, mf->Seg.MuxingApp, len);
+    break;
+case 0x5741: // WritingApp
+    STRGETM(mf, mf->Seg.WritingApp, len);
+    break;
+    ENDFOR(mf);
+
+    mf->Seg.Duration = mul3(duration, mf->Seg.TimecodeScale);
+}
+
+static void parseFirstCluster(MatroskaFile *mf, uint64_t toplen)
+{
+    uint64_t end = filepos(mf) + toplen;
+
+    mf->seen.Cluster = 1;
+    mf->firstTimecode = 0;
+
+    FOREACH(mf, toplen)
+case 0xe7: // Timecode
+    mf->firstTimecode += readUInt(mf, (unsigned)len);
+    break;
+case 0xa3: // BlockEx
+    readVLUInt(mf); // track number
+    mf->firstTimecode += readSInt(mf, 2);
 
-  if (mf->seen.SegmentInfo) {
-    skipbytes(mf,toplen);
+    skipbytes(mf, end - filepos(mf));
     return;
-  }
+case 0xa0: // BlockGroup
+    FOREACH(mf, len)
+case 0xa1: // Block
+    readVLUInt(mf); // track number
+    mf->firstTimecode += readSInt(mf, 2);
+
+    skipbytes(mf, end - filepos(mf));
+    return;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+}
 
-  mf->seen.SegmentInfo = 1;
-  mf->Seg.TimecodeScale = 1000000; // Default value
-
-  FOREACH(mf,toplen)
-    case 0x73a4: // SegmentUID
-      if (len!=sizeof(mf->Seg.UID))
-	errorjmp(mf,"SegmentUID size is not %d bytes",mf->Seg.UID);
-      readbytes(mf,mf->Seg.UID,sizeof(mf->Seg.UID));
-      break;
-    case 0x7384: // SegmentFilename
-      STRGETM(mf,mf->Seg.Filename,len);
-      break;
-    case 0x3cb923: // PrevUID
-      if (len!=sizeof(mf->Seg.PrevUID))
-	errorjmp(mf,"PrevUID size is not %d bytes",mf->Seg.PrevUID);
-      readbytes(mf,mf->Seg.PrevUID,sizeof(mf->Seg.PrevUID));
-      break;
-    case 0x3c83ab: // PrevFilename
-      STRGETM(mf,mf->Seg.PrevFilename,len);
-      break;
-    case 0x3eb923: // NextUID
-      if (len!=sizeof(mf->Seg.NextUID))
-	errorjmp(mf,"NextUID size is not %d bytes",mf->Seg.NextUID);
-      readbytes(mf,mf->Seg.NextUID,sizeof(mf->Seg.NextUID));
-      break;
-    case 0x3e83bb: // NextFilename
-      STRGETM(mf,mf->Seg.NextFilename,len);
-      break;
-    case 0x2ad7b1: // TimecodeScale
-      mf->Seg.TimecodeScale = readUInt(mf,(unsigned)len);
-      if (mf->Seg.TimecodeScale == 0)
-	errorjmp(mf,"Segment timecode scale is zero");
-      break;
-    case 0x4489: // Duration
-      duration = readFloat(mf,(unsigned)len);
-      break;
-    case 0x4461: // DateUTC
-      mf->Seg.DateUTC = readUInt(mf,(unsigned)len);
-      mf->Seg.DateUTCValid = 1;
-      break;
-    case 0x7ba9: // Title
-      STRGETM(mf,mf->Seg.Title,len);
-      break;
-    case 0x4d80: // MuxingApp
-      STRGETM(mf,mf->Seg.MuxingApp,len);
-      break;
-    case 0x5741: // WritingApp
-      STRGETM(mf,mf->Seg.WritingApp,len);
-      break;
-  ENDFOR(mf);
-
-  mf->Seg.Duration = mul3(duration,mf->Seg.TimecodeScale);
-}
-
-static void parseFirstCluster(MatroskaFile *mf,uint64_t toplen) {
-  uint64_t end = filepos(mf) + toplen;
-
-  mf->seen.Cluster = 1;
-  mf->firstTimecode = 0;
-
-  FOREACH(mf,toplen)
-    case 0xe7: // Timecode
-      mf->firstTimecode += readUInt(mf,(unsigned)len);
-      break;
-    case 0xa3: // BlockEx
-      readVLUInt(mf); // track number
-      mf->firstTimecode += readSInt(mf, 2);
-
-      skipbytes(mf,end - filepos(mf));
-      return;
-    case 0xa0: // BlockGroup
-      FOREACH(mf,len)
-	case 0xa1: // Block
-	  readVLUInt(mf); // track number
-	  mf->firstTimecode += readSInt(mf,2); 
-
-	  skipbytes(mf,end - filepos(mf));
-	  return;
-      ENDFOR(mf);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseVideoInfo(MatroskaFile *mf,uint64_t toplen,struct TrackInfo *ti) {
-  uint64_t v;
-  char	    dW = 0, dH = 0;
-
-  FOREACH(mf,toplen)
-    case 0x9a: // FlagInterlaced
-      ti->AV.Video.Interlaced = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x53b8: // StereoMode
-      v = readUInt(mf,(unsigned)len);
-      if (v>3)
-	errorjmp(mf,"Invalid stereo mode");
-      ti->AV.Video.StereoMode = (unsigned char)v;
-      break;
-    case 0xb0: // PixelWidth
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelWidth is too large");
-      ti->AV.Video.PixelWidth = (unsigned)v;
-      if (!dW)
-	ti->AV.Video.DisplayWidth = ti->AV.Video.PixelWidth;
-      break;
-    case 0xba: // PixelHeight
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelHeight is too large");
-      ti->AV.Video.PixelHeight = (unsigned)v;
-      if (!dH)
-	ti->AV.Video.DisplayHeight = ti->AV.Video.PixelHeight;
-      break;
-    case 0x54b0: // DisplayWidth
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"DisplayWidth is too large");
-      ti->AV.Video.DisplayWidth = (unsigned)v;
-      dW = 1;
-      break;
-    case 0x54ba: // DisplayHeight
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"DisplayHeight is too large");
-      ti->AV.Video.DisplayHeight = (unsigned)v;
-      dH = 1;
-      break;
-    case 0x54b2: // DisplayUnit
-      v = readUInt(mf,(unsigned)len);
-      if (v>2)
-	errorjmp(mf,"Invalid DisplayUnit: %d",(int)v);
-      ti->AV.Video.DisplayUnit = (unsigned char)v;
-      break;
-    case 0x54b3: // AspectRatioType
-      v = readUInt(mf,(unsigned)len);
-      if (v>2)
-	errorjmp(mf,"Invalid AspectRatioType: %d",(int)v);
-      ti->AV.Video.AspectRatioType = (unsigned char)v;
-      break;
-    case 0x54aa: // PixelCropBottom
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelCropBottom is too large");
-      ti->AV.Video.CropB = (unsigned)v;
-      break;
-    case 0x54bb: // PixelCropTop
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelCropTop is too large");
-      ti->AV.Video.CropT = (unsigned)v;
-      break;
-    case 0x54cc: // PixelCropLeft
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelCropLeft is too large");
-      ti->AV.Video.CropL = (unsigned)v;
-      break;
-    case 0x54dd: // PixelCropRight
-      v = readUInt(mf,(unsigned)len);
-      if (v>0xffffffff)
-	errorjmp(mf,"PixelCropRight is too large");
-      ti->AV.Video.CropR = (unsigned)v;
-      break;
-    case 0x2eb524: // ColourSpace
-      ti->AV.Video.ColourSpace = (unsigned)readUInt(mf,4);
-      break;
-    case 0x2fb523: // GammaValue
-      ti->AV.Video.GammaValue = readFloat(mf,(unsigned)len);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseAudioInfo(MatroskaFile *mf,uint64_t toplen,struct TrackInfo *ti) {
-  uint64_t   v;
-
-  FOREACH(mf,toplen)
-    case 0xb5: // SamplingFrequency
-      ti->AV.Audio.SamplingFreq = readFloat(mf,(unsigned)len);
-      break;
-    case 0x78b5: // OutputSamplingFrequency
-      ti->AV.Audio.OutputSamplingFreq = readFloat(mf,(unsigned)len);
-      break;
-    case 0x9f: // Channels
-      v = readUInt(mf,(unsigned)len);
-      if (v<1 || v>255)
-	errorjmp(mf,"Invalid Channels value");
-      ti->AV.Audio.Channels = (unsigned char)v;
-      break;
-    case 0x7d7b: // ChannelPositions
-      skipbytes(mf,len);
-      break;
-    case 0x6264: // BitDepth
-      v = readUInt(mf,(unsigned)len);
+static void parseVideoInfo(MatroskaFile *mf, uint64_t toplen, struct TrackInfo *ti)
+{
+    uint64_t v;
+    char	    dW = 0, dH = 0;
+
+    FOREACH(mf, toplen)
+case 0x9a: // FlagInterlaced
+    ti->AV.Video.Interlaced = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x53b8: // StereoMode
+    v = readUInt(mf, (unsigned)len);
+    if (v > 3)
+        errorjmp(mf, "Invalid stereo mode");
+    ti->AV.Video.StereoMode = (unsigned char)v;
+    break;
+case 0xb0: // PixelWidth
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelWidth is too large");
+    ti->AV.Video.PixelWidth = (unsigned)v;
+    if (!dW)
+        ti->AV.Video.DisplayWidth = ti->AV.Video.PixelWidth;
+    break;
+case 0xba: // PixelHeight
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelHeight is too large");
+    ti->AV.Video.PixelHeight = (unsigned)v;
+    if (!dH)
+        ti->AV.Video.DisplayHeight = ti->AV.Video.PixelHeight;
+    break;
+case 0x54b0: // DisplayWidth
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "DisplayWidth is too large");
+    ti->AV.Video.DisplayWidth = (unsigned)v;
+    dW = 1;
+    break;
+case 0x54ba: // DisplayHeight
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "DisplayHeight is too large");
+    ti->AV.Video.DisplayHeight = (unsigned)v;
+    dH = 1;
+    break;
+case 0x54b2: // DisplayUnit
+    v = readUInt(mf, (unsigned)len);
+    if (v > 2)
+        errorjmp(mf, "Invalid DisplayUnit: %d", (int)v);
+    ti->AV.Video.DisplayUnit = (unsigned char)v;
+    break;
+case 0x54b3: // AspectRatioType
+    v = readUInt(mf, (unsigned)len);
+    if (v > 2)
+        errorjmp(mf, "Invalid AspectRatioType: %d", (int)v);
+    ti->AV.Video.AspectRatioType = (unsigned char)v;
+    break;
+case 0x54aa: // PixelCropBottom
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelCropBottom is too large");
+    ti->AV.Video.CropB = (unsigned)v;
+    break;
+case 0x54bb: // PixelCropTop
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelCropTop is too large");
+    ti->AV.Video.CropT = (unsigned)v;
+    break;
+case 0x54cc: // PixelCropLeft
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelCropLeft is too large");
+    ti->AV.Video.CropL = (unsigned)v;
+    break;
+case 0x54dd: // PixelCropRight
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "PixelCropRight is too large");
+    ti->AV.Video.CropR = (unsigned)v;
+    break;
+case 0x2eb524: // ColourSpace
+    ti->AV.Video.ColourSpace = (unsigned)readUInt(mf, 4);
+    break;
+case 0x2fb523: // GammaValue
+    ti->AV.Video.GammaValue = readFloat(mf, (unsigned)len);
+    break;
+    ENDFOR(mf);
+}
+
+static void parseAudioInfo(MatroskaFile *mf, uint64_t toplen, struct TrackInfo *ti)
+{
+    uint64_t   v;
+
+    FOREACH(mf, toplen)
+case 0xb5: // SamplingFrequency
+    ti->AV.Audio.SamplingFreq = readFloat(mf, (unsigned)len);
+    break;
+case 0x78b5: // OutputSamplingFrequency
+    ti->AV.Audio.OutputSamplingFreq = readFloat(mf, (unsigned)len);
+    break;
+case 0x9f: // Channels
+    v = readUInt(mf, (unsigned)len);
+    if (v < 1 || v > 255)
+        errorjmp(mf, "Invalid Channels value");
+    ti->AV.Audio.Channels = (unsigned char)v;
+    break;
+case 0x7d7b: // ChannelPositions
+    skipbytes(mf, len);
+    break;
+case 0x6264: // BitDepth
+    v = readUInt(mf, (unsigned)len);
 #if 0
-      if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI"))
-	errorjmp(mf,"Invalid BitDepth: %d",(int)v);
+    if ((v < 1 || v > 255) && !IsWritingApp(mf, "AVI-Mux GUI"))
+        errorjmp(mf, "Invalid BitDepth: %d", (int)v);
 #endif
-      ti->AV.Audio.BitDepth = (unsigned char)v;
-      break;
-  ENDFOR(mf);
+    ti->AV.Audio.BitDepth = (unsigned char)v;
+    break;
+    ENDFOR(mf);
 
-  if (ti->AV.Audio.Channels == 0)
-    ti->AV.Audio.Channels = 1;
-  if (mkv_TruncFloat(ti->AV.Audio.SamplingFreq) == 0)
-    ti->AV.Audio.SamplingFreq = mkfi(8000);
-  if (mkv_TruncFloat(ti->AV.Audio.OutputSamplingFreq)==0)
-    ti->AV.Audio.OutputSamplingFreq = ti->AV.Audio.SamplingFreq;
+    if (ti->AV.Audio.Channels == 0)
+        ti->AV.Audio.Channels = 1;
+    if (mkv_TruncFloat(ti->AV.Audio.SamplingFreq) == 0)
+        ti->AV.Audio.SamplingFreq = mkfi(8000);
+    if (mkv_TruncFloat(ti->AV.Audio.OutputSamplingFreq) == 0)
+        ti->AV.Audio.OutputSamplingFreq = ti->AV.Audio.SamplingFreq;
 }
 
-static void CopyStr(char **src,char **dst) {
-  size_t l;
+static void CopyStr(char **src, char **dst)
+{
+    size_t l;
 
-  if (!*src)
-    return;
+    if (!*src)
+        return;
 
-  l = strlen(*src)+1;
-  memcpy(*dst,*src,l);
-  *src = *dst;
-  *dst += l;
-}
-
-static void parseTrackEntry(MatroskaFile *mf,uint64_t toplen) {
-  struct TrackInfo  t,*tp,**tpp;
-  uint64_t	    v;
-  char		    *cp = NULL, *cs = NULL;
-  size_t	    cplen = 0, cslen = 0, cpadd = 0;
-  unsigned	    CompScope, num_comp = 0;
-
-  if (mf->nTracks >= MAX_TRACKS)
-    errorjmp(mf,"Too many tracks.");
-
-  // clear track info
-  memset(&t,0,sizeof(t));
-
-  // fill default values
-  t.Enabled = 1;
-  t.Default = 1;
-  t.Lacing = 1;
-  t.TimecodeScale = mkfi(1);
-  t.DecodeAll = 1;
-
-  FOREACH(mf,toplen)
-    case 0xd7: // TrackNumber
-      v = readUInt(mf,(unsigned)len);
-      if (v>255)
-	errorjmp(mf,"Track number is >255 (%d)",(int)v);
-      t.Number = (unsigned char)v;
-      break;
-    case 0x73c5: // TrackUID
-      t.UID = readUInt(mf,(unsigned)len);
-      break;
-    case 0x83: // TrackType
-      v = readUInt(mf,(unsigned)len);
-      if (v<1 || v>254)
-	errorjmp(mf,"Invalid track type: %d",(int)v);
-      t.Type = (unsigned char)v;
-      break;
-    case 0xb9: // Enabled
-      t.Enabled = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x88: // Default
-      t.Default = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x9c: // Lacing
-      t.Lacing = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x6de7: // MinCache
-      v = readUInt(mf,(unsigned)len);
-      if (v > 0xffffffff)
-	errorjmp(mf,"MinCache is too large");
-      t.MinCache = (unsigned)v;
-      break;
-    case 0x6df8: // MaxCache
-      v = readUInt(mf,(unsigned)len);
-      if (v > 0xffffffff)
-	errorjmp(mf,"MaxCache is too large");
-      t.MaxCache = (unsigned)v;
-      break;
-    case 0x23e383: // DefaultDuration
-      t.DefaultDuration = readUInt(mf,(unsigned)len);
-      break;
-    case 0x23314f: // TrackTimecodeScale
-      t.TimecodeScale = readFloat(mf,(unsigned)len);
-      break;
-    case 0x55ee: // MaxBlockAdditionID
-      t.MaxBlockAdditionID = (unsigned)readUInt(mf,(unsigned)len);
-      break;
-    case 0x536e: // Name
-      if (t.Name)
-	errorjmp(mf,"Duplicate Track Name");
-      STRGETA(mf,t.Name,len);
-      break;
-    case 0x22b59c: // Language
-      readLangCC(mf, len, t.Language);
-      break;
-    case 0x86: // CodecID
-      if (t.CodecID)
-	errorjmp(mf,"Duplicate CodecID");
-      STRGETA(mf,t.CodecID,len);
-      break;
-    case 0x63a2: // CodecPrivate
-      if (cp)
-	errorjmp(mf,"Duplicate CodecPrivate");
-      cplen = (unsigned)len;
-      if (len > 262144) { // 256KB
-	cp = mf->cpbuf = mf->cache->memalloc(mf->cache, cplen);
-	if (!cp)
-	  errorjmp(mf,"Out of memory");
-      }
-      else
-	cp = alloca(cplen);
-      readbytes(mf,cp,(int)cplen);
-      break;
-    case 0x258688: // CodecName
-      skipbytes(mf,len);
-      break;
-    case 0x3a9697: // CodecSettings
-      skipbytes(mf,len);
-      break;
-    case 0x3b4040: // CodecInfoURL
-      skipbytes(mf,len);
-      break;
-    case 0x26b240: // CodecDownloadURL
-      skipbytes(mf,len);
-      break;
-    case 0xaa: // CodecDecodeAll
-      t.DecodeAll = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x6fab: // TrackOverlay
-      v = readUInt(mf,(unsigned)len);
-      if (v>255)
-	errorjmp(mf,"Track number in TrackOverlay is too large: %d",(int)v);
-      t.TrackOverlay = (unsigned char)v;
-      break;
-    case 0xe0: // VideoInfo
-      parseVideoInfo(mf,len,&t);
-      break;
-    case 0xe1: // AudioInfo
-      parseAudioInfo(mf,len,&t);
-      break;
-    case 0x6d80: // ContentEncodings
-      FOREACH(mf,len)
-	case 0x6240: // ContentEncoding
-          // fill in defaults
-	  t.CompEnabled = 1;
-	  t.CompMethod = COMP_ZLIB;
-	  CompScope = 1;
-	  if (++num_comp > 1)
-	    return; // only one compression layer supported
-	  FOREACH(mf,len)
-	    case 0x5031: // ContentEncodingOrder
-	      readUInt(mf,(unsigned)len);
-	      break;
-	    case 0x5032: // ContentEncodingScope
-	      CompScope = (unsigned)readUInt(mf,(unsigned)len);
-	      break;
-	    case 0x5033: // ContentEncodingType
-	      if (readUInt(mf,(unsigned)len) != 0)
-		return; // encryption is not supported
-	      break;
-	    case 0x5034: // ContentCompression
-	      FOREACH(mf,len)
-		case 0x4254: // ContentCompAlgo
-		  v = readUInt(mf,(unsigned)len);
-		  t.CompEnabled = 1;
-		  switch (v) {
-		    case 0: // Zlib
-		      t.CompMethod = COMP_ZLIB;
-		      break;
-		    case 3: // prepend fixed data
-		      t.CompMethod = COMP_PREPEND;
-		      break;
-		    default:
-		      return; // unsupported compression, skip track
-		  }
-		  break;
-		case 0x4255: // ContentCompSettings
-		  if (len > 256)
-		    return;
-		  cslen = (unsigned)len;
-		  cs = alloca(cslen);
-		  readbytes(mf, cs, (int)cslen);
-		  break;
-	      ENDFOR(mf);
-	      break;
-	      // TODO Implement Encryption/Signatures
-	  ENDFOR(mf);
-	  break;
-      ENDFOR(mf);
-      break;
-  ENDFOR(mf);
-
-  // validate track info
-  if (!t.CodecID)
-    errorjmp(mf,"Track has no Codec ID");
-
-  if (t.UID != 0) {
-    unsigned  i;
-    for (i = 0; i < mf->nTracks; ++i)
-      if (mf->Tracks[i]->UID == t.UID) // duplicate track entry
-	return;
-  }
+    l = strlen(*src) + 1;
+    memcpy(*dst, *src, l);
+    *src = *dst;
+    *dst += l;
+}
+
+static void parseTrackEntry(MatroskaFile *mf, uint64_t toplen)
+{
+    struct TrackInfo  t, *tp, **tpp;
+    uint64_t	    v;
+    char		    *cp = NULL, *cs = NULL;
+    size_t	    cplen = 0, cslen = 0, cpadd = 0;
+    unsigned	    CompScope, num_comp = 0;
+
+    if (mf->nTracks >= MAX_TRACKS)
+        errorjmp(mf, "Too many tracks.");
+
+    // clear track info
+    memset(&t, 0, sizeof(t));
+
+    // fill default values
+    t.Enabled = 1;
+    t.Default = 1;
+    t.Lacing = 1;
+    t.TimecodeScale = mkfi(1);
+    t.DecodeAll = 1;
+
+    FOREACH(mf, toplen)
+case 0xd7: // TrackNumber
+    v = readUInt(mf, (unsigned)len);
+    if (v > 255)
+        errorjmp(mf, "Track number is >255 (%d)", (int)v);
+    t.Number = (unsigned char)v;
+    break;
+case 0x73c5: // TrackUID
+    t.UID = readUInt(mf, (unsigned)len);
+    break;
+case 0x83: // TrackType
+    v = readUInt(mf, (unsigned)len);
+    if (v < 1 || v > 254)
+        errorjmp(mf, "Invalid track type: %d", (int)v);
+    t.Type = (unsigned char)v;
+    break;
+case 0xb9: // Enabled
+    t.Enabled = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x88: // Default
+    t.Default = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x9c: // Lacing
+    t.Lacing = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x6de7: // MinCache
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "MinCache is too large");
+    t.MinCache = (unsigned)v;
+    break;
+case 0x6df8: // MaxCache
+    v = readUInt(mf, (unsigned)len);
+    if (v > 0xffffffff)
+        errorjmp(mf, "MaxCache is too large");
+    t.MaxCache = (unsigned)v;
+    break;
+case 0x23e383: // DefaultDuration
+    t.DefaultDuration = readUInt(mf, (unsigned)len);
+    break;
+case 0x23314f: // TrackTimecodeScale
+    t.TimecodeScale = readFloat(mf, (unsigned)len);
+    break;
+case 0x55ee: // MaxBlockAdditionID
+    t.MaxBlockAdditionID = (unsigned)readUInt(mf, (unsigned)len);
+    break;
+case 0x536e: // Name
+    if (t.Name)
+        errorjmp(mf, "Duplicate Track Name");
+    STRGETA(mf, t.Name, len);
+    break;
+case 0x22b59c: // Language
+    readLangCC(mf, len, t.Language);
+    break;
+case 0x86: // CodecID
+    if (t.CodecID)
+        errorjmp(mf, "Duplicate CodecID");
+    STRGETA(mf, t.CodecID, len);
+    break;
+case 0x63a2: // CodecPrivate
+    if (cp)
+        errorjmp(mf, "Duplicate CodecPrivate");
+    cplen = (unsigned)len;
+    if (len > 262144) { // 256KB
+        cp = mf->cpbuf = mf->cache->memalloc(mf->cache, cplen);
+        if (!cp)
+            errorjmp(mf, "Out of memory");
+    }
+    else
+        cp = alloca(cplen);
+    readbytes(mf, cp, (int)cplen);
+    break;
+case 0x258688: // CodecName
+    skipbytes(mf, len);
+    break;
+case 0x3a9697: // CodecSettings
+    skipbytes(mf, len);
+    break;
+case 0x3b4040: // CodecInfoURL
+    skipbytes(mf, len);
+    break;
+case 0x26b240: // CodecDownloadURL
+    skipbytes(mf, len);
+    break;
+case 0xaa: // CodecDecodeAll
+    t.DecodeAll = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x6fab: // TrackOverlay
+    v = readUInt(mf, (unsigned)len);
+    if (v > 255)
+        errorjmp(mf, "Track number in TrackOverlay is too large: %d", (int)v);
+    t.TrackOverlay = (unsigned char)v;
+    break;
+case 0xe0: // VideoInfo
+    parseVideoInfo(mf, len, &t);
+    break;
+case 0xe1: // AudioInfo
+    parseAudioInfo(mf, len, &t);
+    break;
+case 0x6d80: // ContentEncodings
+    FOREACH(mf, len)
+case 0x6240: // ContentEncoding
+    // fill in defaults
+    t.CompEnabled = 1;
+    t.CompMethod = COMP_ZLIB;
+    CompScope = 1;
+    if (++num_comp > 1)
+        return; // only one compression layer supported
+    FOREACH(mf, len)
+case 0x5031: // ContentEncodingOrder
+    readUInt(mf, (unsigned)len);
+    break;
+case 0x5032: // ContentEncodingScope
+    CompScope = (unsigned)readUInt(mf, (unsigned)len);
+    break;
+case 0x5033: // ContentEncodingType
+    if (readUInt(mf, (unsigned)len) != 0)
+        return; // encryption is not supported
+    break;
+case 0x5034: // ContentCompression
+    FOREACH(mf, len)
+case 0x4254: // ContentCompAlgo
+    v = readUInt(mf, (unsigned)len);
+    t.CompEnabled = 1;
+    switch (v) {
+    case 0: // Zlib
+        t.CompMethod = COMP_ZLIB;
+        break;
+    case 3: // prepend fixed data
+        t.CompMethod = COMP_PREPEND;
+        break;
+    default:
+        return; // unsupported compression, skip track
+    }
+    break;
+case 0x4255: // ContentCompSettings
+    if (len > 256)
+        return;
+    cslen = (unsigned)len;
+    cs = alloca(cslen);
+    readbytes(mf, cs, (int)cslen);
+    break;
+    ENDFOR(mf);
+    break;
+    // TODO Implement Encryption/Signatures
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+
+    // validate track info
+    if (!t.CodecID)
+        errorjmp(mf, "Track has no Codec ID");
+
+    if (t.UID != 0) {
+        unsigned  i;
+        for (i = 0; i < mf->nTracks; ++i)
+            if (mf->Tracks[i]->UID == t.UID) // duplicate track entry
+                return;
+    }
 
 #ifdef MATROSKA_COMPRESSION_SUPPORT
-  // handle compressed CodecPrivate
-  if (t.CompEnabled && t.CompMethod == COMP_ZLIB && (CompScope & 2) && cplen > 0) {
-    z_stream  zs;
-    Bytef     tmp[64], *ncp;
-    int	      code;
-    uLong     ncplen;
+    // handle compressed CodecPrivate
+    if (t.CompEnabled && t.CompMethod == COMP_ZLIB && (CompScope & 2) && cplen > 0) {
+        z_stream  zs;
+        Bytef     tmp[64], *ncp;
+        int	      code;
+        uLong     ncplen;
 
-    memset(&zs,0,sizeof(zs));
-    if (inflateInit(&zs) != Z_OK)
-      errorjmp(mf, "inflateInit failed");
+        memset(&zs, 0, sizeof(zs));
+        if (inflateInit(&zs) != Z_OK)
+            errorjmp(mf, "inflateInit failed");
 
-    zs.next_in = (Bytef *)cp;
-    zs.avail_in = cplen;
+        zs.next_in = (Bytef *)cp;
+        zs.avail_in = cplen;
 
-    do {
-      zs.next_out = tmp;
-      zs.avail_out = sizeof(tmp);
+        do {
+            zs.next_out = tmp;
+            zs.avail_out = sizeof(tmp);
 
-      code = inflate(&zs, Z_NO_FLUSH);
-    } while (code == Z_OK);
+            code = inflate(&zs, Z_NO_FLUSH);
+        } while (code == Z_OK);
 
-    if (code != Z_STREAM_END)
-      errorjmp(mf, "invalid compressed data in CodecPrivate");
+        if (code != Z_STREAM_END)
+            errorjmp(mf, "invalid compressed data in CodecPrivate");
 
-    ncplen = zs.total_out;
-    ncp = alloca(ncplen);
+        ncplen = zs.total_out;
+        ncp = alloca(ncplen);
 
-    inflateReset(&zs);
+        inflateReset(&zs);
 
-    zs.next_in = (Bytef *)cp;
-    zs.avail_in = cplen;
-    zs.next_out = ncp;
-    zs.avail_out = ncplen;
+        zs.next_in = (Bytef *)cp;
+        zs.avail_in = cplen;
+        zs.next_out = ncp;
+        zs.avail_out = ncplen;
 
-    if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
-      errorjmp(mf, "inflate failed");
+        if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
+            errorjmp(mf, "inflate failed");
 
-    inflateEnd(&zs);
+        inflateEnd(&zs);
 
-    cp = (char *)ncp;
-    cplen = ncplen;
-  }
+        cp = (char *)ncp;
+        cplen = ncplen;
+    }
 #endif
 
-  if (t.CompEnabled && !(CompScope & 1)) {
-    t.CompEnabled = 0;
-    cslen = 0;
-  }
+    if (t.CompEnabled && !(CompScope & 1)) {
+        t.CompEnabled = 0;
+        cslen = 0;
+    }
 
-  // allocate new track
-  tpp = AGET(mf,Tracks);
+    // allocate new track
+    tpp = AGET(mf, Tracks);
 
-  // copy strings
-  if (t.Name)
-    cpadd += strlen(t.Name)+1;
-  if (t.CodecID)
-    cpadd += strlen(t.CodecID)+1;
+    // copy strings
+    if (t.Name)
+        cpadd += strlen(t.Name) + 1;
+    if (t.CodecID)
+        cpadd += strlen(t.CodecID) + 1;
 
-  tp = mf->cache->memalloc(mf->cache,sizeof(*tp) + cplen + cslen + cpadd);
-  if (tp == NULL)
-    errorjmp(mf,"Out of memory");
+    tp = mf->cache->memalloc(mf->cache, sizeof(*tp) + cplen + cslen + cpadd);
+    if (tp == NULL)
+        errorjmp(mf, "Out of memory");
 
-  memcpy(tp,&t,sizeof(*tp));
-  if (cplen) {
-    tp->CodecPrivate = tp+1;
-    tp->CodecPrivateSize = (unsigned)cplen;
-    memcpy(tp->CodecPrivate,cp,cplen);
-  }
-  if (cslen) {
-    tp->CompMethodPrivate = (char *)(tp+1) + cplen;
-    tp->CompMethodPrivateSize = (unsigned)cslen;
-    memcpy(tp->CompMethodPrivate, cs, cslen);
-  }
+    memcpy(tp, &t, sizeof(*tp));
+    if (cplen) {
+        tp->CodecPrivate = tp + 1;
+        tp->CodecPrivateSize = (unsigned)cplen;
+        memcpy(tp->CodecPrivate, cp, cplen);
+    }
+    if (cslen) {
+        tp->CompMethodPrivate = (char *)(tp + 1) + cplen;
+        tp->CompMethodPrivateSize = (unsigned)cslen;
+        memcpy(tp->CompMethodPrivate, cs, cslen);
+    }
 
-  cp = (char*)(tp+1) + cplen + cslen;
-  CopyStr(&tp->Name,&cp);
-  CopyStr(&tp->CodecID,&cp);
+    cp = (char *)(tp + 1) + cplen + cslen;
+    CopyStr(&tp->Name, &cp);
+    CopyStr(&tp->CodecID, &cp);
 
-  // set default language
-  if (!tp->Language[0])
-    memcpy(tp->Language, "eng", 4);
+    // set default language
+    if (!tp->Language[0])
+        memcpy(tp->Language, "eng", 4);
 
-  *tpp = tp;
+    *tpp = tp;
 }
 
-static void parseTracks(MatroskaFile *mf,uint64_t toplen) {
-  mf->seen.Tracks = 1;
-  mf->cpbuf = NULL;
-  FOREACH(mf,toplen)
-    case 0xae: // TrackEntry
-      parseTrackEntry(mf,len);
-      mf->cache->memfree(mf->cache, mf->cpbuf);
-      mf->cpbuf = NULL;
-      break;
-  ENDFOR(mf);
+static void parseTracks(MatroskaFile *mf, uint64_t toplen)
+{
+    mf->seen.Tracks = 1;
+    mf->cpbuf = NULL;
+    FOREACH(mf, toplen)
+case 0xae: // TrackEntry
+    parseTrackEntry(mf, len);
+    mf->cache->memfree(mf->cache, mf->cpbuf);
+    mf->cpbuf = NULL;
+    break;
+    ENDFOR(mf);
+}
+
+static void addCue(MatroskaFile *mf, uint64_t pos, uint64_t timecode)
+{
+    struct Cue  *cc = AGET(mf, Cues);
+    cc->Time = timecode;
+    cc->Position = pos;
+    cc->Track = 0;
+    cc->Block = 0;
 }
 
-static void addCue(MatroskaFile *mf,uint64_t pos,uint64_t timecode) {
-  struct Cue  *cc = AGET(mf,Cues);
-  cc->Time = timecode;
-  cc->Position = pos;
-  cc->Track = 0;
-  cc->Block = 0;
+static void fixupCues(MatroskaFile *mf)
+{
+    // adjust cues, shift cues if file does not start at 0
+    unsigned  i;
+    int64_t  adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
+
+    for (i = 0; i < mf->nCues; ++i) {
+        mf->Cues[i].Time *= mf->Seg.TimecodeScale;
+        mf->Cues[i].Time -= adjust;
+    }
 }
 
-static void fixupCues(MatroskaFile *mf) {
-  // adjust cues, shift cues if file does not start at 0
-  unsigned  i;
-  int64_t  adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
+static void parseCues(MatroskaFile *mf, uint64_t toplen)
+{
+    jmp_buf     jb;
+    uint64_t   v;
+    struct Cue  cc;
+    unsigned    i, j, k;
 
-  for (i=0;i<mf->nCues;++i) {
-    mf->Cues[i].Time *= mf->Seg.TimecodeScale;
-    mf->Cues[i].Time -= adjust;
-  }
+    mf->seen.Cues = 1;
+    mf->nCues = 0;
+    cc.Block = 0;
+
+    memcpy(&jb, &mf->jb, sizeof(jb));
+
+    if (setjmp(mf->jb)) {
+        memcpy(&mf->jb, &jb, sizeof(jb));
+        mf->nCues = 0;
+        mf->seen.Cues = 0;
+        return;
+    }
+
+    FOREACH(mf, toplen)
+case 0xbb: // CuePoint
+    FOREACH(mf, len)
+case 0xb3: // CueTime
+    cc.Time = readUInt(mf, (unsigned)len);
+    break;
+case 0xb7: // CueTrackPositions
+    FOREACH(mf, len)
+case 0xf7: // CueTrack
+    v = readUInt(mf, (unsigned)len);
+    if (v > 255)
+        errorjmp(mf, "CueTrack points to an invalid track: %d", (int)v);
+    cc.Track = (unsigned char)v;
+    break;
+case 0xf1: // CueClusterPosition
+    cc.Position = readUInt(mf, (unsigned)len);
+    break;
+case 0x5378: // CueBlockNumber
+    cc.Block = readUInt(mf, (unsigned)len);
+    break;
+case 0xea: // CodecState
+    readUInt(mf, (unsigned)len);
+    break;
+case 0xdb: // CueReference
+    FOREACH(mf, len)
+case 0x96: // CueRefTime
+    readUInt(mf, (unsigned)len);
+    break;
+case 0x97: // CueRefCluster
+    readUInt(mf, (unsigned)len);
+    break;
+case 0x535f: // CueRefNumber
+    readUInt(mf, (unsigned)len);
+    break;
+case 0xeb: // CueRefCodecState
+    readUInt(mf, (unsigned)len);
+    break;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+
+    if (mf->nCues == 0 && mf->pCluster - mf->pSegment != cc.Position)
+        addCue(mf, mf->pCluster - mf->pSegment, mf->firstTimecode);
+
+    memcpy(AGET(mf, Cues), &cc, sizeof(cc));
+    break;
+    ENDFOR(mf);
+
+    memcpy(&mf->jb, &jb, sizeof(jb));
+
+    ARELEASE(mf, mf, Cues);
+
+    // bubble sort the cues and fuck the losers that write unordered cues
+    if (mf->nCues > 0)
+        for (i = mf->nCues - 1, k = 1; i > 0 && k > 0; --i)
+            for (j = k = 0; j < i; ++j)
+                if (mf->Cues[j].Time > mf->Cues[j + 1].Time) {
+                    struct Cue tmp = mf->Cues[j + 1];
+                    mf->Cues[j + 1] = mf->Cues[j];
+                    mf->Cues[j] = tmp;
+                    ++k;
+                }
+}
+
+static void parseAttachment(MatroskaFile *mf, uint64_t toplen)
+{
+    struct Attachment a, *pa;
+
+    memset(&a, 0, sizeof(a));
+    FOREACH(mf, toplen)
+case 0x467e: // Description
+    STRGETA(mf, a.Description, len);
+    break;
+case 0x466e: // Name
+    STRGETA(mf, a.Name, len);
+    break;
+case 0x4660: // MimeType
+    STRGETA(mf, a.MimeType, len);
+    break;
+case 0x46ae: // UID
+    a.UID = readUInt(mf, (unsigned)len);
+    break;
+case 0x465c: // Data
+    a.Position = filepos(mf);
+    a.Length = len;
+    skipbytes(mf, len);
+    break;
+    ENDFOR(mf);
+
+    if (!a.Position)
+        return;
+
+    pa = AGET(mf, Attachments);
+    memcpy(pa, &a, sizeof(a));
+
+    if (a.Description)
+        pa->Description = mystrdup(mf->cache, a.Description);
+    if (a.Name)
+        pa->Name = mystrdup(mf->cache, a.Name);
+    if (a.MimeType)
+        pa->MimeType = mystrdup(mf->cache, a.MimeType);
+}
+
+static void parseAttachments(MatroskaFile *mf, uint64_t toplen)
+{
+    mf->seen.Attachments = 1;
+
+    FOREACH(mf, toplen)
+case 0x61a7: // AttachedFile
+    parseAttachment(mf, len);
+    break;
+    ENDFOR(mf);
 }
 
-static void parseCues(MatroskaFile *mf,uint64_t toplen) {
-  jmp_buf     jb;
-  uint64_t   v;
-  struct Cue  cc;
-  unsigned    i,j,k;
+static void parseChapter(MatroskaFile *mf, uint64_t toplen, struct Chapter *parent)
+{
+    struct ChapterDisplay	*disp;
+    struct ChapterProcess	*proc;
+    struct ChapterCommand	*cmd;
+    struct Chapter	*ch = ASGET(mf, parent, Children);
+
+    memset(ch, 0, sizeof(*ch));
+
+    ch->Enabled = 1;
+
+    FOREACH(mf, toplen)
+case 0x73c4: // ChapterUID
+    ch->UID = readUInt(mf, (unsigned)len);
+    break;
+case 0x6e67: // ChapterSegmentUID
+    if (len != sizeof(ch->SegmentUID))
+        skipbytes(mf, len);
+    else
+        readbytes(mf, ch->SegmentUID, sizeof(ch->SegmentUID));
+    break;
+case 0x91: // ChapterTimeStart
+    ch->Start = readUInt(mf, (unsigned)len);
+    break;
+case 0x92: // ChapterTimeEnd
+    ch->End = readUInt(mf, (unsigned)len);
+    break;
+case 0x98: // ChapterFlagHidden
+    ch->Hidden = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x4598: // ChapterFlagEnabled
+    ch->Enabled = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x8f: // ChapterTrack
+    FOREACH(mf, len)
+case 0x89: // ChapterTrackNumber
+    *(uint64_t *)(ASGET(mf, ch, Tracks)) = readUInt(mf, (unsigned)len);
+    break;
+    ENDFOR(mf);
+    break;
+case 0x80: // ChapterDisplay
+    disp = NULL;
+
+    FOREACH(mf, len)
+case 0x85: // ChapterString
+    if (disp == NULL) {
+        disp = ASGET(mf, ch, Display);
+        memset(disp, 0, sizeof(*disp));
+    }
+    if (disp->String)
+        skipbytes(mf, len); // Ignore duplicate string
+    else
+        STRGETM(mf, disp->String, len);
+    break;
+case 0x437c: // ChapterLanguage
+    if (disp == NULL) {
+        disp = ASGET(mf, ch, Display);
+        memset(disp, 0, sizeof(*disp));
+    }
+    readLangCC(mf, len, disp->Language);
+    break;
+case 0x437e: // ChapterCountry
+    if (disp == NULL) {
+        disp = ASGET(mf, ch, Display);
+        memset(disp, 0, sizeof(*disp));
+    }
+    readLangCC(mf, len, disp->Country);
+    break;
+    ENDFOR(mf);
+
+    if (disp && !disp->String)
+        --ch->nDisplay;
+    break;
+case 0x6944: // ChapProcess
+    proc = NULL;
+
+    FOREACH(mf, len)
+case 0x6955: // ChapProcessCodecID
+    if (proc == NULL) {
+        proc = ASGET(mf, ch, Process);
+        memset(proc, 0, sizeof(*proc));
+    }
+    proc->CodecID = (unsigned)readUInt(mf, (unsigned)len);
+    break;
+case 0x450d: // ChapProcessPrivate
+    if (proc == NULL) {
+        proc = ASGET(mf, ch, Process);
+        memset(proc, 0, sizeof(*proc));
+    }
+    if (proc->CodecPrivate)
+        skipbytes(mf, len);
+    else {
+        proc->CodecPrivateLength = (unsigned)len;
+        STRGETM(mf, proc->CodecPrivate, len);
+    }
+    break;
+case 0x6911: // ChapProcessCommand
+    if (proc == NULL) {
+        proc = ASGET(mf, ch, Process);
+        memset(proc, 0, sizeof(*proc));
+    }
+
+    cmd = NULL;
 
-  mf->seen.Cues = 1;
-  mf->nCues = 0;
-  cc.Block = 0;
+    FOREACH(mf, len)
+case 0x6922: // ChapterCommandTime
+    if (cmd == NULL) {
+        cmd = ASGET(mf, proc, Commands);
+        memset(cmd, 0, sizeof(*cmd));
+    }
+    cmd->Time = (unsigned)readUInt(mf, (unsigned)len);
+    break;
+case 0x6933: // ChapterCommandString
+    if (cmd == NULL) {
+        cmd = ASGET(mf, proc, Commands);
+        memset(cmd, 0, sizeof(*cmd));
+    }
+    if (cmd->Command)
+        skipbytes(mf, len);
+    else {
+        cmd->CommandLength = (unsigned)len;
+        STRGETM(mf, cmd->Command, len);
+    }
+    break;
+    ENDFOR(mf);
 
-  memcpy(&jb,&mf->jb,sizeof(jb));
+    if (cmd && !cmd->Command)
+        --proc->nCommands;
+    break;
+    ENDFOR(mf);
 
-  if (setjmp(mf->jb)) {
-    memcpy(&mf->jb,&jb,sizeof(jb));
-    mf->nCues = 0;
-    mf->seen.Cues = 0;
-    return;
-  }
+    if (proc && !proc->nCommands)
+        --ch->nProcess;
+    break;
+case 0xb6: // Nested ChapterAtom
+    parseChapter(mf, len, ch);
+    break;
+    ENDFOR(mf);
 
-  FOREACH(mf,toplen)
-    case 0xbb: // CuePoint
-      FOREACH(mf,len)
-	case 0xb3: // CueTime
-	  cc.Time = readUInt(mf,(unsigned)len);
-	  break;
-	case 0xb7: // CueTrackPositions
-	  FOREACH(mf,len)
-	    case 0xf7: // CueTrack
-	      v = readUInt(mf,(unsigned)len);
-	      if (v>255)
-		errorjmp(mf,"CueTrack points to an invalid track: %d",(int)v);
-	      cc.Track = (unsigned char)v;
-	      break;
-	    case 0xf1: // CueClusterPosition
-	      cc.Position = readUInt(mf,(unsigned)len);
-	      break;
-	    case 0x5378: // CueBlockNumber
-	      cc.Block = readUInt(mf,(unsigned)len);
-	      break;
-	    case 0xea: // CodecState
-	      readUInt(mf,(unsigned)len);
-	      break;
-	    case 0xdb: // CueReference
-	      FOREACH(mf,len)
-		case 0x96: // CueRefTime
-		  readUInt(mf,(unsigned)len);
-		  break;
-		case 0x97: // CueRefCluster
-		  readUInt(mf,(unsigned)len);
-		  break;
-		case 0x535f: // CueRefNumber
-		  readUInt(mf,(unsigned)len);
-		  break;
-		case 0xeb: // CueRefCodecState
-		  readUInt(mf,(unsigned)len);
-		  break;
-	      ENDFOR(mf);
-	      break;
-	  ENDFOR(mf);
-	  break;
-      ENDFOR(mf);
-
-      if (mf->nCues == 0 && mf->pCluster - mf->pSegment != cc.Position)
-	addCue(mf,mf->pCluster - mf->pSegment,mf->firstTimecode);
-
-      memcpy(AGET(mf,Cues),&cc,sizeof(cc));
-      break;
-  ENDFOR(mf);
-
-  memcpy(&mf->jb,&jb,sizeof(jb));
-
-  ARELEASE(mf,mf,Cues);
-
-  // bubble sort the cues and fuck the losers that write unordered cues
-  if (mf->nCues > 0)
-    for (i = mf->nCues - 1, k = 1; i > 0 && k > 0; --i)
-      for (j = k = 0; j < i; ++j)
-	if (mf->Cues[j].Time > mf->Cues[j+1].Time) {
-	  struct Cue tmp = mf->Cues[j+1];
-	  mf->Cues[j+1] = mf->Cues[j];
-	  mf->Cues[j] = tmp;
-	  ++k;
-	}
+    ARELEASE(mf, ch, Tracks);
+    ARELEASE(mf, ch, Display);
+    ARELEASE(mf, ch, Children);
 }
 
-static void parseAttachment(MatroskaFile *mf,uint64_t toplen) {
-  struct Attachment a,*pa;
-
-  memset(&a,0,sizeof(a));
-  FOREACH(mf,toplen)
-    case 0x467e: // Description
-      STRGETA(mf,a.Description,len);
-      break;
-    case 0x466e: // Name
-      STRGETA(mf,a.Name,len);
-      break;
-    case 0x4660: // MimeType
-      STRGETA(mf,a.MimeType,len);
-      break;
-    case 0x46ae: // UID
-      a.UID = readUInt(mf,(unsigned)len);
-      break;
-    case 0x465c: // Data
-      a.Position = filepos(mf);
-      a.Length = len;
-      skipbytes(mf,len);
-      break;
-  ENDFOR(mf);
-
-  if (!a.Position)
-    return;
+static void parseChapters(MatroskaFile *mf, uint64_t toplen)
+{
+    struct Chapter  *ch;
+
+    mf->seen.Chapters = 1;
+
+    FOREACH(mf, toplen)
+case 0x45b9: // EditionEntry
+    ch = AGET(mf, Chapters);
+    memset(ch, 0, sizeof(*ch));
+    FOREACH(mf, len)
+case 0x45bc: // EditionUID
+    ch->UID = readUInt(mf, (unsigned)len);
+    break;
+case 0x45bd: // EditionFlagHidden
+    ch->Hidden = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x45db: // EditionFlagDefault
+    ch->Default = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0x45dd: // EditionFlagOrdered
+    ch->Ordered = readUInt(mf, (unsigned)len) != 0;
+    break;
+case 0xb6: // ChapterAtom
+    parseChapter(mf, len, ch);
+    break;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+}
+
+static void parseTags(MatroskaFile *mf, uint64_t toplen)
+{
+    struct Tag  *tag;
+    struct Target *target;
+    struct SimpleTag *st;
+
+    mf->seen.Tags = 1;
+
+    FOREACH(mf, toplen)
+case 0x7373: // Tag
+    tag = AGET(mf, Tags);
+    memset(tag, 0, sizeof(*tag));
+
+    FOREACH(mf, len)
+case 0x63c0: // Targets
+    FOREACH(mf, len)
+case 0x63c5: // TrackUID
+    target = ASGET(mf, tag, Targets);
+    target->UID = readUInt(mf, (unsigned)len);
+    target->Type = TARGET_TRACK;
+    break;
+case 0x63c4: // ChapterUID
+    target = ASGET(mf, tag, Targets);
+    target->UID = readUInt(mf, (unsigned)len);
+    target->Type = TARGET_CHAPTER;
+    break;
+case 0x63c6: // AttachmentUID
+    target = ASGET(mf, tag, Targets);
+    target->UID = readUInt(mf, (unsigned)len);
+    target->Type = TARGET_ATTACHMENT;
+    break;
+case 0x63c9: // EditionUID
+    target = ASGET(mf, tag, Targets);
+    target->UID = readUInt(mf, (unsigned)len);
+    target->Type = TARGET_EDITION;
+    break;
+    ENDFOR(mf);
+    break;
+case 0x67c8: // SimpleTag
+    st = ASGET(mf, tag, SimpleTags);
+    memset(st, 0, sizeof(*st));
+
+    FOREACH(mf, len)
+case 0x45a3: // TagName
+    if (st->Name)
+        skipbytes(mf, len);
+    else
+        STRGETM(mf, st->Name, len);
+    break;
+case 0x4487: // TagString
+    if (st->Value)
+        skipbytes(mf, len);
+    else
+        STRGETM(mf, st->Value, len);
+    break;
+case 0x447a: // TagLanguage
+    readLangCC(mf, len, st->Language);
+    break;
+case 0x4484: // TagDefault
+    st->Default = readUInt(mf, (unsigned)len) != 0;
+    break;
+    ENDFOR(mf);
+
+    if (!st->Name || !st->Value) {
+        mf->cache->memfree(mf->cache, st->Name);
+        mf->cache->memfree(mf->cache, st->Value);
+        --tag->nSimpleTags;
+    }
+    break;
+    ENDFOR(mf);
+    break;
+    ENDFOR(mf);
+}
+
+static void parseContainer(MatroskaFile *mf)
+{
+    uint64_t len;
+    int	    id = readID(mf);
+    if (id == EOF)
+        errorjmp(mf, "Unexpected EOF in parseContainer");
 
-  pa = AGET(mf,Attachments);
-  memcpy(pa,&a,sizeof(a));
-
-  if (a.Description)
-    pa->Description = mystrdup(mf->cache,a.Description);
-  if (a.Name)
-    pa->Name = mystrdup(mf->cache,a.Name);
-  if (a.MimeType)
-    pa->MimeType = mystrdup(mf->cache,a.MimeType);
-}
-
-static void parseAttachments(MatroskaFile *mf,uint64_t toplen) {
-  mf->seen.Attachments = 1;
-
-  FOREACH(mf,toplen)
-    case 0x61a7: // AttachedFile
-      parseAttachment(mf,len);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseChapter(MatroskaFile *mf,uint64_t toplen,struct Chapter *parent) {
-  struct ChapterDisplay	*disp;
-  struct ChapterProcess	*proc;
-  struct ChapterCommand	*cmd;
-  struct Chapter	*ch = ASGET(mf,parent,Children);
-
-  memset(ch,0,sizeof(*ch));
-
-  ch->Enabled = 1;
-
-  FOREACH(mf,toplen)
-    case 0x73c4: // ChapterUID
-      ch->UID = readUInt(mf,(unsigned)len);
-      break;
-    case 0x6e67: // ChapterSegmentUID
-      if (len != sizeof(ch->SegmentUID))
-	skipbytes(mf, len);
-      else
-	readbytes(mf, ch->SegmentUID, sizeof(ch->SegmentUID));
-      break;
-    case 0x91: // ChapterTimeStart
-      ch->Start = readUInt(mf,(unsigned)len);
-      break;
-    case 0x92: // ChapterTimeEnd
-      ch->End = readUInt(mf,(unsigned)len);
-      break;
-    case 0x98: // ChapterFlagHidden
-      ch->Hidden = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x4598: // ChapterFlagEnabled
-      ch->Enabled = readUInt(mf,(unsigned)len)!=0;
-      break;
-    case 0x8f: // ChapterTrack
-      FOREACH(mf,len)
-	case 0x89: // ChapterTrackNumber
-	  *(uint64_t*)(ASGET(mf,ch,Tracks)) = readUInt(mf,(unsigned)len);
-	  break;
-      ENDFOR(mf);
-      break;
-    case 0x80: // ChapterDisplay
-      disp = NULL;
-
-      FOREACH(mf,len)
-	case 0x85: // ChapterString
-	  if (disp==NULL) {
-	    disp = ASGET(mf,ch,Display);
-	    memset(disp, 0, sizeof(*disp));
-	  }
-	  if (disp->String)
-	    skipbytes(mf,len); // Ignore duplicate string
-	  else
-	    STRGETM(mf,disp->String,len);
-	  break;
-	case 0x437c: // ChapterLanguage
-	  if (disp==NULL) {
-	    disp = ASGET(mf,ch,Display);
-	    memset(disp, 0, sizeof(*disp));
-	  }
-	  readLangCC(mf, len, disp->Language);
-	  break;
-	case 0x437e: // ChapterCountry
-	  if (disp==NULL) {
-	    disp = ASGET(mf,ch,Display);
-	    memset(disp, 0, sizeof(*disp));
-	  }
-	  readLangCC(mf, len, disp->Country);
-	  break;
-      ENDFOR(mf);
-
-      if (disp && !disp->String)
-	--ch->nDisplay;
-      break;
-    case 0x6944: // ChapProcess
-      proc = NULL;
-
-      FOREACH(mf,len)
-	case 0x6955: // ChapProcessCodecID
-	  if (proc == NULL) {
-	    proc = ASGET(mf, ch, Process);
-	    memset(proc, 0, sizeof(*proc));
-	  }
-	  proc->CodecID = (unsigned)readUInt(mf,(unsigned)len);
-	  break;
-	case 0x450d: // ChapProcessPrivate
-	  if (proc == NULL) {
-	    proc = ASGET(mf, ch, Process);
-	    memset(proc, 0, sizeof(*proc));
-	  }
-	  if (proc->CodecPrivate)
-	    skipbytes(mf, len);
-	  else {
-	    proc->CodecPrivateLength = (unsigned)len;
-	    STRGETM(mf,proc->CodecPrivate,len);
-	  }
-	  break;
-	case 0x6911: // ChapProcessCommand
-	  if (proc == NULL) {
-	    proc = ASGET(mf, ch, Process);
-	    memset(proc, 0, sizeof(*proc));
-	  }
-
-	  cmd = NULL;
-
-	  FOREACH(mf,len)
-	    case 0x6922: // ChapterCommandTime
-	      if (cmd == NULL) {
-		cmd = ASGET(mf,proc,Commands);
-		memset(cmd, 0, sizeof(*cmd));
-	      }
-	      cmd->Time = (unsigned)readUInt(mf,(unsigned)len);
-	      break;
-	    case 0x6933: // ChapterCommandString
-	      if (cmd == NULL) {
-		cmd = ASGET(mf,proc,Commands);
-		memset(cmd, 0, sizeof(*cmd));
-	      }
-	      if (cmd->Command)
-		skipbytes(mf,len);
-	      else {
-		cmd->CommandLength = (unsigned)len;
-		STRGETM(mf,cmd->Command,len);
-	      }
-	      break;
-	  ENDFOR(mf);
-
-	  if (cmd && !cmd->Command)
-	    --proc->nCommands;
-	  break;
-      ENDFOR(mf);
-
-      if (proc && !proc->nCommands)
-	--ch->nProcess;
-      break;
-    case 0xb6: // Nested ChapterAtom
-      parseChapter(mf,len,ch);
-      break;
-  ENDFOR(mf);
-
-  ARELEASE(mf,ch,Tracks);
-  ARELEASE(mf,ch,Display);
-  ARELEASE(mf,ch,Children);
-}
-
-static void parseChapters(MatroskaFile *mf,uint64_t toplen) {
-  struct Chapter  *ch;
-
-  mf->seen.Chapters = 1;
-
-  FOREACH(mf,toplen)
-    case 0x45b9: // EditionEntry
-	ch = AGET(mf,Chapters);
-	memset(ch, 0, sizeof(*ch));
- 	FOREACH(mf,len)
-	  case 0x45bc: // EditionUID
-	    ch->UID = readUInt(mf,(unsigned)len);
-	    break;
-	  case 0x45bd: // EditionFlagHidden
-	    ch->Hidden = readUInt(mf,(unsigned)len)!=0;
-	    break;
-	  case 0x45db: // EditionFlagDefault
-	    ch->Default = readUInt(mf,(unsigned)len)!=0;
-	    break;
-	  case 0x45dd: // EditionFlagOrdered
-	    ch->Ordered = readUInt(mf,(unsigned)len)!=0;
-	    break;
-	  case 0xb6: // ChapterAtom
-	    parseChapter(mf,len,ch);
-	    break;
-	ENDFOR(mf);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseTags(MatroskaFile *mf,uint64_t toplen) {
-  struct Tag  *tag;
-  struct Target *target;
-  struct SimpleTag *st;
-
-  mf->seen.Tags = 1;
-
-  FOREACH(mf,toplen)
-    case 0x7373: // Tag
-      tag = AGET(mf,Tags);
-      memset(tag,0,sizeof(*tag));
-
-      FOREACH(mf,len)
-	case 0x63c0: // Targets
-	  FOREACH(mf,len)
-	    case 0x63c5: // TrackUID
-	      target = ASGET(mf,tag,Targets);
-	      target->UID = readUInt(mf,(unsigned)len);
-	      target->Type = TARGET_TRACK;
-	      break;
-	    case 0x63c4: // ChapterUID
-	      target = ASGET(mf,tag,Targets);
-	      target->UID = readUInt(mf,(unsigned)len);
-	      target->Type = TARGET_CHAPTER;
-	      break;
-	    case 0x63c6: // AttachmentUID
-	      target = ASGET(mf,tag,Targets);
-	      target->UID = readUInt(mf,(unsigned)len);
-	      target->Type = TARGET_ATTACHMENT;
-	      break;
-	    case 0x63c9: // EditionUID
-	      target = ASGET(mf,tag,Targets);
-	      target->UID = readUInt(mf,(unsigned)len);
-	      target->Type = TARGET_EDITION;
-	      break;
-	  ENDFOR(mf);
-	  break;
-	case 0x67c8: // SimpleTag
-	  st = ASGET(mf,tag,SimpleTags);
-	  memset(st,0,sizeof(*st));
-
-	  FOREACH(mf,len)
-	    case 0x45a3: // TagName
-	      if (st->Name)
-		skipbytes(mf,len);
-	      else
-		STRGETM(mf,st->Name,len);
-	      break;
-	    case 0x4487: // TagString
-	      if (st->Value)
-		skipbytes(mf,len);
-	      else
-		STRGETM(mf,st->Value,len);
-	      break;
-	    case 0x447a: // TagLanguage
-	      readLangCC(mf, len, st->Language);
-	      break;
-	    case 0x4484: // TagDefault
-	      st->Default = readUInt(mf,(unsigned)len)!=0;
-	      break;
-	  ENDFOR(mf);
-
-	  if (!st->Name || !st->Value) {
-	    mf->cache->memfree(mf->cache,st->Name);
-	    mf->cache->memfree(mf->cache,st->Value);
-	    --tag->nSimpleTags;
-	  }
-	  break;
-      ENDFOR(mf);
-      break;
-  ENDFOR(mf);
-}
-
-static void parseContainer(MatroskaFile *mf) {
-  uint64_t len;
-  int	    id = readID(mf);
-  if (id==EOF)
-    errorjmp(mf,"Unexpected EOF in parseContainer");
-
-  len = readSize(mf);
-
-  switch (id) {
+    len = readSize(mf);
+
+    switch (id) {
     case 0x1549a966: // SegmentInfo
-      parseSegmentInfo(mf,len);
-      break;
+        parseSegmentInfo(mf, len);
+        break;
     case 0x1f43b675: // Cluster
-      parseFirstCluster(mf,len);
-      break;
+        parseFirstCluster(mf, len);
+        break;
     case 0x1654ae6b: // Tracks
-      parseTracks(mf,len);
-      break;
+        parseTracks(mf, len);
+        break;
     case 0x1c53bb6b: // Cues
-      parseCues(mf,len);
-      break;
+        parseCues(mf, len);
+        break;
     case 0x1941a469: // Attachments
-      parseAttachments(mf,len);
-      break;
+        parseAttachments(mf, len);
+        break;
     case 0x1043a770: // Chapters
-      parseChapters(mf,len);
-      break;
+        parseChapters(mf, len);
+        break;
     case 0x1254c367: // Tags
-      parseTags(mf,len);
-      break;
-  }
+        parseTags(mf, len);
+        break;
+    }
 }
 
-static void parseContainerPos(MatroskaFile *mf,uint64_t pos) {
-  seek(mf,pos);
-  parseContainer(mf);
+static void parseContainerPos(MatroskaFile *mf, uint64_t pos)
+{
+    seek(mf, pos);
+    parseContainer(mf);
 }
 
-static void parsePointers(MatroskaFile *mf) {
-  jmp_buf		jb;
+static void parsePointers(MatroskaFile *mf)
+{
+    jmp_buf		jb;
 
-  if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
-    parseContainerPos(mf,mf->pSegmentInfo);
-  if (mf->pCluster && !mf->seen.Cluster)
-    parseContainerPos(mf,mf->pCluster);
-  if (mf->pTracks && !mf->seen.Tracks)
-    parseContainerPos(mf,mf->pTracks);
+    if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
+        parseContainerPos(mf, mf->pSegmentInfo);
+    if (mf->pCluster && !mf->seen.Cluster)
+        parseContainerPos(mf, mf->pCluster);
+    if (mf->pTracks && !mf->seen.Tracks)
+        parseContainerPos(mf, mf->pTracks);
 
-  memcpy(&jb,&mf->jb,sizeof(jb));
+    memcpy(&jb, &mf->jb, sizeof(jb));
 
-  if (setjmp(mf->jb)) 
-    mf->flags &= ~MPF_ERROR; // ignore errors
-  else {
-    if (mf->pCues && !mf->seen.Cues)
-	parseContainerPos(mf,mf->pCues);
-    if (mf->pAttachments && !mf->seen.Attachments)
-      parseContainerPos(mf,mf->pAttachments);
-    if (mf->pChapters && !mf->seen.Chapters)
-      parseContainerPos(mf,mf->pChapters);
-    if (mf->pTags && !mf->seen.Tags)
-      parseContainerPos(mf,mf->pTags);
-  }
+    if (setjmp(mf->jb))
+        mf->flags &= ~MPF_ERROR; // ignore errors
+    else {
+        if (mf->pCues && !mf->seen.Cues)
+            parseContainerPos(mf, mf->pCues);
+        if (mf->pAttachments && !mf->seen.Attachments)
+            parseContainerPos(mf, mf->pAttachments);
+        if (mf->pChapters && !mf->seen.Chapters)
+            parseContainerPos(mf, mf->pChapters);
+        if (mf->pTags && !mf->seen.Tags)
+            parseContainerPos(mf, mf->pTags);
+    }
 
-  memcpy(&mf->jb,&jb,sizeof(jb));
+    memcpy(&mf->jb, &jb, sizeof(jb));
 }
 
-static void parseSegment(MatroskaFile *mf,uint64_t toplen) {
-  uint64_t   nextpos;
-  unsigned    nSeekHeads = 0, dontstop = 0;
-  jmp_buf     jb;
+static void parseSegment(MatroskaFile *mf, uint64_t toplen)
+{
+    uint64_t   nextpos;
+    unsigned    nSeekHeads = 0, dontstop = 0;
+    jmp_buf     jb;
 
-  memcpy(&jb,&mf->jb,sizeof(jb));
+    memcpy(&jb, &mf->jb, sizeof(jb));
 
-  if (setjmp(mf->jb))
-    mf->flags &= ~MPF_ERROR;
-  else {
-    // we want to read data until we find a seekhead or a trackinfo
-    FOREACH(mf,toplen)
-      case 0x114d9b74: // SeekHead
+    if (setjmp(mf->jb))
+        mf->flags &= ~MPF_ERROR;
+    else {
+        // we want to read data until we find a seekhead or a trackinfo
+        FOREACH(mf, toplen)
+    case 0x114d9b74: // SeekHead
         if (mf->flags & MKVF_AVOID_SEEKS) {
-	  skipbytes(mf,len);
-	  break;
+            skipbytes(mf, len);
+            break;
         }
 
         nextpos = filepos(mf) + len;
         do {
-	  mf->pSeekHead = 0;
-	  parseSeekHead(mf,len);
-	  ++nSeekHeads;
-	  if (mf->pSeekHead) { // this is possibly a chained SeekHead
-	    seek(mf,mf->pSeekHead);
-	    id = readID(mf);
-	    if (id==EOF) // chained SeekHead points to EOF?
-	      break;
-	    if (id != 0x114d9b74) // chained SeekHead doesnt point to a SeekHead?
-	      break;
-	    len = readSize(mf);
-	  }
+            mf->pSeekHead = 0;
+            parseSeekHead(mf, len);
+            ++nSeekHeads;
+            if (mf->pSeekHead) { // this is possibly a chained SeekHead
+                seek(mf, mf->pSeekHead);
+                id = readID(mf);
+                if (id == EOF) // chained SeekHead points to EOF?
+                    break;
+                if (id != 0x114d9b74) // chained SeekHead doesnt point to a SeekHead?
+                    break;
+                len = readSize(mf);
+            }
         } while (mf->pSeekHead && nSeekHeads < 10);
-        seek(mf,nextpos); // resume reading segment
+        seek(mf, nextpos); // resume reading segment
         break;
-      case 0x1549a966: // SegmentInfo
+    case 0x1549a966: // SegmentInfo
         mf->pSegmentInfo = cur;
-        parseSegmentInfo(mf,len);
+        parseSegmentInfo(mf, len);
         break;
-      case 0x1f43b675: // Cluster
+    case 0x1f43b675: // Cluster
         if (!mf->pCluster)
-	  mf->pCluster = cur;
+            mf->pCluster = cur;
         if (mf->seen.Cluster)
-	  skipbytes(mf,len);
+            skipbytes(mf, len);
         else
-	  parseFirstCluster(mf,len);
+            parseFirstCluster(mf, len);
         break;
-      case 0x1654ae6b: // Tracks
+    case 0x1654ae6b: // Tracks
         mf->pTracks = cur;
-        parseTracks(mf,len);
+        parseTracks(mf, len);
         break;
-      case 0x1c53bb6b: // Cues
+    case 0x1c53bb6b: // Cues
         mf->pCues = cur;
-        parseCues(mf,len);
+        parseCues(mf, len);
         break;
-      case 0x1941a469: // Attachments
+    case 0x1941a469: // Attachments
         mf->pAttachments = cur;
-        parseAttachments(mf,len);
+        parseAttachments(mf, len);
         break;
-      case 0x1043a770: // Chapters
+    case 0x1043a770: // Chapters
         mf->pChapters = cur;
-        parseChapters(mf,len);
+        parseChapters(mf, len);
         break;
-      case 0x1254c367: // Tags
+    case 0x1254c367: // Tags
         mf->pTags = cur;
-        parseTags(mf,len);
+        parseTags(mf, len);
         break;
-    ENDFOR1(mf);
-      // if we have pointers to all key elements
-      if (!dontstop && mf->pSegmentInfo && mf->pTracks && mf->pCluster)
-        break;
-    ENDFOR2();
-  }
+        ENDFOR1(mf);
+        // if we have pointers to all key elements
+        if (!dontstop && mf->pSegmentInfo && mf->pTracks && mf->pCluster)
+            break;
+        ENDFOR2();
+    }
+
+    memcpy(&mf->jb, &jb, sizeof(jb));
+
+    parsePointers(mf);
+}
 
-  memcpy(&mf->jb,&jb,sizeof(jb));
-
-  parsePointers(mf);
-}
-
-static void parseBlockAdditions(MatroskaFile *mf, uint64_t toplen, uint64_t timecode, unsigned track) {
-  uint64_t	add_id = 1, add_pos, add_len;
-  unsigned char	have_add;
-
-  FOREACH(mf, toplen)
-    case 0xa6: // BlockMore
-      have_add = 0;
-      FOREACH(mf, len)
-	case 0xee: // BlockAddId
-	  add_id = readUInt(mf, (unsigned)len);
-	  break;
-	case 0xa5: // BlockAddition
-	  add_pos = filepos(mf);
-	  add_len = len;
-	  skipbytes(mf, len);
-	  ++have_add;
-	  break;
-      ENDFOR(mf);
-      if (have_add == 1 && id > 0 && id < 255) {
-	struct QueueEntry *qe = QAlloc(mf);
-	qe->Start = qe->End = timecode;
-	qe->Position = add_pos;
-	qe->Length = (unsigned)add_len;
-	qe->flags = FRAME_UNKNOWN_START | FRAME_UNKNOWN_END |
-	  (((unsigned)add_id << FRAME_STREAM_SHIFT) & FRAME_STREAM_MASK);
-
-	QPut(&mf->Queues[track],qe);
-      }
-      break;
-  ENDFOR(mf);
-}
-
-static void parseBlockGroup(MatroskaFile *mf,uint64_t toplen,uint64_t timecode, int blockex) {
-  uint64_t	v;
-  uint64_t	duration = 0;
-  uint64_t	dpos;
-  struct QueueEntry *qe,*qf = NULL;
-  unsigned char	have_duration = 0, have_block = 0;
-  unsigned char	gap = 0;
-  unsigned char	lacing = 0;
-  unsigned char	ref = 0;
-  unsigned char	trackid;
-  unsigned	tracknum = 0;
-  int		c;
-  unsigned	nframes = 0,i;
-  unsigned	*sizes;
-  signed short	block_timecode;
-
-  if (blockex)
-    goto blockex;
-
-  FOREACH(mf,toplen)
-    case 0xfb: // ReferenceBlock
-      readSInt(mf,(unsigned)len);
-      ref = 1;
-      break;
+static void parseBlockAdditions(MatroskaFile *mf, uint64_t toplen, uint64_t timecode, unsigned track)
+{
+    uint64_t	add_id = 1, add_pos, add_len;
+    unsigned char	have_add;
+
+    FOREACH(mf, toplen)
+case 0xa6: // BlockMore
+    have_add = 0;
+    FOREACH(mf, len)
+case 0xee: // BlockAddId
+    add_id = readUInt(mf, (unsigned)len);
+    break;
+case 0xa5: // BlockAddition
+    add_pos = filepos(mf);
+    add_len = len;
+    skipbytes(mf, len);
+    ++have_add;
+    break;
+    ENDFOR(mf);
+    if (have_add == 1 && id > 0 && id < 255) {
+        struct QueueEntry *qe = QAlloc(mf);
+        qe->Start = qe->End = timecode;
+        qe->Position = add_pos;
+        qe->Length = (unsigned)add_len;
+        qe->flags = FRAME_UNKNOWN_START | FRAME_UNKNOWN_END |
+                    (((unsigned)add_id << FRAME_STREAM_SHIFT) & FRAME_STREAM_MASK);
+
+        QPut(&mf->Queues[track], qe);
+    }
+    break;
+    ENDFOR(mf);
+}
+
+static void parseBlockGroup(MatroskaFile *mf, uint64_t toplen, uint64_t timecode, int blockex)
+{
+    uint64_t	v;
+    uint64_t	duration = 0;
+    uint64_t	dpos;
+    struct QueueEntry *qe, *qf = NULL;
+    unsigned char	have_duration = 0, have_block = 0;
+    unsigned char	gap = 0;
+    unsigned char	lacing = 0;
+    unsigned char	ref = 0;
+    unsigned char	trackid;
+    unsigned	tracknum = 0;
+    int		c;
+    unsigned	nframes = 0, i;
+    unsigned	*sizes;
+    signed short	block_timecode;
+
+    if (blockex)
+        goto blockex;
+
+    FOREACH(mf, toplen)
+case 0xfb: // ReferenceBlock
+    readSInt(mf, (unsigned)len);
+    ref = 1;
+    break;
 blockex:
-      cur = start = filepos(mf);
-      len = tmplen = toplen;
-    case 0xa1: // Block
-      have_block = 1;
-
-      dpos = filepos(mf);
-
-      v = readVLUInt(mf);
-      if (v>255)
-	errorjmp(mf,"Invalid track number in Block: %d",(int)v);
-      trackid = (unsigned char)v;
-
-      for (tracknum=0;tracknum<mf->nTracks;++tracknum)
-	if (mf->Tracks[tracknum]->Number == trackid) {
-	  if (mf->trackMask & (1<<tracknum)) // ignore this block
-	    break;
-	  goto found;
-	}
+    cur = start = filepos(mf);
+    len = tmplen = toplen;
+case 0xa1: // Block
+    have_block = 1;
+
+    dpos = filepos(mf);
+
+    v = readVLUInt(mf);
+    if (v > 255)
+        errorjmp(mf, "Invalid track number in Block: %d", (int)v);
+    trackid = (unsigned char)v;
+
+    for (tracknum = 0; tracknum < mf->nTracks; ++tracknum)
+        if (mf->Tracks[tracknum]->Number == trackid) {
+            if (mf->trackMask & (1 << tracknum)) // ignore this block
+                break;
+            goto found;
+        }
 
-      // bad trackid/unsupported track
-      skipbytes(mf,start + tmplen - filepos(mf)); // shortcut
-      return;
+    // bad trackid/unsupported track
+    skipbytes(mf, start + tmplen - filepos(mf)); // shortcut
+    return;
 found:
 
-      block_timecode = (signed short)readSInt(mf,2);
-
-      // recalculate this block's timecode to final timecode in ns
-      timecode = mul3(mf->Tracks[tracknum]->TimecodeScale,
-	(timecode - mf->firstTimecode + block_timecode) * mf->Seg.TimecodeScale);
-
-      c = readch(mf);
-      if (c==EOF)
-	errorjmp(mf,"Unexpected EOF while reading Block flags");
-
-      if (blockex)
-	ref = (unsigned char)!(c & 0x80);
-
-      gap = (unsigned char)(c & 0x1);
-      lacing = (unsigned char)((c >> 1) & 3);
-
-      if (lacing) {
-	c = readch(mf);
-	if (c == EOF)
-	  errorjmp(mf,"Unexpected EOF while reading lacing data");
-	nframes = c+1;
-      } else
-	nframes = 1;
-      sizes = alloca(nframes*sizeof(*sizes));
- 
-      switch (lacing) {
-	case 0: // No lacing
-	  sizes[0] = (unsigned)(len - filepos(mf) + dpos);
-	  break;
-	case 1: // Xiph lacing
-	  sizes[nframes-1] = 0;
-	  for (i=0;i<nframes-1;++i) {
-	    sizes[i] = 0;
-	    do {
-	      c = readch(mf);
-	      if (c==EOF)
-		errorjmp(mf,"Unexpected EOF while reading lacing data");
-	      sizes[i] += c;
-	    } while (c==255);
-	    sizes[nframes-1] += sizes[i];
-	  }
-	  sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[nframes-1];
-	  break;
-	case 3: // EBML lacing
-	  sizes[nframes-1] = 0;
-	  sizes[0] = (unsigned)readVLUInt(mf);
-	  for (i=1;i<nframes-1;++i) {
-	    sizes[i] = sizes[i-1] + (int)readVLSInt(mf);
-	    sizes[nframes-1] += sizes[i];
-	  }
-	  if (nframes>1)
-	    sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[0] - sizes[nframes-1];
-	  break;
-	case 2: // Fixed lacing
-	  sizes[0] = (unsigned)(len - filepos(mf) + dpos)/nframes;
-	  for (i=1;i<nframes;++i)
-	    sizes[i] = sizes[0];
-	  break;
-      }
-
-      v = filepos(mf);
-      qf = NULL;
-      for (i=0;i<nframes;++i) {
-	qe = QAlloc(mf);
-	if (!qf)
-	  qf = qe;
-
-	qe->Start = timecode;
-	qe->End = timecode;
-	qe->Position = v;
-	qe->Length = sizes[i];
-	qe->flags = FRAME_UNKNOWN_END | FRAME_KF;
-	if (i == nframes-1 && gap)
-	  qe->flags |= FRAME_GAP;
-	if (i > 0)
-	  qe->flags |= FRAME_UNKNOWN_START;
-
-	QPut(&mf->Queues[tracknum],qe);
-
-	v += sizes[i];
-      }
-
-      // we want to still load these bytes into cache
-      for (v = filepos(mf) & ~0x3fff; v < len + dpos; v += 0x4000)
-	mf->cache->read(mf->cache,v,NULL,0); // touch page
-
-      skipbytes(mf,len - filepos(mf) + dpos);
-
-      if (blockex)
-	goto out;
-      break;
-    case 0x9b: // BlockDuration
-      duration = readUInt(mf,(unsigned)len);
-      have_duration = 1;
-      break;
-    case 0x75a1: // BlockAdditions
-      if (nframes > 0) // have some frames
-	parseBlockAdditions(mf, len, timecode, tracknum);
-      else
-	skipbytes(mf, len);
-      break;
-  ENDFOR(mf);
+    block_timecode = (signed short)readSInt(mf, 2);
+
+    // recalculate this block's timecode to final timecode in ns
+    timecode = mul3(mf->Tracks[tracknum]->TimecodeScale,
+                    (timecode - mf->firstTimecode + block_timecode) * mf->Seg.TimecodeScale);
+
+    c = readch(mf);
+    if (c == EOF)
+        errorjmp(mf, "Unexpected EOF while reading Block flags");
+
+    if (blockex)
+        ref = (unsigned char)!(c & 0x80);
+
+    gap = (unsigned char)(c & 0x1);
+    lacing = (unsigned char)((c >> 1) & 3);
+
+    if (lacing) {
+        c = readch(mf);
+        if (c == EOF)
+            errorjmp(mf, "Unexpected EOF while reading lacing data");
+        nframes = c + 1;
+    }
+    else
+        nframes = 1;
+    sizes = alloca(nframes * sizeof(*sizes));
+
+    switch (lacing) {
+    case 0: // No lacing
+        sizes[0] = (unsigned)(len - filepos(mf) + dpos);
+        break;
+    case 1: // Xiph lacing
+        sizes[nframes - 1] = 0;
+        for (i = 0; i < nframes - 1; ++i) {
+            sizes[i] = 0;
+            do {
+                c = readch(mf);
+                if (c == EOF)
+                    errorjmp(mf, "Unexpected EOF while reading lacing data");
+                sizes[i] += c;
+            } while (c == 255);
+            sizes[nframes - 1] += sizes[i];
+        }
+        sizes[nframes - 1] = (unsigned)(len - filepos(mf) + dpos) - sizes[nframes - 1];
+        break;
+    case 3: // EBML lacing
+        sizes[nframes - 1] = 0;
+        sizes[0] = (unsigned)readVLUInt(mf);
+        for (i = 1; i < nframes - 1; ++i) {
+            sizes[i] = sizes[i - 1] + (int)readVLSInt(mf);
+            sizes[nframes - 1] += sizes[i];
+        }
+        if (nframes > 1)
+            sizes[nframes - 1] = (unsigned)(len - filepos(mf) + dpos) - sizes[0] - sizes[nframes - 1];
+        break;
+    case 2: // Fixed lacing
+        sizes[0] = (unsigned)(len - filepos(mf) + dpos) / nframes;
+        for (i = 1; i < nframes; ++i)
+            sizes[i] = sizes[0];
+        break;
+    }
+
+    v = filepos(mf);
+    qf = NULL;
+    for (i = 0; i < nframes; ++i) {
+        qe = QAlloc(mf);
+        if (!qf)
+            qf = qe;
+
+        qe->Start = timecode;
+        qe->End = timecode;
+        qe->Position = v;
+        qe->Length = sizes[i];
+        qe->flags = FRAME_UNKNOWN_END | FRAME_KF;
+        if (i == nframes - 1 && gap)
+            qe->flags |= FRAME_GAP;
+        if (i > 0)
+            qe->flags |= FRAME_UNKNOWN_START;
+
+        QPut(&mf->Queues[tracknum], qe);
+
+        v += sizes[i];
+    }
+
+    // we want to still load these bytes into cache
+    for (v = filepos(mf) & ~0x3fff; v < len + dpos; v += 0x4000)
+        mf->cache->read(mf->cache, v, NULL, 0); // touch page
+
+    skipbytes(mf, len - filepos(mf) + dpos);
+
+    if (blockex)
+        goto out;
+    break;
+case 0x9b: // BlockDuration
+    duration = readUInt(mf, (unsigned)len);
+    have_duration = 1;
+    break;
+case 0x75a1: // BlockAdditions
+    if (nframes > 0) // have some frames
+        parseBlockAdditions(mf, len, timecode, tracknum);
+    else
+        skipbytes(mf, len);
+    break;
+    ENDFOR(mf);
 
 out:
-  if (!have_block)
-    errorjmp(mf,"Found a BlockGroup without Block");
-
-  if (nframes > 1) {
-    uint64_t defd = mf->Tracks[tracknum]->DefaultDuration;
-    v = qf->Start;
-
-    if (have_duration) {
-      duration = mul3(mf->Tracks[tracknum]->TimecodeScale,
-	duration * mf->Seg.TimecodeScale);
-
-      for (qe = qf; nframes > 1; --nframes, qe = qe->next) {
-	qe->Start = v;
-	v += defd;
-	duration -= defd;
-	qe->End = v;
+    if (!have_block)
+        errorjmp(mf, "Found a BlockGroup without Block");
+
+    if (nframes > 1) {
+        uint64_t defd = mf->Tracks[tracknum]->DefaultDuration;
+        v = qf->Start;
+
+        if (have_duration) {
+            duration = mul3(mf->Tracks[tracknum]->TimecodeScale,
+                            duration * mf->Seg.TimecodeScale);
+
+            for (qe = qf; nframes > 1; --nframes, qe = qe->next) {
+                qe->Start = v;
+                v += defd;
+                duration -= defd;
+                qe->End = v;
 #if 0
-	qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
+                qe->flags &= ~(FRAME_UNKNOWN_START | FRAME_UNKNOWN_END);
 #endif
-      }
-      qe->Start = v;
-      qe->End = v + duration;
-      qe->flags &= ~FRAME_UNKNOWN_END;
-    } else if (mf->Tracks[tracknum]->DefaultDuration) {
-      for (qe = qf; nframes > 0; --nframes, qe = qe->next) {
-	qe->Start = v;
-	v += defd;
-	qe->End = v;
-	qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
-      }
-    }
-  } else if (nframes == 1) {
-    if (have_duration) {
-      qf->End = qf->Start + mul3(mf->Tracks[tracknum]->TimecodeScale,
-	duration * mf->Seg.TimecodeScale);
-      qf->flags &= ~FRAME_UNKNOWN_END;
-    } else if (mf->Tracks[tracknum]->DefaultDuration) {
-      qf->End = qf->Start + mf->Tracks[tracknum]->DefaultDuration;
-      qf->flags &= ~FRAME_UNKNOWN_END;
+            }
+            qe->Start = v;
+            qe->End = v + duration;
+            qe->flags &= ~FRAME_UNKNOWN_END;
+        }
+        else if (mf->Tracks[tracknum]->DefaultDuration) {
+            for (qe = qf; nframes > 0; --nframes, qe = qe->next) {
+                qe->Start = v;
+                v += defd;
+                qe->End = v;
+                qe->flags &= ~(FRAME_UNKNOWN_START | FRAME_UNKNOWN_END);
+            }
+        }
     }
-  }
-
-  if (ref)
-    while (qf) {
-      qf->flags &= ~FRAME_KF;
-      qf = qf->next;
+    else if (nframes == 1) {
+        if (have_duration) {
+            qf->End = qf->Start + mul3(mf->Tracks[tracknum]->TimecodeScale,
+                                       duration * mf->Seg.TimecodeScale);
+            qf->flags &= ~FRAME_UNKNOWN_END;
+        }
+        else if (mf->Tracks[tracknum]->DefaultDuration) {
+            qf->End = qf->Start + mf->Tracks[tracknum]->DefaultDuration;
+            qf->flags &= ~FRAME_UNKNOWN_END;
+        }
     }
+
+    if (ref)
+        while (qf) {
+            qf->flags &= ~FRAME_KF;
+            qf = qf->next;
+        }
 }
 
-static void ClearQueue(MatroskaFile *mf,struct Queue *q) {
-  struct QueueEntry *qe,*qn;
+static void ClearQueue(MatroskaFile *mf, struct Queue *q)
+{
+    struct QueueEntry *qe, *qn;
 
-  for (qe=q->head;qe;qe=qn) {
-    qn = qe->next;
-    qe->next = mf->QFreeList;
-    mf->QFreeList = qe;
-  }
+    for (qe = q->head; qe; qe = qn) {
+        qn = qe->next;
+        qe->next = mf->QFreeList;
+        mf->QFreeList = qe;
+    }
 
-  q->head = NULL;
-  q->tail = NULL;
+    q->head = NULL;
+    q->tail = NULL;
 }
 
-static void EmptyQueues(MatroskaFile *mf) {
-  unsigned	    i;
+static void EmptyQueues(MatroskaFile *mf)
+{
+    unsigned	    i;
 
-  for (i=0;i<mf->nTracks;++i)
-    ClearQueue(mf,&mf->Queues[i]);
+    for (i = 0; i < mf->nTracks; ++i)
+        ClearQueue(mf, &mf->Queues[i]);
 }
 
-static int  readMoreBlocks(MatroskaFile *mf) {
-  uint64_t		toplen, cstop;
-  int64_t		cp;
-  int			cid, ret = 0;
-  jmp_buf		jb;
-  volatile unsigned	retries = 0;
-
-  if (mf->readPosition >= mf->pSegmentTop)
-    return EOF;
+static int  readMoreBlocks(MatroskaFile *mf)
+{
+    uint64_t		toplen, cstop;
+    int64_t		cp;
+    int			cid, ret = 0;
+    jmp_buf		jb;
+    volatile unsigned	retries = 0;
+
+    if (mf->readPosition >= mf->pSegmentTop)
+        return EOF;
+
+    memcpy(&jb, &mf->jb, sizeof(jb));
+
+    if (setjmp(mf->jb)) { // something evil happened here, try to resync
+        // always advance read position no matter what so
+        // we don't get caught in an endless loop
+        mf->readPosition = filepos(mf);
+
+        ret = EOF;
+
+        if (++retries > 3) // don't try too hard
+            goto ex;
+
+        for (;;) {
+            if (filepos(mf) >= mf->pSegmentTop)
+                goto ex;
+
+            cp = mf->cache->scan(mf->cache, filepos(mf), 0x1f43b675); // cluster
+
+            if (cp < 0 || (uint64_t)cp >= mf->pSegmentTop)
+                goto ex;
+
+            seek(mf, cp);
+
+            cid = readID(mf);
+            if (cid == EOF)
+                goto ex;
+            if (cid == 0x1f43b675) {
+                toplen = readSize(mf);
+                if (toplen < MAXCLUSTER) {
+                    // reset error flags
+                    mf->flags &= ~MPF_ERROR;
+                    ret = RBRESYNC;
+                    break;
+                }
+            }
+        }
 
-  memcpy(&jb,&mf->jb,sizeof(jb));
+        mf->readPosition = cp;
+    }
 
-  if (setjmp(mf->jb)) { // something evil happened here, try to resync
-    // always advance read position no matter what so
-    // we don't get caught in an endless loop
-    mf->readPosition = filepos(mf);
+    cstop = mf->cache->getcachesize(mf->cache) >> 1;
+    if (cstop > MAX_READAHEAD)
+        cstop = MAX_READAHEAD;
+    cstop += mf->readPosition;
 
-    ret = EOF;
+    seek(mf, mf->readPosition);
 
-    if (++retries > 3) // don't try too hard
-      goto ex;
+    while (filepos(mf) < mf->pSegmentTop) {
+        cid = readID(mf);
+        if (cid == EOF) {
+            ret = EOF;
+            break;
+        }
+        toplen = readSize(mf);
+
+        if (cid == 0x1f43b675) { // Cluster
+            unsigned char	have_timecode = 0;
+
+            FOREACH(mf, toplen)
+        case 0xe7: // Timecode
+            mf->tcCluster = readUInt(mf, (unsigned)len);
+            have_timecode = 1;
+            break;
+        case 0xa7: // Position
+            readUInt(mf, (unsigned)len);
+            break;
+        case 0xab: // PrevSize
+            readUInt(mf, (unsigned)len);
+            break;
+        case 0x5854: { // SilentTracks
+                unsigned  stmask = 0, i, trk;
+                FOREACH(mf, len)
+            case 0x58d7: // SilentTrackNumber
+                trk = (unsigned)readUInt(mf, (unsigned)len);
+                for (i = 0; i < mf->nTracks; ++i)
+                    if (mf->Tracks[i]->Number == trk) {
+                        stmask |= 1 << i;
+                        break;
+                    }
+                break;
+                ENDFOR(mf);
+                // TODO pass stmask to reading app
+                break;
+            }
+        case 0xa0: // BlockGroup
+            if (!have_timecode)
+                errorjmp(mf, "Found BlockGroup before cluster TimeCode");
+            parseBlockGroup(mf, len, mf->tcCluster, 0);
+            goto out;
+        case 0xa3: // BlockEx
+            if (!have_timecode)
+                errorjmp(mf, "Found BlockGroup before cluster TimeCode");
+            parseBlockGroup(mf, len, mf->tcCluster, 1);
+            goto out;
+            ENDFOR(mf);
+out:;
+        }
+        else {
+            if (toplen > MAXFRAME)
+                errorjmp(mf, "Element in a cluster is too large around %llu, %X [%u]", filepos(mf), cid, (unsigned)toplen);
+            if (cid == 0xa0) // BlockGroup
+                parseBlockGroup(mf, toplen, mf->tcCluster, 0);
+            else if (cid == 0xa3) // BlockEx
+                parseBlockGroup(mf, toplen, mf->tcCluster, 1);
+            else
+                skipbytes(mf, toplen);
+        }
 
-    for (;;) {
-      if (filepos(mf) >= mf->pSegmentTop)
-	goto ex;
-
-      cp = mf->cache->scan(mf->cache,filepos(mf),0x1f43b675); // cluster
-
-      if (cp < 0 || (uint64_t)cp >= mf->pSegmentTop)
-	goto ex;
-
-      seek(mf,cp);
-
-      cid = readID(mf);
-      if (cid == EOF)
-	goto ex;
-      if (cid == 0x1f43b675) {
-	toplen = readSize(mf);
-	if (toplen < MAXCLUSTER) {
-	  // reset error flags
-	  mf->flags &= ~MPF_ERROR;
-	  ret = RBRESYNC;
-	  break;
-	}
-      }
+        if ((mf->readPosition = filepos(mf)) > cstop)
+            break;
     }
 
-    mf->readPosition = cp;
-  }
-
-  cstop = mf->cache->getcachesize(mf->cache)>>1;
-  if (cstop > MAX_READAHEAD)
-    cstop = MAX_READAHEAD;
-  cstop += mf->readPosition;
-
-  seek(mf,mf->readPosition);
-
-  while (filepos(mf) < mf->pSegmentTop) {
-    cid = readID(mf);
-    if (cid == EOF) {
-      ret = EOF;
-      break;
-    }
-    toplen = readSize(mf);
-
-    if (cid == 0x1f43b675) { // Cluster
-      unsigned char	have_timecode = 0;
-
-      FOREACH(mf,toplen)
-	case 0xe7: // Timecode
-	  mf->tcCluster = readUInt(mf,(unsigned)len);
-	  have_timecode = 1;
-	  break;
-	case 0xa7: // Position
-	  readUInt(mf,(unsigned)len);
-	  break;
-	case 0xab: // PrevSize
-	  readUInt(mf,(unsigned)len);
-	  break;
-	case 0x5854: { // SilentTracks
-	  unsigned  stmask = 0, i, trk;
-	  FOREACH(mf, len)
-	    case 0x58d7: // SilentTrackNumber
-	      trk = (unsigned)readUInt(mf, (unsigned)len);
-	      for (i = 0; i < mf->nTracks; ++i)
-		if (mf->Tracks[i]->Number == trk) {
-		  stmask |= 1 << i;
-		  break;
-		}
-	      break;
-	  ENDFOR(mf);
-	  // TODO pass stmask to reading app
-	  break; }
-	case 0xa0: // BlockGroup
-	  if (!have_timecode)
-	    errorjmp(mf,"Found BlockGroup before cluster TimeCode");
-	  parseBlockGroup(mf,len,mf->tcCluster, 0);
-	  goto out;
-	case 0xa3: // BlockEx
-	  if (!have_timecode)
-	    errorjmp(mf,"Found BlockGroup before cluster TimeCode");
-	  parseBlockGroup(mf, len, mf->tcCluster, 1);
-	  goto out;
-      ENDFOR(mf);
-out:;
-    } else {
-      if (toplen > MAXFRAME)
-	errorjmp(mf,"Element in a cluster is too large around %llu, %X [%u]",filepos(mf),cid,(unsigned)toplen);
-      if (cid == 0xa0) // BlockGroup
-	parseBlockGroup(mf,toplen,mf->tcCluster, 0);
-      else if (cid == 0xa3) // BlockEx
-	parseBlockGroup(mf, toplen, mf->tcCluster, 1);
-      else
-	skipbytes(mf,toplen);
-    }
-
-    if ((mf->readPosition = filepos(mf)) > cstop)
-      break;
-  }
-
-  mf->readPosition = filepos(mf);
+    mf->readPosition = filepos(mf);
 
 ex:
-  memcpy(&mf->jb,&jb,sizeof(jb));
+    memcpy(&mf->jb, &jb, sizeof(jb));
 
-  return ret;
+    return ret;
 }
 
 // this is almost the same as readMoreBlocks, except it ensures
 // there are no partial frames queued, however empty queues are ok
-static int  fillQueues(MatroskaFile *mf,unsigned int mask) {
-  unsigned    i,j;
-  int	      ret = 0;
-
-  for (;;) {
-    j = 0;
-
-    for (i=0;i<mf->nTracks;++i)
-      if (mf->Queues[i].head && !(mask & (1<<i)))
-	++j;
-
-    if (j>0) // have at least some frames
-      return ret;
+static int  fillQueues(MatroskaFile *mf, unsigned int mask)
+{
+    unsigned    i, j;
+    int	      ret = 0;
 
-    if ((ret = readMoreBlocks(mf)) < 0) {
-      j = 0;
-      for (i=0;i<mf->nTracks;++i)
-	if (mf->Queues[i].head && !(mask & (1<<i)))
-	  ++j;
-      if (j) // we adjusted some blocks
-	return 0;
-      return EOF;
+    for (;;) {
+        j = 0;
+
+        for (i = 0; i < mf->nTracks; ++i)
+            if (mf->Queues[i].head && !(mask & (1 << i)))
+                ++j;
+
+        if (j > 0) // have at least some frames
+            return ret;
+
+        if ((ret = readMoreBlocks(mf)) < 0) {
+            j = 0;
+            for (i = 0; i < mf->nTracks; ++i)
+                if (mf->Queues[i].head && !(mask & (1 << i)))
+                    ++j;
+            if (j) // we adjusted some blocks
+                return 0;
+            return EOF;
+        }
     }
-  }
 }
 
-static void reindex(MatroskaFile *mf) {
-  jmp_buf     jb;
-  uint64_t   pos = mf->pCluster;
-  uint64_t   step = 10*1024*1024;
-  uint64_t   size, tc, isize;
-  int64_t    next_cluster;
-  int	      id, have_tc, bad;
-  struct Cue  *cue;
+static void reindex(MatroskaFile *mf)
+{
+    jmp_buf     jb;
+    uint64_t   pos = mf->pCluster;
+    uint64_t   step = 10 * 1024 * 1024;
+    uint64_t   size, tc, isize;
+    int64_t    next_cluster;
+    int	      id, have_tc, bad;
+    struct Cue  *cue;
 
-  if (pos >= mf->pSegmentTop)
-    return;
+    if (pos >= mf->pSegmentTop)
+        return;
 
-  if (pos + step * 10 > mf->pSegmentTop)
-    step = (mf->pSegmentTop - pos) / 10;
-  if (step == 0)
-    step = 1;
+    if (pos + step * 10 > mf->pSegmentTop)
+        step = (mf->pSegmentTop - pos) / 10;
+    if (step == 0)
+        step = 1;
 
-  memcpy(&jb,&mf->jb,sizeof(jb));
+    memcpy(&jb, &mf->jb, sizeof(jb));
 
-  // remove all cues
-  mf->nCues = 0;
+    // remove all cues
+    mf->nCues = 0;
 
-  bad = 0;
+    bad = 0;
 
-  while (pos < mf->pSegmentTop) {
-    if (!mf->cache->progress(mf->cache,pos,mf->pSegmentTop))
-      break;
+    while (pos < mf->pSegmentTop) {
+        if (!mf->cache->progress(mf->cache, pos, mf->pSegmentTop))
+            break;
 
-    if (++bad > 50) {
-      pos += step;
-      bad = 0;
-      continue;
-    }
+        if (++bad > 50) {
+            pos += step;
+            bad = 0;
+            continue;
+        }
 
-    // find next cluster header
-    next_cluster = mf->cache->scan(mf->cache,pos,0x1f43b675); // cluster
-    if (next_cluster < 0 || (uint64_t)next_cluster >= mf->pSegmentTop)
-      break;
+        // find next cluster header
+        next_cluster = mf->cache->scan(mf->cache, pos, 0x1f43b675); // cluster
+        if (next_cluster < 0 || (uint64_t)next_cluster >= mf->pSegmentTop)
+            break;
 
-    pos = next_cluster + 4; // prevent endless loops
+        pos = next_cluster + 4; // prevent endless loops
 
-    if (setjmp(mf->jb)) // something evil happened while reindexing
-      continue;
+        if (setjmp(mf->jb)) // something evil happened while reindexing
+            continue;
 
-    seek(mf,next_cluster);
+        seek(mf, next_cluster);
 
-    id = readID(mf);
-    if (id == EOF)
-      break;
-    if (id != 0x1f43b675) // shouldn't happen
-      continue;
+        id = readID(mf);
+        if (id == EOF)
+            break;
+        if (id != 0x1f43b675) // shouldn't happen
+            continue;
 
-    size = readVLUInt(mf);
-    if (size >= MAXCLUSTER || size < 1024)
-      continue;
+        size = readVLUInt(mf);
+        if (size >= MAXCLUSTER || size < 1024)
+            continue;
 
-    have_tc = 0;
-    size += filepos(mf);
+        have_tc = 0;
+        size += filepos(mf);
 
-    while (filepos(mf) < (uint64_t)next_cluster + 1024) {
-      id = readID(mf);
-      if (id == EOF)
-	break;
+        while (filepos(mf) < (uint64_t)next_cluster + 1024) {
+            id = readID(mf);
+            if (id == EOF)
+                break;
 
-      isize = readVLUInt(mf);
+            isize = readVLUInt(mf);
 
-      if (id == 0xe7) { // cluster timecode
-	tc = readUInt(mf,(unsigned)isize);
-	have_tc = 1;
-	break;
-      }
+            if (id == 0xe7) { // cluster timecode
+                tc = readUInt(mf, (unsigned)isize);
+                have_tc = 1;
+                break;
+            }
 
-      skipbytes(mf,isize);
-    }
+            skipbytes(mf, isize);
+        }
 
-    if (!have_tc)
-      continue;
+        if (!have_tc)
+            continue;
 
-    seek(mf,size);
-    id = readID(mf);
+        seek(mf, size);
+        id = readID(mf);
 
-    if (id == EOF)
-      break;
+        if (id == EOF)
+            break;
 
-    if (id != 0x1f43b675) // cluster
-      continue;
+        if (id != 0x1f43b675) // cluster
+            continue;
 
-    // good cluster, remember it
-    cue = AGET(mf,Cues);
-    cue->Time = tc;
-    cue->Position = next_cluster - mf->pSegment;
-    cue->Block = 0;
-    cue->Track = 0;
+        // good cluster, remember it
+        cue = AGET(mf, Cues);
+        cue->Time = tc;
+        cue->Position = next_cluster - mf->pSegment;
+        cue->Block = 0;
+        cue->Track = 0;
 
-    // advance to the next point
-    pos = next_cluster + step;
-    if (pos < size)
-      pos = size;
+        // advance to the next point
+        pos = next_cluster + step;
+        if (pos < size)
+            pos = size;
 
-    bad = 0;
-  }
+        bad = 0;
+    }
 
-  fixupCues(mf);
+    fixupCues(mf);
 
-  if (mf->nCues == 0) {
-    cue = AGET(mf,Cues);
-    cue->Time = mf->firstTimecode;
-    cue->Position = mf->pCluster - mf->pSegment;
-    cue->Block = 0;
-    cue->Track = 0;
-  }
+    if (mf->nCues == 0) {
+        cue = AGET(mf, Cues);
+        cue->Time = mf->firstTimecode;
+        cue->Position = mf->pCluster - mf->pSegment;
+        cue->Block = 0;
+        cue->Track = 0;
+    }
 
-  mf->cache->progress(mf->cache,0,0);
+    mf->cache->progress(mf->cache, 0, 0);
 
-  memcpy(&mf->jb,&jb,sizeof(jb));
+    memcpy(&mf->jb, &jb, sizeof(jb));
 }
 
-static void fixupChapter(uint64_t adj, struct Chapter *ch) {
-  unsigned i;
+static void fixupChapter(uint64_t adj, struct Chapter *ch)
+{
+    unsigned i;
 
-  if (ch->Start != 0)
-    ch->Start -= adj;
-  if (ch->End != 0)
-    ch->End -= adj;
+    if (ch->Start != 0)
+        ch->Start -= adj;
+    if (ch->End != 0)
+        ch->End -= adj;
 
-  for (i=0;i<ch->nChildren;++i)
-    fixupChapter(adj,&ch->Children[i]);
+    for (i = 0; i < ch->nChildren; ++i)
+        fixupChapter(adj, &ch->Children[i]);
 }
 
-static int64_t	findLastTimecode(MatroskaFile *mf) {
-  uint64_t   nd = 0;
-  unsigned    n,vtrack;
+static int64_t	findLastTimecode(MatroskaFile *mf)
+{
+    uint64_t   nd = 0;
+    unsigned    n, vtrack;
 
-  if (mf->nTracks == 0)
-    return -1;
+    if (mf->nTracks == 0)
+        return -1;
 
-  for (n=vtrack=0;n<mf->nTracks;++n)
-    if (mf->Tracks[n]->Type == TT_VIDEO) {
-      vtrack = n;
-      goto ok;
-    }
+    for (n = vtrack = 0; n < mf->nTracks; ++n)
+        if (mf->Tracks[n]->Type == TT_VIDEO) {
+            vtrack = n;
+            goto ok;
+        }
 
-  return -1;
+    return -1;
 ok:
 
-  EmptyQueues(mf);
-
-  if (mf->nCues == 0) {
-    mf->readPosition = mf->pCluster + 13000000 > mf->pSegmentTop ? mf->pCluster : mf->pSegmentTop - 13000000;
-    mf->tcCluster = 0;
-  } else {
-    mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegment;
-    mf->tcCluster = mf->Cues[mf->nCues - 1].Time / mf->Seg.TimecodeScale;
-  }
-  mf->trackMask = ~(1 << vtrack);
+    EmptyQueues(mf);
 
-  do
-    while (mf->Queues[vtrack].head)
-    {
-      uint64_t   tc = mf->Queues[vtrack].head->flags & FRAME_UNKNOWN_END ?
-			  mf->Queues[vtrack].head->Start : mf->Queues[vtrack].head->End;
-      if (nd < tc)
-	nd = tc;
-      QFree(mf,QGet(&mf->Queues[vtrack]));
+    if (mf->nCues == 0) {
+        mf->readPosition = mf->pCluster + 13000000 > mf->pSegmentTop ? mf->pCluster : mf->pSegmentTop - 13000000;
+        mf->tcCluster = 0;
+    }
+    else {
+        mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegment;
+        mf->tcCluster = mf->Cues[mf->nCues - 1].Time / mf->Seg.TimecodeScale;
     }
-  while (fillQueues(mf,0) != EOF);
+    mf->trackMask = ~(1 << vtrack);
+
+    do
+        while (mf->Queues[vtrack].head) {
+            uint64_t   tc = mf->Queues[vtrack].head->flags & FRAME_UNKNOWN_END ?
+                            mf->Queues[vtrack].head->Start : mf->Queues[vtrack].head->End;
+            if (nd < tc)
+                nd = tc;
+            QFree(mf, QGet(&mf->Queues[vtrack]));
+        }
+    while (fillQueues(mf, 0) != EOF);
 
-  mf->trackMask = 0;
+    mf->trackMask = 0;
 
-  EmptyQueues(mf);
+    EmptyQueues(mf);
 
-  // there may have been an error, but at this point we will ignore it
-  if (mf->flags & MPF_ERROR) {
-    mf->flags &= ~MPF_ERROR;
-    if (nd == 0)
-      return -1;
-  }
+    // there may have been an error, but at this point we will ignore it
+    if (mf->flags & MPF_ERROR) {
+        mf->flags &= ~MPF_ERROR;
+        if (nd == 0)
+            return -1;
+    }
 
-  return nd;
+    return nd;
 }
 
-static void parseFile(MatroskaFile *mf) {
-  uint64_t len = filepos(mf), adjust;
-  unsigned  i;
-  int	    id = readID(mf);
-  int	    m;
+static void parseFile(MatroskaFile *mf)
+{
+    uint64_t len = filepos(mf), adjust;
+    unsigned  i;
+    int	    id = readID(mf);
+    int	    m;
 
-  if (id==EOF)
-    errorjmp(mf,"Unexpected EOF at start of file");
+    if (id == EOF)
+        errorjmp(mf, "Unexpected EOF at start of file");
 
-  // files with multiple concatenated segments can have only
-  // one EBML prolog
-  if (len > 0 && id == 0x18538067)
-    goto segment;
+    // files with multiple concatenated segments can have only
+    // one EBML prolog
+    if (len > 0 && id == 0x18538067)
+        goto segment;
 
-  if (id!=0x1a45dfa3)
-    errorjmp(mf,"First element in file is not EBML");
+    if (id != 0x1a45dfa3)
+        errorjmp(mf, "First element in file is not EBML");
 
-  parseEBML(mf,readSize(mf));
+    parseEBML(mf, readSize(mf));
 
-  // next we need to find the first segment
-  for (;;) {
-    id = readID(mf);
-    if (id==EOF)
-      errorjmp(mf,"No segments found in the file");
+    // next we need to find the first segment
+    for (;;) {
+        id = readID(mf);
+        if (id == EOF)
+            errorjmp(mf, "No segments found in the file");
 segment:
-    len = readVLUIntImp(mf,&m);
-    // see if it's unspecified
-    if (len == (MAXU64 >> (57-m*7)))
-      len = MAXU64;
-    if (id == 0x18538067) // Segment
-      break;
-    skipbytes(mf,len);
-  }
+        len = readVLUIntImp(mf, &m);
+        // see if it's unspecified
+        if (len == (MAXU64 >> (57 - m * 7)))
+            len = MAXU64;
+        if (id == 0x18538067) // Segment
+            break;
+        skipbytes(mf, len);
+    }
 
-  // found it
-  mf->pSegment = filepos(mf);
-  if (len == MAXU64) {
-    mf->pSegmentTop = MAXU64;
-    if (mf->cache->getfilesize) {
-      int64_t seglen = mf->cache->getfilesize(mf->cache);
-      if (seglen > 0)
-	mf->pSegmentTop = seglen;
-    }
-  } else
-    mf->pSegmentTop = mf->pSegment + len;
-  parseSegment(mf,len);
-
-  // check if we got all data
-  if (!mf->seen.SegmentInfo)
-    errorjmp(mf,"Couldn't find SegmentInfo");
-  if (!mf->seen.Cluster)
-    mf->pCluster = mf->pSegmentTop;
-
-  adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
-
-  for (i=0;i<mf->nChapters;++i)
-    fixupChapter(adjust, &mf->Chapters[i]);
-
-  fixupCues(mf);
-
-  // release extra memory
-  ARELEASE(mf,mf,Tracks);
-
-  // initialize reader
-  mf->Queues = mf->cache->memalloc(mf->cache,mf->nTracks * sizeof(*mf->Queues));
-  if (mf->Queues == NULL)
-    errorjmp(mf, "Ouf of memory");
-  memset(mf->Queues, 0, mf->nTracks * sizeof(*mf->Queues));
-
-  // try to detect real duration
-  if (!(mf->flags & MKVF_AVOID_SEEKS)) {
-    int64_t nd = findLastTimecode(mf);
-    if (nd > 0)
-      mf->Seg.Duration = nd;
-  }
+    // found it
+    mf->pSegment = filepos(mf);
+    if (len == MAXU64) {
+        mf->pSegmentTop = MAXU64;
+        if (mf->cache->getfilesize) {
+            int64_t seglen = mf->cache->getfilesize(mf->cache);
+            if (seglen > 0)
+                mf->pSegmentTop = seglen;
+        }
+    }
+    else
+        mf->pSegmentTop = mf->pSegment + len;
+    parseSegment(mf, len);
 
-  // move to first frame
-  mf->readPosition = mf->pCluster;
-  mf->tcCluster = mf->firstTimecode;
-}
+    // check if we got all data
+    if (!mf->seen.SegmentInfo)
+        errorjmp(mf, "Couldn't find SegmentInfo");
+    if (!mf->seen.Cluster)
+        mf->pCluster = mf->pSegmentTop;
 
-static void DeleteChapter(MatroskaFile *mf,struct Chapter *ch) {
-  unsigned i,j;
+    adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
 
-  for (i=0;i<ch->nDisplay;++i)
-    mf->cache->memfree(mf->cache,ch->Display[i].String);
-  mf->cache->memfree(mf->cache,ch->Display);
-  mf->cache->memfree(mf->cache,ch->Tracks);
+    for (i = 0; i < mf->nChapters; ++i)
+        fixupChapter(adjust, &mf->Chapters[i]);
 
-  for (i=0;i<ch->nProcess;++i) {
-    for (j=0;j<ch->Process[i].nCommands;++j)
-      mf->cache->memfree(mf->cache,ch->Process[i].Commands[j].Command);
-    mf->cache->memfree(mf->cache,ch->Process[i].Commands);
-    mf->cache->memfree(mf->cache,ch->Process[i].CodecPrivate);
-  }
-  mf->cache->memfree(mf->cache,ch->Process);
+    fixupCues(mf);
+
+    // release extra memory
+    ARELEASE(mf, mf, Tracks);
+
+    // initialize reader
+    mf->Queues = mf->cache->memalloc(mf->cache, mf->nTracks * sizeof(*mf->Queues));
+    if (mf->Queues == NULL)
+        errorjmp(mf, "Ouf of memory");
+    memset(mf->Queues, 0, mf->nTracks * sizeof(*mf->Queues));
 
-  for (i=0;i<ch->nChildren;++i)
-    DeleteChapter(mf,&ch->Children[i]);
-  mf->cache->memfree(mf->cache,ch->Children);
+    // try to detect real duration
+    if (!(mf->flags & MKVF_AVOID_SEEKS)) {
+        int64_t nd = findLastTimecode(mf);
+        if (nd > 0)
+            mf->Seg.Duration = nd;
+    }
+
+    // move to first frame
+    mf->readPosition = mf->pCluster;
+    mf->tcCluster = mf->firstTimecode;
+}
+
+static void DeleteChapter(MatroskaFile *mf, struct Chapter *ch)
+{
+    unsigned i, j;
+
+    for (i = 0; i < ch->nDisplay; ++i)
+        mf->cache->memfree(mf->cache, ch->Display[i].String);
+    mf->cache->memfree(mf->cache, ch->Display);
+    mf->cache->memfree(mf->cache, ch->Tracks);
+
+    for (i = 0; i < ch->nProcess; ++i) {
+        for (j = 0; j < ch->Process[i].nCommands; ++j)
+            mf->cache->memfree(mf->cache, ch->Process[i].Commands[j].Command);
+        mf->cache->memfree(mf->cache, ch->Process[i].Commands);
+        mf->cache->memfree(mf->cache, ch->Process[i].CodecPrivate);
+    }
+    mf->cache->memfree(mf->cache, ch->Process);
+
+    for (i = 0; i < ch->nChildren; ++i)
+        DeleteChapter(mf, &ch->Children[i]);
+    mf->cache->memfree(mf->cache, ch->Children);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // public interface
 MatroskaFile  *mkv_OpenEx(InputStream *io,
-			  uint64_t base,
-			  unsigned flags,
-			  char *err_msg,unsigned msgsize)
-{
-  MatroskaFile	*mf = io->memalloc(io,sizeof(*mf));
-  if (mf == NULL) {
-    mystrlcpy(err_msg,"Out of memory",msgsize);
-    return NULL;
-  }
+                          uint64_t base,
+                          unsigned flags,
+                          char *err_msg, unsigned msgsize)
+{
+    MatroskaFile	*mf = io->memalloc(io, sizeof(*mf));
+    if (mf == NULL) {
+        mystrlcpy(err_msg, "Out of memory", msgsize);
+        return NULL;
+    }
 
-  memset(mf,0,sizeof(*mf));
+    memset(mf, 0, sizeof(*mf));
 
-  mf->cache = io;
-  mf->flags = flags;
-  io->progress(io,0,0);
+    mf->cache = io;
+    mf->flags = flags;
+    io->progress(io, 0, 0);
 
-  if (setjmp(mf->jb)==0) {
-    seek(mf,base);
-    parseFile(mf);
-  } else { // parser error
-    mystrlcpy(err_msg,mf->errmsg,msgsize);
-    mkv_Close(mf);
-    return NULL;
-  }
+    if (setjmp(mf->jb) == 0) {
+        seek(mf, base);
+        parseFile(mf);
+    }
+    else {   // parser error
+        mystrlcpy(err_msg, mf->errmsg, msgsize);
+        mkv_Close(mf);
+        return NULL;
+    }
 
-  return mf;
+    return mf;
 }
 
 MatroskaFile  *mkv_Open(InputStream *io,
-			char *err_msg,unsigned msgsize)
+                        char *err_msg, unsigned msgsize)
 {
-  return mkv_OpenEx(io,0,0,err_msg,msgsize);
+    return mkv_OpenEx(io, 0, 0, err_msg, msgsize);
 }
 
-void	      mkv_Close(MatroskaFile *mf) {
-  unsigned  i,j;
+void	      mkv_Close(MatroskaFile *mf)
+{
+    unsigned  i, j;
 
-  if (mf==NULL)
-    return;
+    if (mf == NULL)
+        return;
 
-  for (i=0;i<mf->nTracks;++i)
-    mf->cache->memfree(mf->cache,mf->Tracks[i]);
-  mf->cache->memfree(mf->cache,mf->Tracks);
+    for (i = 0; i < mf->nTracks; ++i)
+        mf->cache->memfree(mf->cache, mf->Tracks[i]);
+    mf->cache->memfree(mf->cache, mf->Tracks);
 
-  for (i=0;i<mf->nQBlocks;++i)
-    mf->cache->memfree(mf->cache,mf->QBlocks[i]);
-  mf->cache->memfree(mf->cache,mf->QBlocks);
+    for (i = 0; i < mf->nQBlocks; ++i)
+        mf->cache->memfree(mf->cache, mf->QBlocks[i]);
+    mf->cache->memfree(mf->cache, mf->QBlocks);
 
-  mf->cache->memfree(mf->cache,mf->Queues);
+    mf->cache->memfree(mf->cache, mf->Queues);
 
-  mf->cache->memfree(mf->cache,mf->Seg.Title);
-  mf->cache->memfree(mf->cache,mf->Seg.MuxingApp);
-  mf->cache->memfree(mf->cache,mf->Seg.WritingApp);
-  mf->cache->memfree(mf->cache,mf->Seg.Filename);
-  mf->cache->memfree(mf->cache,mf->Seg.NextFilename);
-  mf->cache->memfree(mf->cache,mf->Seg.PrevFilename);
+    mf->cache->memfree(mf->cache, mf->Seg.Title);
+    mf->cache->memfree(mf->cache, mf->Seg.MuxingApp);
+    mf->cache->memfree(mf->cache, mf->Seg.WritingApp);
+    mf->cache->memfree(mf->cache, mf->Seg.Filename);
+    mf->cache->memfree(mf->cache, mf->Seg.NextFilename);
+    mf->cache->memfree(mf->cache, mf->Seg.PrevFilename);
 
-  mf->cache->memfree(mf->cache,mf->Cues);
+    mf->cache->memfree(mf->cache, mf->Cues);
 
-  for (i=0;i<mf->nAttachments;++i) {
-    mf->cache->memfree(mf->cache,mf->Attachments[i].Description);
-    mf->cache->memfree(mf->cache,mf->Attachments[i].Name);
-    mf->cache->memfree(mf->cache,mf->Attachments[i].MimeType);
-  }
-  mf->cache->memfree(mf->cache,mf->Attachments);
+    for (i = 0; i < mf->nAttachments; ++i) {
+        mf->cache->memfree(mf->cache, mf->Attachments[i].Description);
+        mf->cache->memfree(mf->cache, mf->Attachments[i].Name);
+        mf->cache->memfree(mf->cache, mf->Attachments[i].MimeType);
+    }
+    mf->cache->memfree(mf->cache, mf->Attachments);
 
-  for (i=0;i<mf->nChapters;++i)
-    DeleteChapter(mf,&mf->Chapters[i]);
-  mf->cache->memfree(mf->cache,mf->Chapters);
+    for (i = 0; i < mf->nChapters; ++i)
+        DeleteChapter(mf, &mf->Chapters[i]);
+    mf->cache->memfree(mf->cache, mf->Chapters);
 
-  for (i=0;i<mf->nTags;++i) {
-    for (j=0;j<mf->Tags[i].nSimpleTags;++j) {
-      mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Name);
-      mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Value);
+    for (i = 0; i < mf->nTags; ++i) {
+        for (j = 0; j < mf->Tags[i].nSimpleTags; ++j) {
+            mf->cache->memfree(mf->cache, mf->Tags[i].SimpleTags[j].Name);
+            mf->cache->memfree(mf->cache, mf->Tags[i].SimpleTags[j].Value);
+        }
+        mf->cache->memfree(mf->cache, mf->Tags[i].Targets);
+        mf->cache->memfree(mf->cache, mf->Tags[i].SimpleTags);
     }
-    mf->cache->memfree(mf->cache,mf->Tags[i].Targets);
-    mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags);
-  }
-  mf->cache->memfree(mf->cache,mf->Tags);
+    mf->cache->memfree(mf->cache, mf->Tags);
 
-  mf->cache->memfree(mf->cache,mf);
+    mf->cache->memfree(mf->cache, mf);
 }
 
-const char    *mkv_GetLastError(MatroskaFile *mf) {
-  return mf->errmsg[0] ? mf->errmsg : NULL;
+const char    *mkv_GetLastError(MatroskaFile *mf)
+{
+    return mf->errmsg[0] ? mf->errmsg : NULL;
 }
 
-SegmentInfo   *mkv_GetFileInfo(MatroskaFile *mf) {
-  return &mf->Seg;
+SegmentInfo   *mkv_GetFileInfo(MatroskaFile *mf)
+{
+    return &mf->Seg;
 }
 
-unsigned int  mkv_GetNumTracks(MatroskaFile *mf) {
-  return mf->nTracks;
+unsigned int  mkv_GetNumTracks(MatroskaFile *mf)
+{
+    return mf->nTracks;
 }
 
-TrackInfo     *mkv_GetTrackInfo(MatroskaFile *mf,unsigned track) {
-  if (track>mf->nTracks)
-    return NULL;
+TrackInfo     *mkv_GetTrackInfo(MatroskaFile *mf, unsigned track)
+{
+    if (track > mf->nTracks)
+        return NULL;
 
-  return mf->Tracks[track];
+    return mf->Tracks[track];
 }
 
-void	      mkv_GetAttachments(MatroskaFile *mf,Attachment **at,unsigned *count) {
-  *at = mf->Attachments;
-  *count = mf->nAttachments;
+void	      mkv_GetAttachments(MatroskaFile *mf, Attachment **at, unsigned *count)
+{
+    *at = mf->Attachments;
+    *count = mf->nAttachments;
 }
 
-void	      mkv_GetChapters(MatroskaFile *mf,Chapter **ch,unsigned *count) {
-  *ch = mf->Chapters;
-  *count = mf->nChapters;
+void	      mkv_GetChapters(MatroskaFile *mf, Chapter **ch, unsigned *count)
+{
+    *ch = mf->Chapters;
+    *count = mf->nChapters;
 }
 
-void	      mkv_GetTags(MatroskaFile *mf,Tag **tag,unsigned *count) {
-  *tag = mf->Tags;
-  *count = mf->nTags;
+void	      mkv_GetTags(MatroskaFile *mf, Tag **tag, unsigned *count)
+{
+    *tag = mf->Tags;
+    *count = mf->nTags;
 }
 
-uint64_t     mkv_GetSegmentTop(MatroskaFile *mf) {
-  return mf->pSegmentTop;
+uint64_t     mkv_GetSegmentTop(MatroskaFile *mf)
+{
+    return mf->pSegmentTop;
 }
 
 #define	IS_DELTA(f) (!((f)->flags & FRAME_KF) || ((f)->flags & FRAME_UNKNOWN_START))
 
-void  mkv_Seek(MatroskaFile *mf,uint64_t timecode,unsigned flags) {
-  int		i,j,m,ret;
-  unsigned	n,z,mask;
-  uint64_t	m_kftime[MAX_TRACKS];
-  unsigned char	m_seendf[MAX_TRACKS];
+void  mkv_Seek(MatroskaFile *mf, uint64_t timecode, unsigned flags)
+{
+    int		i, j, m, ret;
+    unsigned	n, z, mask;
+    uint64_t	m_kftime[MAX_TRACKS];
+    unsigned char	m_seendf[MAX_TRACKS];
 
-  if (mf->flags & MKVF_AVOID_SEEKS)
-    return;
+    if (mf->flags & MKVF_AVOID_SEEKS)
+        return;
 
-  if (timecode == 0) {
-    EmptyQueues(mf);
-    mf->readPosition = mf->pCluster;
-    mf->tcCluster = mf->firstTimecode;
-    mf->flags &= ~MPF_ERROR;
+    if (timecode == 0) {
+        EmptyQueues(mf);
+        mf->readPosition = mf->pCluster;
+        mf->tcCluster = mf->firstTimecode;
+        mf->flags &= ~MPF_ERROR;
 
-    return;
-  }
+        return;
+    }
 
-  if (mf->nCues==0)
-    reindex(mf);
+    if (mf->nCues == 0)
+        reindex(mf);
 
-  if (mf->nCues==0)
-    return;
+    if (mf->nCues == 0)
+        return;
 
-  mf->flags &= ~MPF_ERROR;
-
-  i = 0;
-  j = mf->nCues - 1;
-
-  for (;;) {
-    if (i>j) {
-      j = j>=0 ? j : 0;
-
-      if (setjmp(mf->jb)!=0)
-	return;
-
-      mkv_SetTrackMask(mf,mf->trackMask);
-
-      if (flags & (MKVF_SEEK_TO_PREV_KEYFRAME | MKVF_SEEK_TO_PREV_KEYFRAME_STRICT)) {
-	// we do this in two stages
-	// a. find the last keyframes before the require position
-	// b. seek to them
-
-	// pass 1
-	for (;;) {
-	  for (n=0;n<mf->nTracks;++n) {
-	    m_kftime[n] = MAXU64;
-	    m_seendf[n] = 0;
-	  }
-
-	  EmptyQueues(mf);
-
-	  mf->readPosition = mf->Cues[j].Position + mf->pSegment;
-	  mf->tcCluster = mf->Cues[j].Time;
-
-	  for (;;) {
-	    if ((ret = fillQueues(mf,0)) < 0 || ret == RBRESYNC)
-	      return;
-
-	    // drain queues until we get to the required timecode
-	    for (n=0;n<mf->nTracks;++n) {
-	      if (mf->Queues[n].head && (mf->Queues[n].head->Start<timecode || (m_seendf[n] == 0 && m_kftime[n] == MAXU64))) {
-		if (IS_DELTA(mf->Queues[n].head))
-		  m_seendf[n] = 1;
-		else
-		  m_kftime[n] = mf->Queues[n].head->Start;
-	      }
-
-	      while (mf->Queues[n].head && mf->Queues[n].head->Start<timecode)
-	      {
-		if (IS_DELTA(mf->Queues[n].head))
-		  m_seendf[n] = 1;
-		else
-		  m_kftime[n] = mf->Queues[n].head->Start;
-		QFree(mf,QGet(&mf->Queues[n]));
-	      }
-
-              // We've drained the queue, so the frame at head is the next one past the requered point.
-              // In strict mode we are done, but when seeking is not strict we use the head frame
-              // if it's not an audio track (we accept preroll within a frame for audio), and the head frame
-              // is a keyframe
-              if (!(flags & MKVF_SEEK_TO_PREV_KEYFRAME_STRICT))
-	        if (mf->Queues[n].head && (mf->Tracks[n]->Type != TT_AUDIO || mf->Queues[n].head->Start<=timecode))
-		  if (!IS_DELTA(mf->Queues[n].head))
-		    m_kftime[n] = mf->Queues[n].head->Start;
-	    }
-
-	    for (n=0;n<mf->nTracks;++n)
-	      if (mf->Queues[n].head && mf->Queues[n].head->Start>=timecode)
-		goto found;
-	  }
-found:
-  
-	  for (n=0;n<mf->nTracks;++n)
-	    if (!(mf->trackMask & (1<<n)) && m_kftime[n]==MAXU64 &&
-		m_seendf[n] && j>0)
-	    {
-	      // we need to restart the search from prev cue
-	      --j;
-	      goto again;
-	    }
-
-	  break;
-again:;
-	}
-      } else
-	for (n=0;n<mf->nTracks;++n)
-	  m_kftime[n] = timecode;
+    mf->flags &= ~MPF_ERROR;
 
-      // now seek to this timecode
-      EmptyQueues(mf);
+    i = 0;
+    j = mf->nCues - 1;
 
-      mf->readPosition = mf->Cues[j].Position + mf->pSegment;
-      mf->tcCluster = mf->Cues[j].Time;
+    for (;;) {
+        if (i > j) {
+            j = j >= 0 ? j : 0;
+
+            if (setjmp(mf->jb) != 0)
+                return;
+
+            mkv_SetTrackMask(mf, mf->trackMask);
+
+            if (flags & (MKVF_SEEK_TO_PREV_KEYFRAME | MKVF_SEEK_TO_PREV_KEYFRAME_STRICT)) {
+                // we do this in two stages
+                // a. find the last keyframes before the require position
+                // b. seek to them
+
+                // pass 1
+                for (;;) {
+                    for (n = 0; n < mf->nTracks; ++n) {
+                        m_kftime[n] = MAXU64;
+                        m_seendf[n] = 0;
+                    }
+
+                    EmptyQueues(mf);
+
+                    mf->readPosition = mf->Cues[j].Position + mf->pSegment;
+                    mf->tcCluster = mf->Cues[j].Time;
+
+                    for (;;) {
+                        if ((ret = fillQueues(mf, 0)) < 0 || ret == RBRESYNC)
+                            return;
+
+                        // drain queues until we get to the required timecode
+                        for (n = 0; n < mf->nTracks; ++n) {
+                            if (mf->Queues[n].head && (mf->Queues[n].head->Start < timecode || (m_seendf[n] == 0 && m_kftime[n] == MAXU64))) {
+                                if (IS_DELTA(mf->Queues[n].head))
+                                    m_seendf[n] = 1;
+                                else
+                                    m_kftime[n] = mf->Queues[n].head->Start;
+                            }
+
+                            while (mf->Queues[n].head && mf->Queues[n].head->Start < timecode) {
+                                if (IS_DELTA(mf->Queues[n].head))
+                                    m_seendf[n] = 1;
+                                else
+                                    m_kftime[n] = mf->Queues[n].head->Start;
+                                QFree(mf, QGet(&mf->Queues[n]));
+                            }
+
+                            // We've drained the queue, so the frame at head is the next one past the requered point.
+                            // In strict mode we are done, but when seeking is not strict we use the head frame
+                            // if it's not an audio track (we accept preroll within a frame for audio), and the head frame
+                            // is a keyframe
+                            if (!(flags & MKVF_SEEK_TO_PREV_KEYFRAME_STRICT))
+                                if (mf->Queues[n].head && (mf->Tracks[n]->Type != TT_AUDIO || mf->Queues[n].head->Start <= timecode))
+                                    if (!IS_DELTA(mf->Queues[n].head))
+                                        m_kftime[n] = mf->Queues[n].head->Start;
+                        }
+
+                        for (n = 0; n < mf->nTracks; ++n)
+                            if (mf->Queues[n].head && mf->Queues[n].head->Start >= timecode)
+                                goto found;
+                    }
+found:
 
-      for (mask=0;;) {
-	if ((ret = fillQueues(mf,mask)) < 0 || ret == RBRESYNC)
-	  return;
+                    for (n = 0; n < mf->nTracks; ++n)
+                        if (!(mf->trackMask & (1 << n)) && m_kftime[n] == MAXU64 &&
+                            m_seendf[n] && j > 0) {
+                            // we need to restart the search from prev cue
+                            --j;
+                            goto again;
+                        }
 
-	// drain queues until we get to the required timecode
-	for (n=0;n<mf->nTracks;++n) {
-	  struct QueueEntry *qe;
-	  for (qe = mf->Queues[n].head;qe && qe->Start<m_kftime[n];qe = mf->Queues[n].head)
-	    QFree(mf,QGet(&mf->Queues[n]));
-	}
+                    break;
+again:;
+                }
+            }
+            else
+                for (n = 0; n < mf->nTracks; ++n)
+                    m_kftime[n] = timecode;
+
+            // now seek to this timecode
+            EmptyQueues(mf);
+
+            mf->readPosition = mf->Cues[j].Position + mf->pSegment;
+            mf->tcCluster = mf->Cues[j].Time;
+
+            for (mask = 0;;) {
+                if ((ret = fillQueues(mf, mask)) < 0 || ret == RBRESYNC)
+                    return;
+
+                // drain queues until we get to the required timecode
+                for (n = 0; n < mf->nTracks; ++n) {
+                    struct QueueEntry *qe;
+                    for (qe = mf->Queues[n].head; qe && qe->Start < m_kftime[n]; qe = mf->Queues[n].head)
+                        QFree(mf, QGet(&mf->Queues[n]));
+                }
+
+                for (n = z = 0; n < mf->nTracks; ++n)
+                    if (m_kftime[n] == MAXU64 || (mf->Queues[n].head && mf->Queues[n].head->Start >= m_kftime[n])) {
+                        ++z;
+                        mask |= 1 << n;
+                    }
+
+                if (z == mf->nTracks)
+                    return;
+            }
+        }
 
-	for (n=z=0;n<mf->nTracks;++n)
-	  if (m_kftime[n]==MAXU64 || (mf->Queues[n].head && mf->Queues[n].head->Start>=m_kftime[n])) {
-	    ++z;
-	    mask |= 1<<n;
-	  }
+        m = (i + j) >> 1;
 
-	if (z==mf->nTracks)
-	  return;
-      }
+        if (timecode < mf->Cues[m].Time)
+            j = m - 1;
+        else
+            i = m + 1;
     }
-
-    m = (i+j)>>1;
-
-    if (timecode < mf->Cues[m].Time)
-      j = m-1;
-    else
-      i = m+1;
-  }
 }
 
-void  mkv_SkipToKeyframe(MatroskaFile *mf) {
-  unsigned  n,wait;
-  uint64_t ht;
+void  mkv_SkipToKeyframe(MatroskaFile *mf)
+{
+    unsigned  n, wait;
+    uint64_t ht;
 
-  if (setjmp(mf->jb)!=0)
-    return;
+    if (setjmp(mf->jb) != 0)
+        return;
 
-  // remove delta frames from queues
-  do {
-    wait = 0;
+    // remove delta frames from queues
+    do {
+        wait = 0;
 
-    if (fillQueues(mf,0)<0)
-      return;
+        if (fillQueues(mf, 0) < 0)
+            return;
 
-    for (n=0;n<mf->nTracks;++n)
-      if (mf->Queues[n].head && !(mf->Queues[n].head->flags & FRAME_KF)) {
-	++wait;
-	QFree(mf,QGet(&mf->Queues[n]));
-      }
-  } while (wait);
+        for (n = 0; n < mf->nTracks; ++n)
+            if (mf->Queues[n].head && !(mf->Queues[n].head->flags & FRAME_KF)) {
+                ++wait;
+                QFree(mf, QGet(&mf->Queues[n]));
+            }
+    } while (wait);
 
-  // find highest queued time
-  for (n=0,ht=0;n<mf->nTracks;++n)
-    if (mf->Queues[n].head && ht<mf->Queues[n].head->Start)
-      ht = mf->Queues[n].head->Start;
+    // find highest queued time
+    for (n = 0, ht = 0; n < mf->nTracks; ++n)
+        if (mf->Queues[n].head && ht < mf->Queues[n].head->Start)
+            ht = mf->Queues[n].head->Start;
 
-  // ensure the time difference is less than 100ms
-  do {
-    wait = 0;
+    // ensure the time difference is less than 100ms
+    do {
+        wait = 0;
 
-    if (fillQueues(mf,0)<0)
-      return;
+        if (fillQueues(mf, 0) < 0)
+            return;
 
-    for (n=0;n<mf->nTracks;++n)
-      while (mf->Queues[n].head && mf->Queues[n].head->next &&
-	  (mf->Queues[n].head->next->flags & FRAME_KF) &&
-	  ht - mf->Queues[n].head->Start > 100000000)
-      {
-	++wait;
-	QFree(mf,QGet(&mf->Queues[n]));
-      }
+        for (n = 0; n < mf->nTracks; ++n)
+            while (mf->Queues[n].head && mf->Queues[n].head->next &&
+                   (mf->Queues[n].head->next->flags & FRAME_KF) &&
+                   ht - mf->Queues[n].head->Start > 100000000) {
+                ++wait;
+                QFree(mf, QGet(&mf->Queues[n]));
+            }
 
-  } while (wait);
+    } while (wait);
 }
 
-uint64_t mkv_GetLowestQTimecode(MatroskaFile *mf) {
-  unsigned  n,seen;
-  uint64_t t;
+uint64_t mkv_GetLowestQTimecode(MatroskaFile *mf)
+{
+    unsigned  n, seen;
+    uint64_t t;
 
-  // find the lowest queued timecode
-  for (n=seen=0,t=0;n<mf->nTracks;++n)
-    if (mf->Queues[n].head && (!seen || t > mf->Queues[n].head->Start))
-      t = mf->Queues[n].head->Start, seen=1;
+    // find the lowest queued timecode
+    for (n = seen = 0, t = 0; n < mf->nTracks; ++n)
+        if (mf->Queues[n].head && (!seen || t > mf->Queues[n].head->Start))
+            t = mf->Queues[n].head->Start, seen = 1;
 
-  return seen ? t : (uint64_t)LL(-1);
+    return seen ? t : (uint64_t)LL(-1);
 }
 
-int	      mkv_TruncFloat(MKFLOAT f) {
+int	      mkv_TruncFloat(MKFLOAT f)
+{
 #ifdef MATROSKA_INTEGER_ONLY
-  return (int)(f.v >> 32);
+    return (int)(f.v >> 32);
 #else
-  return (int)f;
+    return (int)f;
 #endif
 }
 
 #define	FTRACK	0xffffffff
 
-void	      mkv_SetTrackMask(MatroskaFile *mf,unsigned int mask) {
-  unsigned int	  i;
+void	      mkv_SetTrackMask(MatroskaFile *mf, unsigned int mask)
+{
+    unsigned int	  i;
 
-  if (mf->flags & MPF_ERROR)
-    return;
+    if (mf->flags & MPF_ERROR)
+        return;
 
-  mf->trackMask = mask;
+    mf->trackMask = mask;
 
-  for (i=0;i<mf->nTracks;++i)
-    if (mask & (1<<i))
-      ClearQueue(mf,&mf->Queues[i]);
+    for (i = 0; i < mf->nTracks; ++i)
+        if (mask & (1 << i))
+            ClearQueue(mf, &mf->Queues[i]);
 }
 
 int	      mkv_ReadFrame(MatroskaFile *mf,
-			    unsigned int mask,unsigned int *track,
-			    uint64_t *StartTime,uint64_t *EndTime,
-			    uint64_t *FilePos,unsigned int *FrameSize,
-			    unsigned int *FrameFlags)
+                        unsigned int mask, unsigned int *track,
+                        uint64_t *StartTime, uint64_t *EndTime,
+                        uint64_t *FilePos, unsigned int *FrameSize,
+                        unsigned int *FrameFlags)
 {
-  unsigned int	    i,j;
-  struct QueueEntry *qe;
-
-  if (setjmp(mf->jb)!=0)
-    return -1;
-
-  do {
-    // extract required frame, use block with the lowest timecode
-    for (j=FTRACK,i=0;i<mf->nTracks;++i)
-      if (!(mask & (1<<i)) && mf->Queues[i].head) {
-	j = i;
-	++i;
-	break;
-      }
-
-    for (;i<mf->nTracks;++i)
-      if (!(mask & (1<<i)) && mf->Queues[i].head &&
-	  mf->Queues[j].head->Start > mf->Queues[i].head->Start)
-	j = i;
-
-    if (j != FTRACK) {
-      qe = QGet(&mf->Queues[j]);
+    unsigned int	    i, j;
+    struct QueueEntry *qe;
 
-      *track = j;
-      *StartTime = qe->Start;
-      *EndTime = qe->End;
-      *FilePos = qe->Position;
-      *FrameSize = qe->Length;
-      *FrameFlags = qe->flags;
+    if (setjmp(mf->jb) != 0)
+        return -1;
 
-      QFree(mf,qe);
+    do {
+        // extract required frame, use block with the lowest timecode
+        for (j = FTRACK, i = 0; i < mf->nTracks; ++i)
+            if (!(mask & (1 << i)) && mf->Queues[i].head) {
+                j = i;
+                ++i;
+                break;
+            }
+
+        for (; i < mf->nTracks; ++i)
+            if (!(mask & (1 << i)) && mf->Queues[i].head &&
+                mf->Queues[j].head->Start > mf->Queues[i].head->Start)
+                j = i;
+
+        if (j != FTRACK) {
+            qe = QGet(&mf->Queues[j]);
+
+            *track = j;
+            *StartTime = qe->Start;
+            *EndTime = qe->End;
+            *FilePos = qe->Position;
+            *FrameSize = qe->Length;
+            *FrameFlags = qe->flags;
+
+            QFree(mf, qe);
+
+            return 0;
+        }
 
-      return 0;
-    }
+        if (mf->flags & MPF_ERROR)
+            return -1;
 
-    if (mf->flags & MPF_ERROR)
-      return -1;
+    } while (fillQueues(mf, mask) >= 0);
 
-  } while (fillQueues(mf,mask)>=0);
-
-  return EOF;
+    return EOF;
 }
 
 #ifdef MATROSKA_COMPRESSION_SUPPORT
@@ -3175,159 +3259,161 @@ int	      mkv_ReadFrame(MatroskaFile *mf,
  * Compressed streams support
  ************************************************************************/
 struct CompressedStream {
-  MatroskaFile	  *mf;
-  z_stream	  zs;
+    MatroskaFile	  *mf;
+    z_stream	  zs;
 
-  /* current compressed frame */
-  uint64_t	  frame_pos;
-  unsigned	  frame_size;
-  char		  frame_buffer[2048];
+    /* current compressed frame */
+    uint64_t	  frame_pos;
+    unsigned	  frame_size;
+    char		  frame_buffer[2048];
 
-  /* decoded data buffer */
-  char		  decoded_buffer[2048];
-  unsigned	  decoded_ptr;
-  unsigned	  decoded_size;
+    /* decoded data buffer */
+    char		  decoded_buffer[2048];
+    unsigned	  decoded_ptr;
+    unsigned	  decoded_size;
 
-  /* error handling */
-  char		  errmsg[128];
+    /* error handling */
+    char		  errmsg[128];
 };
 
 CompressedStream  *cs_Create(/* in */	MatroskaFile *mf,
-			     /* in */	unsigned tracknum,
-			     /* out */	char *errormsg,
-			     /* in */	unsigned msgsize)
-{
-  CompressedStream  *cs;
-  TrackInfo	    *ti;
-  int		    code;
-
-  ti = mkv_GetTrackInfo(mf, tracknum);
-  if (ti == NULL) {
-    mystrlcpy(errormsg, "No such track.", msgsize);
-    return NULL;
-  }
+                                        /* in */	unsigned tracknum,
+                                        /* out */	char *errormsg,
+                                        /* in */	unsigned msgsize)
+{
+    CompressedStream  *cs;
+    TrackInfo	    *ti;
+    int		    code;
+
+    ti = mkv_GetTrackInfo(mf, tracknum);
+    if (ti == NULL) {
+        mystrlcpy(errormsg, "No such track.", msgsize);
+        return NULL;
+    }
 
-  if (!ti->CompEnabled) {
-    mystrlcpy(errormsg, "Track is not compressed.", msgsize);
-    return NULL;
-  }
+    if (!ti->CompEnabled) {
+        mystrlcpy(errormsg, "Track is not compressed.", msgsize);
+        return NULL;
+    }
 
-  if (ti->CompMethod != COMP_ZLIB) {
-    mystrlcpy(errormsg, "Unsupported compression method.", msgsize);
-    return NULL;
-  }
+    if (ti->CompMethod != COMP_ZLIB) {
+        mystrlcpy(errormsg, "Unsupported compression method.", msgsize);
+        return NULL;
+    }
 
-  cs = mf->cache->memalloc(mf->cache,sizeof(*cs));
-  if (cs == NULL) {
-    mystrlcpy(errormsg, "Ouf of memory.", msgsize);
-    return NULL;
-  }
+    cs = mf->cache->memalloc(mf->cache, sizeof(*cs));
+    if (cs == NULL) {
+        mystrlcpy(errormsg, "Ouf of memory.", msgsize);
+        return NULL;
+    }
 
-  memset(&cs->zs,0,sizeof(cs->zs));
-  code = inflateInit(&cs->zs);
-  if (code != Z_OK) {
-    mystrlcpy(errormsg, "ZLib error.", msgsize);
-    mf->cache->memfree(mf->cache,cs);
-    return NULL;
-  }
+    memset(&cs->zs, 0, sizeof(cs->zs));
+    code = inflateInit(&cs->zs);
+    if (code != Z_OK) {
+        mystrlcpy(errormsg, "ZLib error.", msgsize);
+        mf->cache->memfree(mf->cache, cs);
+        return NULL;
+    }
 
-  cs->frame_size = 0;
-  cs->decoded_ptr = cs->decoded_size = 0;
-  cs->mf = mf;
+    cs->frame_size = 0;
+    cs->decoded_ptr = cs->decoded_size = 0;
+    cs->mf = mf;
 
-  return cs;
+    return cs;
 }
 
-void		  cs_Destroy(/* in */ CompressedStream *cs) {
-  if (cs == NULL)
-    return;
+void		  cs_Destroy(/* in */ CompressedStream *cs)
+{
+    if (cs == NULL)
+        return;
 
-  inflateEnd(&cs->zs);
-  cs->mf->cache->memfree(cs->mf->cache,cs);
+    inflateEnd(&cs->zs);
+    cs->mf->cache->memfree(cs->mf->cache, cs);
 }
 
 /* advance to the next frame in matroska stream, you need to pass values returned
  * by mkv_ReadFrame */
 void		  cs_NextFrame(/* in */ CompressedStream *cs,
-			       /* in */ uint64_t pos,
-			       /* in */ unsigned size)
+                                    /* in */ uint64_t pos,
+                                    /* in */ unsigned size)
 {
-  cs->zs.avail_in = 0;
-  inflateReset(&cs->zs);
-  cs->frame_pos = pos;
-  cs->frame_size = size;
-  cs->decoded_ptr = cs->decoded_size = 0;
+    cs->zs.avail_in = 0;
+    inflateReset(&cs->zs);
+    cs->frame_pos = pos;
+    cs->frame_size = size;
+    cs->decoded_ptr = cs->decoded_size = 0;
 }
 
 /* read and decode more data from current frame, return number of bytes decoded,
  * 0 on end of frame, or -1 on error */
-int		  cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize)
-{
-  char	    *cp = buffer;
-  unsigned  rd = 0;
-  unsigned  todo;
-  int	    code;
-  
-  do {
-    /* try to copy data from decoded buffer */
-    if (cs->decoded_ptr < cs->decoded_size) {
-      todo = cs->decoded_size - cs->decoded_ptr;;
-      if (todo > bufsize - rd)
-	todo = bufsize - rd;
-
-      memcpy(cp, cs->decoded_buffer + cs->decoded_ptr, todo);
-
-      rd += todo;
-      cp += todo;
-      cs->decoded_ptr += todo;
-    } else {
-      /* setup output buffer */
-      cs->zs.next_out = (Bytef *)cs->decoded_buffer;
-      cs->zs.avail_out = sizeof(cs->decoded_buffer);
-
-      /* try to read more data */
-      if (cs->zs.avail_in == 0 && cs->frame_size > 0) {
-	todo = cs->frame_size;
-	if (todo > sizeof(cs->frame_buffer))
-	  todo = sizeof(cs->frame_buffer);
-
-	if (cs->mf->cache->read(cs->mf->cache, cs->frame_pos, cs->frame_buffer, todo) != (int)todo) {
-	  mystrlcpy(cs->errmsg, "File read failed", sizeof(cs->errmsg));
-	  return -1;
-	}
-
-	cs->zs.next_in = (Bytef *)cs->frame_buffer;
-	cs->zs.avail_in = todo;
-
-	cs->frame_pos += todo;
-	cs->frame_size -= todo;
-      }
+int		  cs_ReadData(CompressedStream *cs, char *buffer, unsigned bufsize)
+{
+    char	    *cp = buffer;
+    unsigned  rd = 0;
+    unsigned  todo;
+    int	    code;
 
-      /* try to decode more data */
-      code = inflate(&cs->zs,Z_NO_FLUSH);
-      if (code != Z_OK && code != Z_STREAM_END) {
-	mystrlcpy(cs->errmsg, "ZLib error.", sizeof(cs->errmsg));
-	return -1;
-      }
+    do {
+        /* try to copy data from decoded buffer */
+        if (cs->decoded_ptr < cs->decoded_size) {
+            todo = cs->decoded_size - cs->decoded_ptr;;
+            if (todo > bufsize - rd)
+                todo = bufsize - rd;
 
-      /* handle decoded data */
-      if (cs->zs.avail_out == sizeof(cs->decoded_buffer)) /* EOF */
-	break;
+            memcpy(cp, cs->decoded_buffer + cs->decoded_ptr, todo);
 
-      cs->decoded_ptr = 0;
-      cs->decoded_size = sizeof(cs->decoded_buffer) - cs->zs.avail_out;
-    }
-  } while (rd < bufsize);
+            rd += todo;
+            cp += todo;
+            cs->decoded_ptr += todo;
+        }
+        else {
+            /* setup output buffer */
+            cs->zs.next_out = (Bytef *)cs->decoded_buffer;
+            cs->zs.avail_out = sizeof(cs->decoded_buffer);
+
+            /* try to read more data */
+            if (cs->zs.avail_in == 0 && cs->frame_size > 0) {
+                todo = cs->frame_size;
+                if (todo > sizeof(cs->frame_buffer))
+                    todo = sizeof(cs->frame_buffer);
+
+                if (cs->mf->cache->read(cs->mf->cache, cs->frame_pos, cs->frame_buffer, todo) != (int)todo) {
+                    mystrlcpy(cs->errmsg, "File read failed", sizeof(cs->errmsg));
+                    return -1;
+                }
+
+                cs->zs.next_in = (Bytef *)cs->frame_buffer;
+                cs->zs.avail_in = todo;
+
+                cs->frame_pos += todo;
+                cs->frame_size -= todo;
+            }
+
+            /* try to decode more data */
+            code = inflate(&cs->zs, Z_NO_FLUSH);
+            if (code != Z_OK && code != Z_STREAM_END) {
+                mystrlcpy(cs->errmsg, "ZLib error.", sizeof(cs->errmsg));
+                return -1;
+            }
+
+            /* handle decoded data */
+            if (cs->zs.avail_out == sizeof(cs->decoded_buffer)) /* EOF */
+                break;
+
+            cs->decoded_ptr = 0;
+            cs->decoded_size = sizeof(cs->decoded_buffer) - cs->zs.avail_out;
+        }
+    } while (rd < bufsize);
 
-  return rd;
+    return rd;
 }
 
 /* return error message for the last error */
 const char	  *cs_GetLastError(CompressedStream *cs)
 {
-  if (!cs->errmsg[0])
-    return NULL;
-  return cs->errmsg;
+    if (!cs->errmsg[0])
+        return NULL;
+    return cs->errmsg;
 }
 #endif
 
diff --git a/src/MatroskaParser.h b/src/MatroskaParser.h
index 4d3d6634792bea33090fa65acd5c7a6a3fcd0f0f..d19f1caf20a3ee632f61a9363571b763a92e9043 100644
--- a/src/MatroskaParser.h
+++ b/src/MatroskaParser.h
@@ -62,7 +62,7 @@ extern "C" {
 /* MKFLOATing point */
 #ifdef MATROSKA_INTEGER_ONLY
 typedef struct {
-  int64_t	v;
+    int64_t	v;
 } MKFLOAT;
 #else
 typedef	double	MKFLOAT;
@@ -70,22 +70,22 @@ typedef	double	MKFLOAT;
 
 /* generic I/O */
 struct InputStream {
-  /* read bytes from stream */
-  int	      (*read)(struct InputStream *cc,uint64_t pos,void *buffer,int count);
-  /* scan for a four byte signature, bytes must be nonzero */
-  int64_t    (*scan)(struct InputStream *cc,uint64_t start,unsigned signature);
-  /* get cache size, this is used to cap readahead */
-  unsigned    (*getcachesize)(struct InputStream *cc);
-  /* fetch last error message */
-  const char *(*geterror)(struct InputStream *cc);
-  /* memory allocation */
-  void	      *(*memalloc)(struct InputStream *cc,size_t size);
-  void	      *(*memrealloc)(struct InputStream *cc,void *mem,size_t newsize);
-  void	      (*memfree)(struct InputStream *cc,void *mem);
-  /* zero return causes parser to abort open */
-  int	      (*progress)(struct InputStream *cc,uint64_t cur,uint64_t max);
-  /* get file size, optional, can be NULL or return -1 if filesize is unknown */
-  int64_t    (*getfilesize)(struct InputStream *cc);
+    /* read bytes from stream */
+    int	      (*read)(struct InputStream *cc, uint64_t pos, void *buffer, int count);
+    /* scan for a four byte signature, bytes must be nonzero */
+    int64_t    (*scan)(struct InputStream *cc, uint64_t start, unsigned signature);
+    /* get cache size, this is used to cap readahead */
+    unsigned    (*getcachesize)(struct InputStream *cc);
+    /* fetch last error message */
+    const char *(*geterror)(struct InputStream *cc);
+    /* memory allocation */
+    void	      *(*memalloc)(struct InputStream *cc, size_t size);
+    void	      *(*memrealloc)(struct InputStream *cc, void *mem, size_t newsize);
+    void	      (*memfree)(struct InputStream *cc, void *mem);
+    /* zero return causes parser to abort open */
+    int	      (*progress)(struct InputStream *cc, uint64_t cur, uint64_t max);
+    /* get file size, optional, can be NULL or return -1 if filesize is unknown */
+    int64_t    (*getfilesize)(struct InputStream *cc);
 };
 
 typedef struct InputStream InputStream;
@@ -105,129 +105,129 @@ typedef struct MatroskaFile MatroskaFile;
 #define	TT_SUB	    17
 
 struct TrackInfo {
-  unsigned char	  Number;
-  unsigned char	  Type;
-  unsigned char	  TrackOverlay;
-  uint64_t	  UID;
-  uint64_t	  MinCache;
-  uint64_t	  MaxCache;
-  uint64_t	  DefaultDuration;
-  MKFLOAT	  TimecodeScale;
-  void		  *CodecPrivate;
-  unsigned	  CodecPrivateSize;
-  unsigned	  CompMethod;
-  void		  *CompMethodPrivate;
-  unsigned	  CompMethodPrivateSize;
-  unsigned	  MaxBlockAdditionID;
-
-  unsigned int  Enabled:1;
-  unsigned int  Default:1;
-  unsigned int  Lacing:1;
-  unsigned int  DecodeAll:1;
-  unsigned int  CompEnabled:1;
-
-  union {
-    struct {
-      unsigned char   StereoMode;
-      unsigned char   DisplayUnit;
-      unsigned char   AspectRatioType;
-      unsigned int    PixelWidth;
-      unsigned int    PixelHeight;
-      unsigned int    DisplayWidth;
-      unsigned int    DisplayHeight;
-      unsigned int    CropL, CropT, CropR, CropB;
-      unsigned int    ColourSpace;
-      MKFLOAT	      GammaValue;
-
-      unsigned int  Interlaced:1;
-    } Video;
-    struct {
-      MKFLOAT	      SamplingFreq;
-      MKFLOAT	      OutputSamplingFreq;
-      unsigned char   Channels;
-      unsigned char   BitDepth;
-    } Audio;
-  } AV;
-
-  /* various strings */
-  char			*Name;
-  char			Language[4];
-  char			*CodecID;
+    unsigned char	  Number;
+    unsigned char	  Type;
+    unsigned char	  TrackOverlay;
+    uint64_t	  UID;
+    uint64_t	  MinCache;
+    uint64_t	  MaxCache;
+    uint64_t	  DefaultDuration;
+    MKFLOAT	  TimecodeScale;
+    void		  *CodecPrivate;
+    unsigned	  CodecPrivateSize;
+    unsigned	  CompMethod;
+    void		  *CompMethodPrivate;
+    unsigned	  CompMethodPrivateSize;
+    unsigned	  MaxBlockAdditionID;
+
+    unsigned int  Enabled: 1;
+    unsigned int  Default: 1;
+    unsigned int  Lacing: 1;
+    unsigned int  DecodeAll: 1;
+    unsigned int  CompEnabled: 1;
+
+    union {
+        struct {
+            unsigned char   StereoMode;
+            unsigned char   DisplayUnit;
+            unsigned char   AspectRatioType;
+            unsigned int    PixelWidth;
+            unsigned int    PixelHeight;
+            unsigned int    DisplayWidth;
+            unsigned int    DisplayHeight;
+            unsigned int    CropL, CropT, CropR, CropB;
+            unsigned int    ColourSpace;
+            MKFLOAT	      GammaValue;
+
+            unsigned int  Interlaced: 1;
+        } Video;
+        struct {
+            MKFLOAT	      SamplingFreq;
+            MKFLOAT	      OutputSamplingFreq;
+            unsigned char   Channels;
+            unsigned char   BitDepth;
+        } Audio;
+    } AV;
+
+    /* various strings */
+    char			*Name;
+    char			Language[4];
+    char			*CodecID;
 };
 
 typedef struct TrackInfo  TrackInfo;
 
 struct SegmentInfo {
-  char			UID[16];
-  char			PrevUID[16];
-  char			NextUID[16];
-  char			*Filename;
-  char			*PrevFilename;
-  char			*NextFilename;
-  char			*Title;
-  char			*MuxingApp;
-  char			*WritingApp;
-  uint64_t		TimecodeScale;
-  uint64_t		Duration;
-  int64_t		DateUTC;
-  char			DateUTCValid;
+    char			UID[16];
+    char			PrevUID[16];
+    char			NextUID[16];
+    char			*Filename;
+    char			*PrevFilename;
+    char			*NextFilename;
+    char			*Title;
+    char			*MuxingApp;
+    char			*WritingApp;
+    uint64_t		TimecodeScale;
+    uint64_t		Duration;
+    int64_t		DateUTC;
+    char			DateUTCValid;
 };
 
 typedef struct SegmentInfo SegmentInfo;
 
 struct Attachment {
-  uint64_t		Position;
-  uint64_t		Length;
-  uint64_t		UID;
-  char			*Name;
-  char			*Description;
-  char			*MimeType;
+    uint64_t		Position;
+    uint64_t		Length;
+    uint64_t		UID;
+    char			*Name;
+    char			*Description;
+    char			*MimeType;
 };
 
 typedef struct Attachment Attachment;
 
 struct ChapterDisplay {
-  char			*String;
-  char			Language[4];
-  char			Country[4];
+    char			*String;
+    char			Language[4];
+    char			Country[4];
 };
 
 struct ChapterCommand {
-  unsigned		Time;
-  unsigned		CommandLength;
-  void			*Command;
+    unsigned		Time;
+    unsigned		CommandLength;
+    void			*Command;
 };
 
 struct ChapterProcess {
-  unsigned		CodecID;
-  unsigned		CodecPrivateLength;
-  void			*CodecPrivate;
-  unsigned		nCommands,nCommandsSize;
-  struct ChapterCommand	*Commands;
+    unsigned		CodecID;
+    unsigned		CodecPrivateLength;
+    void			*CodecPrivate;
+    unsigned		nCommands, nCommandsSize;
+    struct ChapterCommand	*Commands;
 };
 
 struct Chapter {
-  uint64_t		UID;
-  uint64_t		Start;
-  uint64_t		End;
-
-  unsigned		nTracks,nTracksSize;
-  uint64_t		*Tracks;
-  unsigned		nDisplay,nDisplaySize;
-  struct ChapterDisplay	*Display;
-  unsigned		nChildren,nChildrenSize;
-  struct Chapter	*Children;
-  unsigned		nProcess,nProcessSize;
-  struct ChapterProcess	*Process;
-
-  char			SegmentUID[16];
-
-  unsigned int	Hidden:1;
-  unsigned int	Enabled:1;
-
-  // Editions
-  unsigned int	Default:1;
-  unsigned int	Ordered:1;
+    uint64_t		UID;
+    uint64_t		Start;
+    uint64_t		End;
+
+    unsigned		nTracks, nTracksSize;
+    uint64_t		*Tracks;
+    unsigned		nDisplay, nDisplaySize;
+    struct ChapterDisplay	*Display;
+    unsigned		nChildren, nChildrenSize;
+    struct Chapter	*Children;
+    unsigned		nProcess, nProcessSize;
+    struct ChapterProcess	*Process;
+
+    char			SegmentUID[16];
+
+    unsigned int	Hidden: 1;
+    unsigned int	Enabled: 1;
+
+    // Editions
+    unsigned int	Default: 1;
+    unsigned int	Ordered: 1;
 };
 
 typedef struct Chapter	Chapter;
@@ -237,23 +237,23 @@ typedef struct Chapter	Chapter;
 #define	TARGET_ATTACHMENT 2
 #define	TARGET_EDITION	  3
 struct Target {
-  uint64_t	    UID;
-  unsigned	    Type;
+    uint64_t	    UID;
+    unsigned	    Type;
 };
 
 struct SimpleTag {
-  char		    *Name;
-  char		    *Value;
-  char		    Language[4];
-  unsigned	    Default:1;
+    char		    *Name;
+    char		    *Value;
+    char		    Language[4];
+    unsigned	    Default: 1;
 };
 
 struct Tag {
-  unsigned	    nTargets,nTargetsSize;
-  struct Target	    *Targets;
+    unsigned	    nTargets, nTargetsSize;
+    struct Target	    *Targets;
 
-  unsigned	    nSimpleTags,nSimpleTagsSize;
-  struct SimpleTag  *SimpleTags;
+    unsigned	    nSimpleTags, nSimpleTagsSize;
+    struct SimpleTag  *SimpleTags;
 };
 
 typedef struct Tag  Tag;
@@ -262,16 +262,16 @@ typedef struct Tag  Tag;
  * io pointer is recorded inside MatroskaFile
  */
 X MatroskaFile  *mkv_Open(/* in */ InputStream *io,
-			/* out */ char *err_msg,
-			/* in */  unsigned msgsize);
+                                   /* out */ char *err_msg,
+                                   /* in */  unsigned msgsize);
 
 #define	MKVF_AVOID_SEEKS    1 /* use sequential reading only */
 
 X MatroskaFile  *mkv_OpenEx(/* in */  InputStream *io,
-			  /* in */  uint64_t base,
-			  /* in */  unsigned flags,
-			  /* out */ char *err_msg,
-			  /* in */  unsigned msgsize);
+                                      /* in */  uint64_t base,
+                                      /* in */  unsigned flags,
+                                      /* out */ char *err_msg,
+                                      /* in */  unsigned msgsize);
 
 /* Close and deallocate mf
  * NULL pointer is ok and is simply ignored
@@ -290,14 +290,14 @@ X TrackInfo     *mkv_GetTrackInfo(/* in */ MatroskaFile *mf,/* in */ unsigned tr
 
 /* chapters, tags and attachments */
 X void	      mkv_GetAttachments(/* in */   MatroskaFile *mf,
-				 /* out */  Attachment **at,
-				 /* out */  unsigned *count);
+        /* out */  Attachment **at,
+        /* out */  unsigned *count);
 X void	      mkv_GetChapters(/* in */	MatroskaFile *mf,
-			      /* out */	Chapter **ch,
-			      /* out */ unsigned *count);
+                                        /* out */	Chapter **ch,
+                                        /* out */ unsigned *count);
 X void	      mkv_GetTags(/* in */  MatroskaFile *mf,
-			  /* out */ Tag **tag,
-			  /* out */ unsigned *count);
+                                    /* out */ Tag **tag,
+                                    /* out */ unsigned *count);
 
 X uint64_t   mkv_GetSegmentTop(MatroskaFile *mf);
 
@@ -310,8 +310,8 @@ X uint64_t   mkv_GetSegmentTop(MatroskaFile *mf);
 #define	MKVF_SEEK_TO_PREV_KEYFRAME_STRICT   2
 
 X void	      mkv_Seek(/* in */ MatroskaFile *mf,
-		       /* in */	uint64_t timecode /* in ns */,
-		       /* in */ unsigned flags);
+                                /* in */	uint64_t timecode /* in ns */,
+                                /* in */ unsigned flags);
 
 X void	      mkv_SkipToKeyframe(MatroskaFile *mf);
 
@@ -344,13 +344,13 @@ X void	      mkv_SetTrackMask(/* in */ MatroskaFile *mf,/* in */ unsigned int ma
  * set of tracks, 0 on success
  */
 X int	      mkv_ReadFrame(/* in */  MatroskaFile *mf,
-			    /* in */  unsigned int mask,
-			    /* out */ unsigned int *track,
-			    /* out */ uint64_t *StartTime /* in ns */,
-			    /* out */ uint64_t *EndTime /* in ns */,
-			    /* out */ uint64_t *FilePos /* in bytes from start of file */,
-			    /* out */ unsigned int *FrameSize /* in bytes */,
-			    /* out */ unsigned int *FrameFlags);
+                                      /* in */  unsigned int mask,
+                                      /* out */ unsigned int *track,
+                                      /* out */ uint64_t *StartTime /* in ns */,
+                                      /* out */ uint64_t *EndTime /* in ns */,
+                                      /* out */ uint64_t *FilePos /* in bytes from start of file */,
+                                      /* out */ unsigned int *FrameSize /* in bytes */,
+                                      /* out */ unsigned int *FrameFlags);
 
 #ifdef MATROSKA_COMPRESSION_SUPPORT
 /* Compressed streams support */
@@ -359,20 +359,20 @@ struct CompressedStream;
 typedef struct CompressedStream CompressedStream;
 
 X CompressedStream  *cs_Create(/* in */	MatroskaFile *mf,
-			     /* in */	unsigned tracknum,
-			     /* out */	char *errormsg,
-			     /* in */	unsigned msgsize);
+                                        /* in */	unsigned tracknum,
+                                        /* out */	char *errormsg,
+                                        /* in */	unsigned msgsize);
 X void		  cs_Destroy(/* in */ CompressedStream *cs);
 
 /* advance to the next frame in matroska stream, you need to pass values returned
  * by mkv_ReadFrame */
 X void		  cs_NextFrame(/* in */ CompressedStream *cs,
-			       /* in */ uint64_t pos,
-			       /* in */ unsigned size);
+                                    /* in */ uint64_t pos,
+                                    /* in */ unsigned size);
 
 /* read and decode more data from current frame, return number of bytes decoded,
  * 0 on end of frame, or -1 on error */
-X int		  cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize);
+X int		  cs_ReadData(CompressedStream *cs, char *buffer, unsigned bufsize);
 
 /* return error message for the last error */
 X const char	  *cs_GetLastError(CompressedStream *cs);
diff --git a/src/aegisublocale.cpp b/src/aegisublocale.cpp
index de8df7296d7742508c42305da65acc0e05e5a5c7..822851d12ead22ae3b5c6e4511c38bfe1b45a047 100644
--- a/src/aegisublocale.cpp
+++ b/src/aegisublocale.cpp
@@ -50,72 +50,76 @@
 #define AEGISUB_CATALOG "aegisub"
 #endif
 
-wxTranslations *AegisubLocale::GetTranslations() {
-	wxTranslations *translations = wxTranslations::Get();
-	if (!translations) {
-		wxTranslations::Set(translations = new wxTranslations);
-		wxFileTranslationsLoader::AddCatalogLookupPathPrefix(config::path->Decode("?data/locale/").wstring());
-	}
-	return translations;
+wxTranslations *AegisubLocale::GetTranslations()
+{
+    wxTranslations *translations = wxTranslations::Get();
+    if (!translations) {
+        wxTranslations::Set(translations = new wxTranslations);
+        wxFileTranslationsLoader::AddCatalogLookupPathPrefix(config::path->Decode("?data/locale/").wstring());
+    }
+    return translations;
 }
 
-void AegisubLocale::Init(std::string const& language) {
-	wxTranslations *translations = GetTranslations();
-	translations->SetLanguage(to_wx(language));
-	translations->AddCatalog(AEGISUB_CATALOG);
-	translations->AddStdCatalog();
+void AegisubLocale::Init(std::string const &language)
+{
+    wxTranslations *translations = GetTranslations();
+    translations->SetLanguage(to_wx(language));
+    translations->AddCatalog(AEGISUB_CATALOG);
+    translations->AddStdCatalog();
 
-	setlocale(LC_NUMERIC, "C");
-	setlocale(LC_CTYPE, "C");
-	active_language = language;
+    setlocale(LC_NUMERIC, "C");
+    setlocale(LC_CTYPE, "C");
+    active_language = language;
 }
 
-bool AegisubLocale::HasLanguage(std::string const& language) {
-	auto langs = GetTranslations()->GetAvailableTranslations(AEGISUB_CATALOG);
-	return std::find(langs.begin(), langs.end(), to_wx(language)) != langs.end();
+bool AegisubLocale::HasLanguage(std::string const &language)
+{
+    auto langs = GetTranslations()->GetAvailableTranslations(AEGISUB_CATALOG);
+    return std::find(langs.begin(), langs.end(), to_wx(language)) != langs.end();
 }
 
-std::string AegisubLocale::PickLanguage() {
-	if (active_language.empty()) {
-		wxString os_ui_language = GetTranslations()->GetBestTranslation(AEGISUB_CATALOG);
-		if (!os_ui_language.empty())
-			return from_wx(os_ui_language);
-	}
-
-	wxArrayString langs = GetTranslations()->GetAvailableTranslations(AEGISUB_CATALOG);
-
-	// No translations available, so don't bother asking the user
-	if (langs.empty() && active_language.empty())
-		return "en_US";
-
-	langs.insert(langs.begin(), "en_US");
-
-	// Check if user local language is available, if so, make it first
-	const wxLanguageInfo *info = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage());
-	if (info) {
-		auto it = std::find(langs.begin(), langs.end(), info->CanonicalName);
-		if (it != langs.end())
-			std::rotate(langs.begin(), it, it + 1);
-	}
-
-	// Generate names
-	wxArrayString langNames;
-	for (auto const& lang : langs)
-		langNames.push_back(LocalizedLanguageName(lang));
-
-	long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxOK | wxCENTRE;
-	if (!active_language.empty())
-		style |= wxCANCEL;
-
-	wxSingleChoiceDialog dialog(nullptr, "Please choose a language:", "Language", langNames,
-			(void **)nullptr,
-			style);
-	if (dialog.ShowModal() == wxID_OK) {
-		int picked = dialog.GetSelection();
-		auto new_lang = from_wx(langs[picked]);
-		if (new_lang != active_language)
-			return new_lang;
-	}
-
-	return "";
+std::string AegisubLocale::PickLanguage()
+{
+    if (active_language.empty()) {
+        wxString os_ui_language = GetTranslations()->GetBestTranslation(AEGISUB_CATALOG);
+        if (!os_ui_language.empty())
+            return from_wx(os_ui_language);
+    }
+
+    wxArrayString langs = GetTranslations()->GetAvailableTranslations(AEGISUB_CATALOG);
+
+    // No translations available, so don't bother asking the user
+    if (langs.empty() && active_language.empty())
+        return "en_US";
+
+    langs.insert(langs.begin(), "en_US");
+
+    // Check if user local language is available, if so, make it first
+    const wxLanguageInfo *info = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage());
+    if (info) {
+        auto it = std::find(langs.begin(), langs.end(), info->CanonicalName);
+        if (it != langs.end())
+            std::rotate(langs.begin(), it, it + 1);
+    }
+
+    // Generate names
+    wxArrayString langNames;
+    for (auto const &lang : langs)
+        langNames.push_back(LocalizedLanguageName(lang));
+
+    long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxOK | wxCENTRE;
+    if (!active_language.empty())
+        style |= wxCANCEL;
+
+    wxSingleChoiceDialog dialog(nullptr, "Please choose a language:", "Language", langNames,
+                                (void **)nullptr,
+                                style);
+    if (dialog.ShowModal() == wxID_OK) {
+        int picked = dialog.GetSelection();
+        auto new_lang = from_wx(langs[picked]);
+        if (new_lang != active_language)
+            return new_lang;
+    }
+
+    return "";
 }
diff --git a/src/aegisublocale.h b/src/aegisublocale.h
index bb95d60ed1e17f9a61a858da4295e1e055fb19d7..f989002b7a00bb6e72754652301cfc499048d6e5 100644
--- a/src/aegisublocale.h
+++ b/src/aegisublocale.h
@@ -32,11 +32,11 @@
 class wxTranslations;
 
 class AegisubLocale {
-	std::string active_language;
-	wxTranslations *GetTranslations();
+    std::string active_language;
+    wxTranslations *GetTranslations();
 
 public:
-	void Init(std::string const& language);
-	bool HasLanguage(std::string const& language);
-	std::string PickLanguage();
+    void Init(std::string const &language);
+    bool HasLanguage(std::string const &language);
+    std::string PickLanguage();
 };
diff --git a/src/ass_attachment.cpp b/src/ass_attachment.cpp
index bc9917c64b0f4767d3fdf3a0961bd77a199abd61..0d0659c234da17208b3f39b9f223e702b70bc6e4 100644
--- a/src/ass_attachment.cpp
+++ b/src/ass_attachment.cpp
@@ -25,46 +25,49 @@
 // Out-of-line to anchor vtable
 AssEntryGroup AssAttachment::Group() const { return group; }
 
-AssAttachment::AssAttachment(std::string const& header, AssEntryGroup group)
-: entry_data(header + "\r\n")
-, filename(header.substr(10))
-, group(group)
+AssAttachment::AssAttachment(std::string const &header, AssEntryGroup group)
+    : entry_data(header + "\r\n")
+    , filename(header.substr(10))
+    , group(group)
 {
 }
 
-AssAttachment::AssAttachment(agi::fs::path const& name, AssEntryGroup group)
-: filename(name.filename().string())
-, group(group)
+AssAttachment::AssAttachment(agi::fs::path const &name, AssEntryGroup group)
+    : filename(name.filename().string())
+    , group(group)
 {
-	// SSA stuffs some information about the font in the embedded filename, but
-	// nothing else uses it so just do the absolute minimum (0 is the encoding)
-	if (boost::iends_with(filename.get(), ".ttf"))
-		filename = filename.get().substr(0, filename.get().size() - 4) + "_0" + filename.get().substr(filename.get().size() - 4);
+    // SSA stuffs some information about the font in the embedded filename, but
+    // nothing else uses it so just do the absolute minimum (0 is the encoding)
+    if (boost::iends_with(filename.get(), ".ttf"))
+        filename = filename.get().substr(0, filename.get().size() - 4) + "_0" + filename.get().substr(filename.get().size() - 4);
 
-	agi::read_file_mapping file(name);
-	auto buff = file.read();
-	entry_data = (group == AssEntryGroup::FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n";
-	entry_data = entry_data.get() + agi::ass::UUEncode(buff, buff + file.size());
+    agi::read_file_mapping file(name);
+    auto buff = file.read();
+    entry_data = (group == AssEntryGroup::FONT ? "fontname: " : "filename: ") + filename.get() + "\r\n";
+    entry_data = entry_data.get() + agi::ass::UUEncode(buff, buff + file.size());
 }
 
-size_t AssAttachment::GetSize() const {
-	auto header_end = entry_data.get().find('\n');
-	return entry_data.get().size() - header_end - 1;
+size_t AssAttachment::GetSize() const
+{
+    auto header_end = entry_data.get().find('\n');
+    return entry_data.get().size() - header_end - 1;
 }
 
-void AssAttachment::Extract(agi::fs::path const& filename) const {
-	auto header_end = entry_data.get().find('\n');
-	auto decoded = agi::ass::UUDecode(entry_data.get().c_str() + header_end + 1, &entry_data.get().back() + 1);
-	agi::io::Save(filename, true).Get().write(&decoded[0], decoded.size());
+void AssAttachment::Extract(agi::fs::path const &filename) const
+{
+    auto header_end = entry_data.get().find('\n');
+    auto decoded = agi::ass::UUDecode(entry_data.get().c_str() + header_end + 1, &entry_data.get().back() + 1);
+    agi::io::Save(filename, true).Get().write(&decoded[0], decoded.size());
 }
 
-std::string AssAttachment::GetFileName(bool raw) const {
-	if (raw || !boost::iends_with(filename.get(), ".ttf")) return filename;
+std::string AssAttachment::GetFileName(bool raw) const
+{
+    if (raw || !boost::iends_with(filename.get(), ".ttf")) return filename;
 
-	// Remove stuff after last underscore if it's a font
-	std::string::size_type last_under = filename.get().rfind('_');
-	if (last_under == std::string::npos)
-		return filename;
+    // Remove stuff after last underscore if it's a font
+    std::string::size_type last_under = filename.get().rfind('_');
+    if (last_under == std::string::npos)
+        return filename;
 
-	return filename.get().substr(0, last_under) + ".ttf";
+    return filename.get().substr(0, last_under) + ".ttf";
 }
diff --git a/src/ass_attachment.h b/src/ass_attachment.h
index d99e0fbf70c7b9f76053a094375acd558d17b6f8..0c53a116b4825997eae46ec6865d64574d2c3073 100644
--- a/src/ass_attachment.h
+++ b/src/ass_attachment.h
@@ -22,33 +22,33 @@
 
 /// @class AssAttachment
 class AssAttachment final : public AssEntry {
-	/// ASS uuencoded entry data, including header.
-	boost::flyweight<std::string> entry_data;
+    /// ASS uuencoded entry data, including header.
+    boost::flyweight<std::string> entry_data;
 
-	/// Name of the attached file, with SSA font mangling if it is a ttf
-	boost::flyweight<std::string> filename;
+    /// Name of the attached file, with SSA font mangling if it is a ttf
+    boost::flyweight<std::string> filename;
 
-	AssEntryGroup group;
+    AssEntryGroup group;
 
 public:
-	/// Get the size of the attached file in bytes
-	size_t GetSize() const;
+    /// Get the size of the attached file in bytes
+    size_t GetSize() const;
 
-	/// Add a line of data (without newline) read from a subtitle file
-	void AddData(std::string const& data) { entry_data = entry_data.get() + data + "\r\n"; }
+    /// Add a line of data (without newline) read from a subtitle file
+    void AddData(std::string const &data) { entry_data = entry_data.get() + data + "\r\n"; }
 
-	/// Extract the contents of this attachment to a file
-	/// @param filename Path to save the attachment to
-	void Extract(agi::fs::path const& filename) const;
+    /// Extract the contents of this attachment to a file
+    /// @param filename Path to save the attachment to
+    void Extract(agi::fs::path const &filename) const;
 
-	/// Get the name of the attached file
-	/// @param raw If false, remove the SSA filename mangling
-	std::string GetFileName(bool raw=false) const;
+    /// Get the name of the attached file
+    /// @param raw If false, remove the SSA filename mangling
+    std::string GetFileName(bool raw = false) const;
 
-	std::string const& GetEntryData() const { return entry_data; }
-	AssEntryGroup Group() const override;
+    std::string const &GetEntryData() const { return entry_data; }
+    AssEntryGroup Group() const override;
 
-	AssAttachment(AssAttachment const& rgt) = default;
-	AssAttachment(std::string const& header, AssEntryGroup group);
-	AssAttachment(agi::fs::path const& name, AssEntryGroup group);
+    AssAttachment(AssAttachment const &rgt) = default;
+    AssAttachment(std::string const &header, AssEntryGroup group);
+    AssAttachment(agi::fs::path const &name, AssEntryGroup group);
 };
diff --git a/src/ass_dialogue.cpp b/src/ass_dialogue.cpp
index 88ce9386ac9453bdfa298c64e7906687c0a6b079..c45f4d6da1a12e130af35f35e642c29d6a4833a4 100644
--- a/src/ass_dialogue.cpp
+++ b/src/ass_dialogue.cpp
@@ -51,238 +51,250 @@ using namespace boost::adaptors;
 
 static int next_id = 0;
 
-AssDialogue::AssDialogue() {
-	Id = ++next_id;
+AssDialogue::AssDialogue()
+{
+    Id = ++next_id;
 }
 
-AssDialogue::AssDialogue(AssDialogue const& that)
-: AssDialogueBase(that)
-, AssEntryListHook(that)
+AssDialogue::AssDialogue(AssDialogue const &that)
+    : AssDialogueBase(that)
+    , AssEntryListHook(that)
 {
-	Id = ++next_id;
+    Id = ++next_id;
 }
 
-AssDialogue::AssDialogue(AssDialogueBase const& that) : AssDialogueBase(that) { }
+AssDialogue::AssDialogue(AssDialogueBase const &that) : AssDialogueBase(that) { }
 
-AssDialogue::AssDialogue(std::string const& data) {
-	Id = ++next_id;
-	Parse(data);
+AssDialogue::AssDialogue(std::string const &data)
+{
+    Id = ++next_id;
+    Parse(data);
 }
 
 AssDialogue::~AssDialogue () { }
 
 class tokenizer {
-	agi::StringRange str;
-	agi::split_iterator<agi::StringRange::const_iterator> pos;
+    agi::StringRange str;
+    agi::split_iterator<agi::StringRange::const_iterator> pos;
 
 public:
-	tokenizer(agi::StringRange const& str) : str(str) , pos(agi::Split(str, ',')) { }
+    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()));
-		return *pos++;
-	}
+    agi::StringRange next_tok() {
+        if (pos.eof())
+            throw SubtitleFormatParseError("Failed parsing line: " + std::string(str.begin(), str.end()));
+        return *pos++;
+    }
 
-	std::string next_str() { return agi::str(next_tok()); }
-	std::string next_str_trim() { return agi::str(boost::trim_copy(next_tok())); }
+    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;
-		str = agi::StringRange(raw.begin() + 10, raw.end());
-	}
-	else if (boost::starts_with(raw, "Comment:")) {
-		Comment = true;
-		str = agi::StringRange(raw.begin() + 9, raw.end());
-	}
-	else
-		throw SubtitleFormatParseError("Failed parsing line: " + raw);
-
-	tokenizer tkn(str);
-
-	// Get first token and see if it has "Marked=" in it
-	auto tmp = tkn.next_str_trim();
-	bool ssa = boost::istarts_with(tmp, "marked=");
-
-	// Get layer number
-	if (ssa)
-		Layer = 0;
-	else
-		Layer = boost::lexical_cast<int>(tmp);
-
-	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<int>(tkn.next_str()), 9999);
-	Effect = tkn.next_str_trim();
-
-	std::string text{tkn.next_tok().begin(), str.end()};
-
-	if (text.size() > 1 && text[0] == '{' && text[1] == '=') {
-		static const boost::regex extradata_test("^\\{(=\\d+)+\\}");
-		boost::match_results<std::string::iterator> rematch;
-		if (boost::regex_search(text.begin(), text.end(), rematch, extradata_test)) {
-			std::string extradata_str = rematch.str(0);
-			text = rematch.suffix().str();
-
-			static const boost::regex idmatcher("=(\\d+)");
-			auto start = extradata_str.begin();
-			auto end = extradata_str.end();
-			std::vector<uint32_t> ids;
-			while (boost::regex_search(start, end, rematch, idmatcher)) {
-				auto id = boost::lexical_cast<uint32_t>(rematch.str(1));
-				ids.push_back(id);
-				start = rematch.suffix().first;
-			}
-			ExtradataIds = ids;
-		}
-	}
-
-	Text = text;
+void AssDialogue::Parse(std::string const &raw)
+{
+    agi::StringRange str;
+    if (boost::starts_with(raw, "Dialogue:")) {
+        Comment = false;
+        str = agi::StringRange(raw.begin() + 10, raw.end());
+    }
+    else if (boost::starts_with(raw, "Comment:")) {
+        Comment = true;
+        str = agi::StringRange(raw.begin() + 9, raw.end());
+    }
+    else
+        throw SubtitleFormatParseError("Failed parsing line: " + raw);
+
+    tokenizer tkn(str);
+
+    // Get first token and see if it has "Marked=" in it
+    auto tmp = tkn.next_str_trim();
+    bool ssa = boost::istarts_with(tmp, "marked=");
+
+    // Get layer number
+    if (ssa)
+        Layer = 0;
+    else
+        Layer = boost::lexical_cast<int>(tmp);
+
+    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<int>(tkn.next_str()), 9999);
+    Effect = tkn.next_str_trim();
+
+    std::string text{tkn.next_tok().begin(), str.end()};
+
+    if (text.size() > 1 && text[0] == '{' && text[1] == '=') {
+        static const boost::regex extradata_test("^\\{(=\\d+)+\\}");
+        boost::match_results<std::string::iterator> rematch;
+        if (boost::regex_search(text.begin(), text.end(), rematch, extradata_test)) {
+            std::string extradata_str = rematch.str(0);
+            text = rematch.suffix().str();
+
+            static const boost::regex idmatcher("=(\\d+)");
+            auto start = extradata_str.begin();
+            auto end = extradata_str.end();
+            std::vector<uint32_t> ids;
+            while (boost::regex_search(start, end, rematch, idmatcher)) {
+                auto id = boost::lexical_cast<uint32_t>(rematch.str(1));
+                ids.push_back(id);
+                start = rematch.suffix().first;
+            }
+            ExtradataIds = ids;
+        }
+    }
+
+    Text = text;
 }
 
-static void append_int(std::string &str, int v) {
-	boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, v);
-	str += ',';
+static 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(std::string &out, std::string const& str) {
-	out += str;
-	out += ',';
+static void append_str(std::string &out, std::string const &str)
+{
+    out += str;
+    out += ',';
 }
 
-static void append_unsafe_str(std::string &out, std::string const& str) {
-	for (auto c : str) {
-		if (c == ',')
-			out += ';';
-		else
-			out += c;
-	}
-	out += ',';
+static void append_unsafe_str(std::string &out, std::string const &str)
+{
+    for (auto c : str) {
+        if (c == ',')
+            out += ';';
+        else
+            out += c;
+    }
+    out += ',';
 }
 
-std::string AssDialogue::GetEntryData() const {
-	std::string str = Comment ? "Comment: " : "Dialogue: ";
-	str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
-
-	append_int(str, Layer);
-	append_str(str, Start.GetAssFormatted());
-	append_str(str, End.GetAssFormatted());
-	append_unsafe_str(str, Style);
-	append_unsafe_str(str, Actor);
-	for (auto margin : Margin)
-		append_int(str, margin);
-	append_unsafe_str(str, Effect);
-
-	if (ExtradataIds.get().size() > 0) {
-		str += '{';
-		for (auto id : ExtradataIds.get()) {
-			str += '=';
-			boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, id);
-		}
-		str += '}';
-	}
-
-	for (auto c : Text.get()) {
-		if (c != '\n' && c != '\r')
-			str += c;
-	}
-
-	return str;
+std::string AssDialogue::GetEntryData() const
+{
+    std::string str = Comment ? "Comment: " : "Dialogue: ";
+    str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size());
+
+    append_int(str, Layer);
+    append_str(str, Start.GetAssFormatted());
+    append_str(str, End.GetAssFormatted());
+    append_unsafe_str(str, Style);
+    append_unsafe_str(str, Actor);
+    for (auto margin : Margin)
+        append_int(str, margin);
+    append_unsafe_str(str, Effect);
+
+    if (ExtradataIds.get().size() > 0) {
+        str += '{';
+        for (auto id : ExtradataIds.get()) {
+            str += '=';
+            boost::spirit::karma::generate(back_inserter(str), boost::spirit::karma::int_, id);
+        }
+        str += '}';
+    }
+
+    for (auto c : Text.get()) {
+        if (c != '\n' && c != '\r')
+            str += c;
+    }
+
+    return str;
 }
 
-std::vector<std::unique_ptr<AssDialogueBlock>> AssDialogue::ParseTags() const {
-	std::vector<std::unique_ptr<AssDialogueBlock>> Blocks;
-
-	// Empty line, make an empty block
-	if (Text.get().empty()) {
-		Blocks.push_back(agi::make_unique<AssDialogueBlockPlain>());
-		return Blocks;
-	}
-
-	int drawingLevel = 0;
-	std::string const& text(Text.get());
-
-	for (size_t len = text.size(), cur = 0; cur < len; ) {
-		// Overrides block
-		if (text[cur] == '{') {
-			size_t end = text.find('}', cur);
-
-			// VSFilter requires that override blocks be closed, while libass
-			// does not. We match VSFilter here.
-			if (end == std::string::npos)
-				goto plain;
-
-			++cur;
-			// Get contents of block
-			std::string work = text.substr(cur, end - cur);
-			cur = end + 1;
-
-			if (work.size() && work.find('\\') == std::string::npos) {
-				//We've found an override block with no backslashes
-				//We're going to assume it's a comment and not consider it an override block
-				Blocks.push_back(agi::make_unique<AssDialogueBlockComment>(work));
-			}
-			else {
-				// Create block
-				auto block = agi::make_unique<AssDialogueBlockOverride>(work);
-				block->ParseTags();
-
-				// Look for \p in block
-				for (auto const& tag : block->Tags) {
-					if (tag.Name == "\\p")
-						drawingLevel = tag.Params[0].Get<int>(0);
-				}
-				Blocks.push_back(std::move(block));
-			}
-
-			continue;
-		}
-
-		// Plain-text/drawing block
+std::vector<std::unique_ptr<AssDialogueBlock>> AssDialogue::ParseTags() const
+{
+    std::vector<std::unique_ptr<AssDialogueBlock>> Blocks;
+
+    // Empty line, make an empty block
+    if (Text.get().empty()) {
+        Blocks.push_back(agi::make_unique<AssDialogueBlockPlain>());
+        return Blocks;
+    }
+
+    int drawingLevel = 0;
+    std::string const &text(Text.get());
+
+    for (size_t len = text.size(), cur = 0; cur < len; ) {
+        // Overrides block
+        if (text[cur] == '{') {
+            size_t end = text.find('}', cur);
+
+            // VSFilter requires that override blocks be closed, while libass
+            // does not. We match VSFilter here.
+            if (end == std::string::npos)
+                goto plain;
+
+            ++cur;
+            // Get contents of block
+            std::string work = text.substr(cur, end - cur);
+            cur = end + 1;
+
+            if (work.size() && work.find('\\') == std::string::npos) {
+                //We've found an override block with no backslashes
+                //We're going to assume it's a comment and not consider it an override block
+                Blocks.push_back(agi::make_unique<AssDialogueBlockComment>(work));
+            }
+            else {
+                // Create block
+                auto block = agi::make_unique<AssDialogueBlockOverride>(work);
+                block->ParseTags();
+
+                // Look for \p in block
+                for (auto const &tag : block->Tags) {
+                    if (tag.Name == "\\p")
+                        drawingLevel = tag.Params[0].Get<int>(0);
+                }
+                Blocks.push_back(std::move(block));
+            }
+
+            continue;
+        }
+
+        // Plain-text/drawing block
 plain:
-		std::string work;
-		size_t end = text.find('{', cur + 1);
-		if (end == std::string::npos) {
-			work = text.substr(cur);
-			cur = len;
-		}
-		else {
-			work = text.substr(cur, end - cur);
-			cur = end;
-		}
-
-		if (drawingLevel == 0)
-			Blocks.push_back(agi::make_unique<AssDialogueBlockPlain>(work));
-		else
-			Blocks.push_back(agi::make_unique<AssDialogueBlockDrawing>(work, drawingLevel));
-	}
-
-	return Blocks;
+        std::string work;
+        size_t end = text.find('{', cur + 1);
+        if (end == std::string::npos) {
+            work = text.substr(cur);
+            cur = len;
+        }
+        else {
+            work = text.substr(cur, end - cur);
+            cur = end;
+        }
+
+        if (drawingLevel == 0)
+            Blocks.push_back(agi::make_unique<AssDialogueBlockPlain>(work));
+        else
+            Blocks.push_back(agi::make_unique<AssDialogueBlockDrawing>(work, drawingLevel));
+    }
+
+    return Blocks;
 }
 
-void AssDialogue::StripTags() {
-	Text = GetStrippedText();
+void AssDialogue::StripTags()
+{
+    Text = GetStrippedText();
 }
 
 static std::string get_text(std::unique_ptr<AssDialogueBlock> &d) { return d->GetText(); }
-void AssDialogue::UpdateText(std::vector<std::unique_ptr<AssDialogueBlock>>& blocks) {
-	if (blocks.empty()) return;
-	Text = join(blocks | transformed(get_text), "");
+void AssDialogue::UpdateText(std::vector<std::unique_ptr<AssDialogueBlock>> &blocks)
+{
+    if (blocks.empty()) return;
+    Text = join(blocks | transformed(get_text), "");
 }
 
-bool AssDialogue::CollidesWith(const AssDialogue *target) const {
-	if (!target) return false;
-	return ((Start < target->Start) ? (target->Start < End) : (Start < target->End));
+bool AssDialogue::CollidesWith(const AssDialogue *target) const
+{
+    if (!target) return false;
+    return ((Start < target->Start) ? (target->Start < End) : (Start < target->End));
 }
 
 static std::string get_text_p(AssDialogueBlock *d) { return d->GetText(); }
-std::string AssDialogue::GetStrippedText() const {
-	auto blocks = ParseTags();
-	return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
+std::string AssDialogue::GetStrippedText() const
+{
+    auto blocks = ParseTags();
+    return join(blocks | agi::of_type<AssDialogueBlockPlain>() | transformed(get_text_p), "");
 }
diff --git a/src/ass_dialogue.h b/src/ass_dialogue.h
index 786d6759115e18e4a0eb8d7c360d7026d664083a..8945cf247cc41b8cb944739dbf200c035d8550f0 100644
--- a/src/ass_dialogue.h
+++ b/src/ass_dialogue.h
@@ -37,10 +37,10 @@
 #include <vector>
 
 enum class AssBlockType {
-	PLAIN,
-	COMMENT,
-	OVERRIDE,
-	DRAWING
+    PLAIN,
+    COMMENT,
+    OVERRIDE,
+    DRAWING
 };
 
 /// @class AssDialogueBlock
@@ -65,114 +65,114 @@ enum class AssBlockType {
 /// @endverbatim
 class AssDialogueBlock {
 protected:
-	/// Text of this block
-	std::string text;
+    /// Text of this block
+    std::string text;
 public:
-	AssDialogueBlock(std::string text) : text(std::move(text)) { }
-	virtual ~AssDialogueBlock() = default;
+    AssDialogueBlock(std::string text) : text(std::move(text)) { }
+    virtual ~AssDialogueBlock() = default;
 
-	virtual AssBlockType GetType() const = 0;
-	virtual std::string GetText() { return text; }
+    virtual AssBlockType GetType() const = 0;
+    virtual std::string GetText() { return text; }
 };
 
 class AssDialogueBlockPlain final : public AssDialogueBlock {
 public:
-	using AssDialogueBlock::text;
-	AssBlockType GetType() const override { return AssBlockType::PLAIN; }
-	AssDialogueBlockPlain(std::string const& text = std::string()) : AssDialogueBlock(text) { }
+    using AssDialogueBlock::text;
+    AssBlockType GetType() const override { return AssBlockType::PLAIN; }
+    AssDialogueBlockPlain(std::string const &text = std::string()) : AssDialogueBlock(text) { }
 };
 
 class AssDialogueBlockComment final : public AssDialogueBlock {
 public:
-	AssBlockType GetType() const override { return AssBlockType::COMMENT; }
-	AssDialogueBlockComment(std::string const& text = std::string()) : AssDialogueBlock("{" + text + "}") { }
+    AssBlockType GetType() const override { return AssBlockType::COMMENT; }
+    AssDialogueBlockComment(std::string const &text = std::string()) : AssDialogueBlock("{" + text + "}") { }
 };
 
 class AssDialogueBlockDrawing final : public AssDialogueBlock {
 public:
-	using AssDialogueBlock::text;
-	int Scale;
+    using AssDialogueBlock::text;
+    int Scale;
 
-	AssBlockType GetType() const override { return AssBlockType::DRAWING; }
-	AssDialogueBlockDrawing(std::string const& text, int scale) : AssDialogueBlock(text), Scale(scale) { }
+    AssBlockType GetType() const override { return AssBlockType::DRAWING; }
+    AssDialogueBlockDrawing(std::string const &text, int scale) : AssDialogueBlock(text), Scale(scale) { }
 };
 
 class AssDialogueBlockOverride final : public AssDialogueBlock {
 public:
-	AssDialogueBlockOverride(std::string const& text = std::string()) : AssDialogueBlock(text) { }
+    AssDialogueBlockOverride(std::string const &text = std::string()) : AssDialogueBlock(text) { }
 
-	std::vector<AssOverrideTag> Tags;
+    std::vector<AssOverrideTag> Tags;
 
-	AssBlockType GetType() const override { return AssBlockType::OVERRIDE; }
-	std::string GetText() override;
-	void ParseTags();
-	void AddTag(std::string const& tag);
+    AssBlockType GetType() const override { return AssBlockType::OVERRIDE; }
+    std::string GetText() override;
+    void ParseTags();
+    void AddTag(std::string const &tag);
 
-	/// Type of callback function passed to ProcessParameters
-	typedef void (*ProcessParametersCallback)(std::string const&, AssOverrideParameter *, void *);
-	/// @brief Process parameters via callback
-	/// @param callback The callback function to call per tag parameter
-	/// @param userData User data to pass to callback function
-	void ProcessParameters(ProcessParametersCallback callback, void *userData);
+    /// Type of callback function passed to ProcessParameters
+    typedef void (*ProcessParametersCallback)(std::string const &, AssOverrideParameter *, void *);
+    /// @brief Process parameters via callback
+    /// @param callback The callback function to call per tag parameter
+    /// @param userData User data to pass to callback function
+    void ProcessParameters(ProcessParametersCallback callback, void *userData);
 };
 
 struct AssDialogueBase {
-	/// 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
-	/// the different versions of the file.
-	int Id;
-
-	int Row = -1;
-
-	/// Is this a comment line?
-	bool Comment = false;
-	/// Layer number
-	int Layer = 0;
-	/// Margins: 0 = Left, 1 = Right, 2 = Top (Vertical)
-	std::array<int, 3> Margin = std::array<int, 3>{{ 0, 0, 0 }};
-	/// Starting time
-	agi::Time Start = 0;
-	/// Ending time
-	agi::Time End = 5000;
-	/// Style name
-	boost::flyweight<std::string> Style = boost::flyweight<std::string>("Default");
-	/// Actor name
-	boost::flyweight<std::string> Actor;
-	/// Effect name
-	boost::flyweight<std::string> Effect;
-	/// IDs of extradata entries for line
-	boost::flyweight<std::vector<uint32_t>> ExtradataIds;
-	/// Raw text data
-	boost::flyweight<std::string> Text;
+    /// 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
+    /// the different versions of the file.
+    int Id;
+
+    int Row = -1;
+
+    /// Is this a comment line?
+    bool Comment = false;
+    /// Layer number
+    int Layer = 0;
+    /// Margins: 0 = Left, 1 = Right, 2 = Top (Vertical)
+    std::array<int, 3> Margin = std::array<int, 3> {{ 0, 0, 0 }};
+    /// Starting time
+    agi::Time Start = 0;
+    /// Ending time
+    agi::Time End = 5000;
+    /// Style name
+    boost::flyweight<std::string> Style = boost::flyweight<std::string>("Default");
+    /// Actor name
+    boost::flyweight<std::string> Actor;
+    /// Effect name
+    boost::flyweight<std::string> Effect;
+    /// IDs of extradata entries for line
+    boost::flyweight<std::vector<uint32_t>> ExtradataIds;
+    /// Raw text data
+    boost::flyweight<std::string> Text;
 };
 
 class AssDialogue final : public AssEntry, public AssDialogueBase, public AssEntryListHook {
-	/// @brief Parse raw ASS data into everything else
-	/// @param data ASS line
-	void Parse(std::string const& data);
+    /// @brief Parse raw ASS data into everything else
+    /// @param data ASS line
+    void Parse(std::string const &data);
 public:
-	AssEntryGroup Group() const override { return AssEntryGroup::DIALOGUE; }
+    AssEntryGroup Group() const override { return AssEntryGroup::DIALOGUE; }
 
-	/// Parse text as ASS and return block information
-	std::vector<std::unique_ptr<AssDialogueBlock>> ParseTags() const;
+    /// Parse text as ASS and return block information
+    std::vector<std::unique_ptr<AssDialogueBlock>> ParseTags() const;
 
-	/// Strip all ASS tags from the text
-	void StripTags();
-	/// Strip a specific ASS tag from the text
-	/// Get text without tags
-	std::string GetStrippedText() const;
+    /// Strip all ASS tags from the text
+    void StripTags();
+    /// Strip a specific ASS tag from the text
+    /// Get text without tags
+    std::string GetStrippedText() const;
 
-	/// Update the text of the line from parsed blocks
-	void UpdateText(std::vector<std::unique_ptr<AssDialogueBlock>>& blocks);
-	std::string GetEntryData() const;
+    /// Update the text of the line from parsed blocks
+    void UpdateText(std::vector<std::unique_ptr<AssDialogueBlock>> &blocks);
+    std::string GetEntryData() const;
 
-	/// Does this line collide with the passed line?
-	bool CollidesWith(const AssDialogue *target) const;
+    /// Does this line collide with the passed line?
+    bool CollidesWith(const AssDialogue *target) const;
 
-	AssDialogue();
-	AssDialogue(AssDialogue const&);
-	AssDialogue(AssDialogueBase const&);
-	AssDialogue(std::string const& data);
-	~AssDialogue();
+    AssDialogue();
+    AssDialogue(AssDialogue const &);
+    AssDialogue(AssDialogueBase const &);
+    AssDialogue(std::string const &data);
+    ~AssDialogue();
 };
 
diff --git a/src/ass_entry.cpp b/src/ass_entry.cpp
index 9befe02fe7d98d0e5d3afde72f5ca8139259a06b..96950b140490903bc9eb37ac51b5606d010d75a8 100644
--- a/src/ass_entry.cpp
+++ b/src/ass_entry.cpp
@@ -21,15 +21,16 @@
 
 #include "ass_entry.h"
 
-std::string const& AssEntry::GroupHeader() const {
-	static std::string ass_headers[] = {
-		"[Script Info]",
-		"[V4+ Styles]",
-		"[Fonts]",
-		"[Graphics]",
-		"[Events]",
-		"[Aegisub Extradata]",
-		""
-	};
-	return ass_headers[(int)Group()];
+std::string const &AssEntry::GroupHeader() const
+{
+    static std::string ass_headers[] = {
+        "[Script Info]",
+        "[V4+ Styles]",
+        "[Fonts]",
+        "[Graphics]",
+        "[Events]",
+        "[Aegisub Extradata]",
+        ""
+    };
+    return ass_headers[(int)Group()];
 }
diff --git a/src/ass_entry.h b/src/ass_entry.h
index f1f6b3f4c0136f54def6b11d1879ee9ff947deaa..86411e52896c3b693bd87ad9d273772591862ab7 100644
--- a/src/ass_entry.h
+++ b/src/ass_entry.h
@@ -38,24 +38,24 @@
 #include <string>
 
 enum class AssEntryGroup {
-	INFO = 0,
-	STYLE,
-	FONT,
-	GRAPHIC,
-	DIALOGUE,
-	EXTRADATA,
-	GROUP_MAX
+    INFO = 0,
+    STYLE,
+    FONT,
+    GRAPHIC,
+    DIALOGUE,
+    EXTRADATA,
+    GROUP_MAX
 };
 
 using AssEntryListHook = boost::intrusive::make_list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>::type;
 
 class AssEntry {
 public:
-	virtual ~AssEntry() = default;
+    virtual ~AssEntry() = default;
 
-	/// Section of the file this entry belongs to
-	virtual AssEntryGroup Group() const=0;
+    /// Section of the file this entry belongs to
+    virtual AssEntryGroup Group() const = 0;
 
-	/// ASS or SSA Section header for this entry's group
-	std::string const& GroupHeader() const;
+    /// ASS or SSA Section header for this entry's group
+    std::string const &GroupHeader() const;
 };
diff --git a/src/ass_export_filter.cpp b/src/ass_export_filter.cpp
index 4f092d3517919433af54f81e22d2a3f08143ac9b..23d6936381557df786ecbf753a5ae428a0754596 100644
--- a/src/ass_export_filter.cpp
+++ b/src/ass_export_filter.cpp
@@ -36,45 +36,50 @@
 
 #include <libaegisub/format.h>
 
-static FilterList& filters() {
-	static FilterList instance;
-	return instance;
+static FilterList &filters()
+{
+    static FilterList instance;
+    return instance;
 }
 
 AssExportFilter::AssExportFilter(std::string name, std::string description, int priority)
-: name(std::move(name))
-, priority(priority)
-, description(std::move(description))
+    : name(std::move(name))
+    , priority(priority)
+    , description(std::move(description))
 {
 }
 
-void AssExportFilterChain::Register(std::unique_ptr<AssExportFilter> filter) {
-	int filter_copy = 1;
-	std::string name = filter->name;
-	// Find a unique name
-	while (GetFilter(name))
-		name = agi::format("%s (%d)", filter->name, filter_copy++);
+void AssExportFilterChain::Register(std::unique_ptr<AssExportFilter> filter)
+{
+    int filter_copy = 1;
+    std::string name = filter->name;
+    // Find a unique name
+    while (GetFilter(name))
+        name = agi::format("%s (%d)", filter->name, filter_copy++);
 
-	filter->name = name;
+    filter->name = name;
 
-	// Look for place to insert
-	auto begin(filters().begin()), end(filters().end());
-	while (begin != end && begin->priority >= filter->priority) ++begin;
-	filters().insert(begin, *filter.release());
+    // Look for place to insert
+    auto begin(filters().begin()), end(filters().end());
+    while (begin != end && begin->priority >= filter->priority) ++begin;
+    filters().insert(begin, *filter.release());
 }
 
-FilterList *AssExportFilterChain::GetFilterList() {
-	return &filters();
+FilterList *AssExportFilterChain::GetFilterList()
+{
+    return &filters();
 }
 
-void AssExportFilterChain::Clear() {
-	filters().clear_and_dispose([](AssExportFilter *f) { delete f; });
+void AssExportFilterChain::Clear()
+{
+    filters().clear_and_dispose([](AssExportFilter * f) { delete f; });
 }
 
-AssExportFilter *AssExportFilterChain::GetFilter(std::string const& name) {
-	for (auto& filter : filters()) {
-		if (filter.name == name)
-			return &filter;
-	}
-	return nullptr;
+AssExportFilter *AssExportFilterChain::GetFilter(std::string const &name)
+{
+    for (auto &filter : filters()) {
+        if (filter.name == name)
+            return &filter;
+    }
+    return nullptr;
 }
diff --git a/src/ass_export_filter.h b/src/ass_export_filter.h
index a8ae97c7f833cc313fc740579094d07636112f0f..00c12582c0176aa3984aea61d82808019cb8660e 100644
--- a/src/ass_export_filter.h
+++ b/src/ass_export_filter.h
@@ -45,54 +45,54 @@ class wxWindow;
 namespace agi { struct Context; }
 
 class AssExportFilter : public boost::intrusive::make_list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>::type {
-	/// The filter chain needs to be able to muck around with filter names when
-	/// they're registered to avoid duplicates
-	friend class AssExportFilterChain;
+    /// The filter chain needs to be able to muck around with filter names when
+    /// they're registered to avoid duplicates
+    friend class AssExportFilterChain;
 
-	/// This filter's name
-	std::string name;
+    /// This filter's name
+    std::string name;
 
-	/// Higher priority = run earlier
-	int priority;
+    /// Higher priority = run earlier
+    int priority;
 
-	/// User-visible description of this filter
-	std::string description;
+    /// User-visible description of this filter
+    std::string description;
 
 public:
-	AssExportFilter(std::string name, std::string description, int priority = 0);
-	virtual ~AssExportFilter() = default;
-
-	std::string const& GetName() const { return name; }
-	std::string const& GetDescription() const { return description; }
-
-	/// Process subtitles
-	/// @param subs Subtitles to process
-	/// @param parent_window Window to use as the parent if the filter wishes
-	///                      to open a progress dialog
-	virtual void ProcessSubs(AssFile *subs, wxWindow *parent_window=nullptr)=0;
-
-	/// Draw setup controls
-	/// @param parent Parent window to add controls to
-	/// @param c Project context
-	virtual wxWindow *GetConfigDialogWindow(wxWindow *parent, agi::Context *c) { return nullptr; }
-
-	/// Load settings to use from the configuration dialog
-	/// @param is_default If true use default settings instead
-	/// @param c Project context
-	virtual void LoadSettings(bool is_default, agi::Context *c) { }
+    AssExportFilter(std::string name, std::string description, int priority = 0);
+    virtual ~AssExportFilter() = default;
+
+    std::string const &GetName() const { return name; }
+    std::string const &GetDescription() const { return description; }
+
+    /// Process subtitles
+    /// @param subs Subtitles to process
+    /// @param parent_window Window to use as the parent if the filter wishes
+    ///                      to open a progress dialog
+    virtual void ProcessSubs(AssFile *subs, wxWindow *parent_window = nullptr) = 0;
+
+    /// Draw setup controls
+    /// @param parent Parent window to add controls to
+    /// @param c Project context
+    virtual wxWindow *GetConfigDialogWindow(wxWindow *parent, agi::Context *c) { return nullptr; }
+
+    /// Load settings to use from the configuration dialog
+    /// @param is_default If true use default settings instead
+    /// @param c Project context
+    virtual void LoadSettings(bool is_default, agi::Context *c) { }
 };
 
 typedef boost::intrusive::make_list<AssExportFilter, boost::intrusive::constant_time_size<false>>::type FilterList;
 
 class AssExportFilterChain {
 public:
-	/// Register an export filter
-	static void Register(std::unique_ptr<AssExportFilter> filter);
-	/// Unregister and delete all export filters
-	static void Clear();
-	/// Get a filter by name or nullptr if it doesn't exist
-	static AssExportFilter *GetFilter(std::string const& name);
-
-	/// Get the list of registered filters
-	static FilterList *GetFilterList();
+    /// Register an export filter
+    static void Register(std::unique_ptr<AssExportFilter> filter);
+    /// Unregister and delete all export filters
+    static void Clear();
+    /// Get a filter by name or nullptr if it doesn't exist
+    static AssExportFilter *GetFilter(std::string const &name);
+
+    /// Get the list of registered filters
+    static FilterList *GetFilterList();
 };
diff --git a/src/ass_exporter.cpp b/src/ass_exporter.cpp
index 123bcd076b2ec7a17457734932ff2af841769232..f0f6b99dfd0856e1ccb33235c93baaa1a63b6ac0 100644
--- a/src/ass_exporter.cpp
+++ b/src/ass_exporter.cpp
@@ -46,62 +46,68 @@
 
 AssExporter::AssExporter(agi::Context *c) : c(c) { }
 
-void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer) {
-	is_default = false;
-	for (auto& filter : *AssExportFilterChain::GetFilterList()) {
-		// Make sure to construct static box sizer first, so it won't overlap
-		// the controls on wxMac.
-		auto box = new wxStaticBoxSizer(wxVERTICAL, parent, to_wx(filter.GetName()));
-		wxWindow *window = filter.GetConfigDialogWindow(parent, c);
-		if (window) {
-			box->Add(window, 0, wxEXPAND, 0);
-			target_sizer->Add(box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
-			target_sizer->Show(box, false);
-			Sizers[filter.GetName()] = box;
-		}
-		else {
-			delete box;
-		}
-	}
+void AssExporter::DrawSettings(wxWindow *parent, wxSizer *target_sizer)
+{
+    is_default = false;
+    for (auto &filter : *AssExportFilterChain::GetFilterList()) {
+        // Make sure to construct static box sizer first, so it won't overlap
+        // the controls on wxMac.
+        auto box = new wxStaticBoxSizer(wxVERTICAL, parent, to_wx(filter.GetName()));
+        wxWindow *window = filter.GetConfigDialogWindow(parent, c);
+        if (window) {
+            box->Add(window, 0, wxEXPAND, 0);
+            target_sizer->Add(box, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
+            target_sizer->Show(box, false);
+            Sizers[filter.GetName()] = box;
+        }
+        else {
+            delete box;
+        }
+    }
 }
 
-void AssExporter::AddFilter(std::string const& name) {
-	auto filter = AssExportFilterChain::GetFilter(name);
-	if (!filter) throw agi::InternalError("Filter not found: " + name);
+void AssExporter::AddFilter(std::string const &name)
+{
+    auto filter = AssExportFilterChain::GetFilter(name);
+    if (!filter) throw agi::InternalError("Filter not found: " + name);
 
-	filters.push_back(filter);
+    filters.push_back(filter);
 }
 
-std::vector<std::string> AssExporter::GetAllFilterNames() const {
-	std::vector<std::string> names;
-	for (auto& filter : *AssExportFilterChain::GetFilterList())
-		names.emplace_back(filter.GetName());
-	return names;
+std::vector<std::string> AssExporter::GetAllFilterNames() const
+{
+    std::vector<std::string> names;
+    for (auto &filter : *AssExportFilterChain::GetFilterList())
+        names.emplace_back(filter.GetName());
+    return names;
 }
 
-void AssExporter::Export(agi::fs::path const& filename, std::string const& charset, wxWindow *export_dialog) {
-	AssFile subs(*c->ass);
+void AssExporter::Export(agi::fs::path const &filename, std::string const &charset, wxWindow *export_dialog)
+{
+    AssFile subs(*c->ass);
 
-	for (auto filter : filters) {
-		filter->LoadSettings(is_default, c);
-		filter->ProcessSubs(&subs, export_dialog);
-	}
+    for (auto filter : filters) {
+        filter->LoadSettings(is_default, c);
+        filter->ProcessSubs(&subs, export_dialog);
+    }
 
-	const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
-	if (!writer)
-		throw agi::InvalidInputException("Unknown file type.");
+    const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
+    if (!writer)
+        throw agi::InvalidInputException("Unknown file type.");
 
-	writer->ExportFile(&subs, filename, c->project->Timecodes(), charset);
+    writer->ExportFile(&subs, filename, c->project->Timecodes(), charset);
 }
 
-wxSizer *AssExporter::GetSettingsSizer(std::string const& name) {
-	auto pos = Sizers.find(name);
-	return pos == Sizers.end() ? nullptr : pos->second;
+wxSizer *AssExporter::GetSettingsSizer(std::string const &name)
+{
+    auto pos = Sizers.find(name);
+    return pos == Sizers.end() ? nullptr : pos->second;
 }
 
-std::string const& AssExporter::GetDescription(std::string const& name) const {
-	auto filter = AssExportFilterChain::GetFilter(name);
-	if (filter)
-		return filter->GetDescription();
-	throw agi::InternalError("Filter not found: " + name);
+std::string const &AssExporter::GetDescription(std::string const &name) const
+{
+    auto filter = AssExportFilterChain::GetFilter(name);
+    if (filter)
+        return filter->GetDescription();
+    throw agi::InternalError("Filter not found: " + name);
 }
diff --git a/src/ass_exporter.h b/src/ass_exporter.h
index 1565c01f261dc8b33790dd4f7d2da04f78a4913d..d046203004a4a2fd9f2c887ef692d10838385a8a 100644
--- a/src/ass_exporter.h
+++ b/src/ass_exporter.h
@@ -44,46 +44,46 @@ class wxSizer;
 class wxWindow;
 
 class AssExporter {
-	typedef std::vector<AssExportFilter*>::const_iterator filter_iterator;
+    typedef std::vector<AssExportFilter *>::const_iterator filter_iterator;
 
-	/// Sizers for configuration panels
-	std::map<std::string, wxSizer*> Sizers;
+    /// Sizers for configuration panels
+    std::map<std::string, wxSizer *> Sizers;
 
-	/// Filters which will be applied to the subtitles
-	std::vector<AssExportFilter*> filters;
+    /// Filters which will be applied to the subtitles
+    std::vector<AssExportFilter *> filters;
 
-	/// Input context
-	agi::Context *c;
+    /// Input context
+    agi::Context *c;
 
-	/// Have the config windows been created, or should filters simply use
-	/// their default settings
-	bool is_default = true;
+    /// Have the config windows been created, or should filters simply use
+    /// their default settings
+    bool is_default = true;
 
 public:
-	AssExporter(agi::Context *c);
+    AssExporter(agi::Context *c);
 
-	/// Get the names of all registered export filters
-	std::vector<std::string> GetAllFilterNames() const;
+    /// Get the names of all registered export filters
+    std::vector<std::string> GetAllFilterNames() const;
 
-	/// Add the named filter to the list of filters to be run
-	/// @throws std::string if filter is not found
-	void AddFilter(std::string const& name);
+    /// Add the named filter to the list of filters to be run
+    /// @throws std::string if filter is not found
+    void AddFilter(std::string const &name);
 
-	/// Apply selected export filters and save with the given charset
-	/// @param file Target filename
-	/// @param charset Target charset
-	/// @param parent_window Parent window the filters should use when opening dialogs
-	void Export(agi::fs::path const& file, std::string const& charset, wxWindow *parent_window= nullptr);
+    /// Apply selected export filters and save with the given charset
+    /// @param file Target filename
+    /// @param charset Target charset
+    /// @param parent_window Parent window the filters should use when opening dialogs
+    void Export(agi::fs::path const &file, std::string const &charset, wxWindow *parent_window = nullptr);
 
-	/// Add configuration panels for all registered filters to the target sizer
-	/// @param parent Parent window for controls
-	/// @param target_sizer Sizer to add configuration panels to
-	void DrawSettings(wxWindow *parent, wxSizer *target_sizer);
+    /// Add configuration panels for all registered filters to the target sizer
+    /// @param parent Parent window for controls
+    /// @param target_sizer Sizer to add configuration panels to
+    void DrawSettings(wxWindow *parent, wxSizer *target_sizer);
 
-	/// Get the sizer created by DrawSettings for a specific filter
-	wxSizer *GetSettingsSizer(std::string const& name);
+    /// Get the sizer created by DrawSettings for a specific filter
+    wxSizer *GetSettingsSizer(std::string const &name);
 
-	/// Get the description of the named export filter
-	/// @throws std::string if filter is not found
-	std::string const& GetDescription(std::string const& name) const;
+    /// Get the description of the named export filter
+    /// @throws std::string if filter is not found
+    std::string const &GetDescription(std::string const &name) const;
 };
diff --git a/src/ass_file.cpp b/src/ass_file.cpp
index bd06c562e89469ab1b9875009fdc9c90a0de8159..0b8b5ddfef3779f1ec24b3828a91b3524fd4b337 100644
--- a/src/ass_file.cpp
+++ b/src/ass_file.cpp
@@ -33,276 +33,301 @@
 
 AssFile::AssFile() { }
 
-AssFile::~AssFile() {
-	Styles.clear_and_dispose([](AssStyle *e) { delete e; });
-	Events.clear_and_dispose([](AssDialogue *e) { delete e; });
+AssFile::~AssFile()
+{
+    Styles.clear_and_dispose([](AssStyle * e) { delete e; });
+    Events.clear_and_dispose([](AssDialogue * e) { delete e; });
 }
 
-void AssFile::LoadDefault(bool include_dialogue_line, std::string const& style_catalog) {
-	Info.emplace_back("Title", "Default Aegisub file");
-	Info.emplace_back("ScriptType", "v4.00+");
-	Info.emplace_back("WrapStyle", "0");
-	Info.emplace_back("ScaledBorderAndShadow", "yes");
-	if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) {
-		Info.emplace_back("PlayResX", std::to_string(OPT_GET("Subtitle/Default Resolution/Width")->GetInt()));
-		Info.emplace_back("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt()));
-	}
-	Info.emplace_back("YCbCr Matrix", "None");
-
-	// Add default style
-	Styles.push_back(*new AssStyle);
-
-	// Add/replace any catalog styles requested
-	if (AssStyleStorage::CatalogExists(style_catalog)) {
-		AssStyleStorage catalog;
-		catalog.LoadCatalog(style_catalog);
-		catalog.ReplaceIntoFile(*this);
-	}
-
-	if (include_dialogue_line)
-		Events.push_back(*new AssDialogue);
+void AssFile::LoadDefault(bool include_dialogue_line, std::string const &style_catalog)
+{
+    Info.emplace_back("Title", "Default Aegisub file");
+    Info.emplace_back("ScriptType", "v4.00+");
+    Info.emplace_back("WrapStyle", "0");
+    Info.emplace_back("ScaledBorderAndShadow", "yes");
+    if (!OPT_GET("Subtitle/Default Resolution/Auto")->GetBool()) {
+        Info.emplace_back("PlayResX", std::to_string(OPT_GET("Subtitle/Default Resolution/Width")->GetInt()));
+        Info.emplace_back("PlayResY", std::to_string(OPT_GET("Subtitle/Default Resolution/Height")->GetInt()));
+    }
+    Info.emplace_back("YCbCr Matrix", "None");
+
+    // Add default style
+    Styles.push_back(*new AssStyle);
+
+    // Add/replace any catalog styles requested
+    if (AssStyleStorage::CatalogExists(style_catalog)) {
+        AssStyleStorage catalog;
+        catalog.LoadCatalog(style_catalog);
+        catalog.ReplaceIntoFile(*this);
+    }
+
+    if (include_dialogue_line)
+        Events.push_back(*new AssDialogue);
 }
 
 AssFile::AssFile(const AssFile &from)
-: Info(from.Info)
-, Attachments(from.Attachments)
-, Extradata(from.Extradata)
-, next_extradata_id(from.next_extradata_id)
+    : Info(from.Info)
+    , Attachments(from.Attachments)
+    , Extradata(from.Extradata)
+    , next_extradata_id(from.next_extradata_id)
 {
-	Styles.clone_from(from.Styles,
-		[](AssStyle const& e) { return new AssStyle(e); },
-		[](AssStyle *e) { delete e; });
-	Events.clone_from(from.Events,
-		[](AssDialogue const& e) { return new AssDialogue(e); },
-		[](AssDialogue *e) { delete e; });
+    Styles.clone_from(from.Styles,
+    [](AssStyle const & e) { return new AssStyle(e); },
+    [](AssStyle * e) { delete e; });
+    Events.clone_from(from.Events,
+    [](AssDialogue const & e) { return new AssDialogue(e); },
+    [](AssDialogue * e) { delete e; });
 }
 
-void AssFile::swap(AssFile& from) throw() {
-	Info.swap(from.Info);
-	Styles.swap(from.Styles);
-	Events.swap(from.Events);
-	Attachments.swap(from.Attachments);
-	Extradata.swap(from.Extradata);
-	std::swap(Properties, from.Properties);
-	std::swap(next_extradata_id, from.next_extradata_id);
+void AssFile::swap(AssFile &from) throw()
+{
+    Info.swap(from.Info);
+    Styles.swap(from.Styles);
+    Events.swap(from.Events);
+    Attachments.swap(from.Attachments);
+    Extradata.swap(from.Extradata);
+    std::swap(Properties, from.Properties);
+    std::swap(next_extradata_id, from.next_extradata_id);
 }
 
-AssFile& AssFile::operator=(AssFile from) {
-	swap(from);
-	return *this;
+AssFile &AssFile::operator=(AssFile from)
+{
+    swap(from);
+    return *this;
 }
 
-EntryList<AssDialogue>::iterator AssFile::iterator_to(AssDialogue& line) {
-	using l = EntryList<AssDialogue>;
-	bool in_list = !l::node_algorithms::inited(l::value_traits::to_node_ptr(line));
-	return in_list ? Events.iterator_to(line) : Events.end();
+EntryList<AssDialogue>::iterator AssFile::iterator_to(AssDialogue &line)
+{
+    using l = EntryList<AssDialogue>;
+    bool in_list = !l::node_algorithms::inited(l::value_traits::to_node_ptr(line));
+    return in_list ? Events.iterator_to(line) : Events.end();
 }
 
-void AssFile::InsertAttachment(agi::fs::path const& filename) {
-	AssEntryGroup group = AssEntryGroup::GRAPHIC;
+void AssFile::InsertAttachment(agi::fs::path const &filename)
+{
+    AssEntryGroup group = AssEntryGroup::GRAPHIC;
 
-	auto ext = boost::to_lower_copy(filename.extension().string());
-	if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb")
-		group = AssEntryGroup::FONT;
+    auto ext = boost::to_lower_copy(filename.extension().string());
+    if (ext == ".ttf" || ext == ".ttc" || ext == ".pfb")
+        group = AssEntryGroup::FONT;
 
-	Attachments.emplace_back(filename, group);
+    Attachments.emplace_back(filename, group);
 }
 
-std::string AssFile::GetScriptInfo(std::string const& key) const {
-	for (auto const& info : Info) {
-		if (boost::iequals(key, info.Key()))
-			return info.Value();
-	}
+std::string AssFile::GetScriptInfo(std::string const &key) const
+{
+    for (auto const &info : Info) {
+        if (boost::iequals(key, info.Key()))
+            return info.Value();
+    }
 
-	return "";
+    return "";
 }
 
-int AssFile::GetScriptInfoAsInt(std::string const& key) const {
-	return atoi(GetScriptInfo(key).c_str());
+int AssFile::GetScriptInfoAsInt(std::string const &key) const
+{
+    return atoi(GetScriptInfo(key).c_str());
 }
 
-void AssFile::SetScriptInfo(std::string const& key, std::string const& value) {
-	for (auto it = Info.begin(); it != Info.end(); ++it) {
-		if (boost::iequals(key, it->Key())) {
-			if (value.empty())
-				Info.erase(it);
-			else
-				it->SetValue(value);
-			return;
-		}
-	}
-
-	if (!value.empty())
-		Info.emplace_back(key, value);
+void AssFile::SetScriptInfo(std::string const &key, std::string const &value)
+{
+    for (auto it = Info.begin(); it != Info.end(); ++it) {
+        if (boost::iequals(key, it->Key())) {
+            if (value.empty())
+                Info.erase(it);
+            else
+                it->SetValue(value);
+            return;
+        }
+    }
+
+    if (!value.empty())
+        Info.emplace_back(key, value);
 }
 
-void AssFile::GetResolution(int &sw, int &sh) const {
-	sw = GetScriptInfoAsInt("PlayResX");
-	sh = GetScriptInfoAsInt("PlayResY");
-
-	// Gabest logic: default is 384x288, assume 1280x1024 if either height or
-	// width are that, otherwise assume 4:3 if only heigh or width are set.
-	// Why 1280x1024? Who the fuck knows. Clearly just Gabest trolling everyone.
-	if (sw == 0 && sh == 0) {
-		sw = 384;
-		sh = 288;
-	}
-	else if (sw == 0)
-		sw = sh == 1024 ? 1280 : sh * 4 / 3;
-	else if (sh == 0)
-		sh = sw == 1280 ? 1024 : sw * 3 / 4;
+void AssFile::GetResolution(int &sw, int &sh) const
+{
+    sw = GetScriptInfoAsInt("PlayResX");
+    sh = GetScriptInfoAsInt("PlayResY");
+
+    // Gabest logic: default is 384x288, assume 1280x1024 if either height or
+    // width are that, otherwise assume 4:3 if only heigh or width are set.
+    // Why 1280x1024? Who the fuck knows. Clearly just Gabest trolling everyone.
+    if (sw == 0 && sh == 0) {
+        sw = 384;
+        sh = 288;
+    }
+    else if (sw == 0)
+        sw = sh == 1024 ? 1280 : sh * 4 / 3;
+    else if (sh == 0)
+        sh = sw == 1280 ? 1024 : sw * 3 / 4;
 }
 
-std::vector<std::string> AssFile::GetStyles() const {
-	std::vector<std::string> styles;
-	for (auto& style : Styles)
-		styles.push_back(style.name);
-	return styles;
+std::vector<std::string> AssFile::GetStyles() const
+{
+    std::vector<std::string> styles;
+    for (auto &style : Styles)
+        styles.push_back(style.name);
+    return styles;
 }
 
-AssStyle *AssFile::GetStyle(std::string const& name) {
-	for (auto& style : Styles) {
-		if (boost::iequals(style.name, name))
-			return &style;
-	}
-	return nullptr;
+AssStyle *AssFile::GetStyle(std::string const &name)
+{
+    for (auto &style : Styles) {
+        if (boost::iequals(style.name, name))
+            return &style;
+    }
+    return nullptr;
 }
 
-int AssFile::Commit(wxString const& desc, int type, int amend_id, AssDialogue *single_line) {
-	if (type == COMMIT_NEW || (type & COMMIT_DIAG_ADDREM) || (type & COMMIT_ORDER)) {
-		int i = 0;
-		for (auto& event : Events)
-			event.Row = i++;
-	}
+int AssFile::Commit(wxString const &desc, int type, int amend_id, AssDialogue *single_line)
+{
+    if (type == COMMIT_NEW || (type & COMMIT_DIAG_ADDREM) || (type & COMMIT_ORDER)) {
+        int i = 0;
+        for (auto &event : Events)
+            event.Row = i++;
+    }
 
-	PushState({desc, &amend_id, single_line});
+    PushState({desc, &amend_id, single_line});
 
-	AnnounceCommit(type, single_line);
+    AnnounceCommit(type, single_line);
 
-	return amend_id;
+    return amend_id;
 }
 
-bool AssFile::CompStart(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.Start < rgt.Start;
+bool AssFile::CompStart(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.Start < rgt.Start;
 }
-bool AssFile::CompEnd(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.End < rgt.End;
+bool AssFile::CompEnd(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.End < rgt.End;
 }
-bool AssFile::CompStyle(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.Style < rgt.Style;
+bool AssFile::CompStyle(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.Style < rgt.Style;
 }
-bool AssFile::CompActor(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.Actor < rgt.Actor;
+bool AssFile::CompActor(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.Actor < rgt.Actor;
 }
-bool AssFile::CompEffect(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.Effect < rgt.Effect;
+bool AssFile::CompEffect(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.Effect < rgt.Effect;
 }
-bool AssFile::CompLayer(AssDialogue const& lft, AssDialogue const& rgt) {
-	return lft.Layer < rgt.Layer;
+bool AssFile::CompLayer(AssDialogue const &lft, AssDialogue const &rgt)
+{
+    return lft.Layer < rgt.Layer;
 }
 
-void AssFile::Sort(CompFunc comp, std::set<AssDialogue*> const& limit) {
-	Sort(Events, comp, limit);
+void AssFile::Sort(CompFunc comp, std::set<AssDialogue *> const &limit)
+{
+    Sort(Events, comp, limit);
 }
 
-void AssFile::Sort(EntryList<AssDialogue> &lst, CompFunc comp, std::set<AssDialogue*> const& limit) {
-	if (limit.empty()) {
-		lst.sort(comp);
-		return;
-	}
-
-	// Sort each selected block separately, leaving everything else untouched
-	for (auto begin = lst.begin(); begin != lst.end(); ++begin) {
-		if (!limit.count(&*begin)) continue;
-		auto end = begin;
-		while (end != lst.end() && limit.count(&*end)) ++end;
-
-		// sort doesn't support only sorting a sublist, so move them to a temp list
-		EntryList<AssDialogue> tmp;
-		tmp.splice(tmp.begin(), lst, begin, end);
-		tmp.sort(comp);
-		lst.splice(end, tmp);
-
-		begin = --end;
-	}
+void AssFile::Sort(EntryList<AssDialogue> &lst, CompFunc comp, std::set<AssDialogue *> const &limit)
+{
+    if (limit.empty()) {
+        lst.sort(comp);
+        return;
+    }
+
+    // Sort each selected block separately, leaving everything else untouched
+    for (auto begin = lst.begin(); begin != lst.end(); ++begin) {
+        if (!limit.count(&*begin)) continue;
+        auto end = begin;
+        while (end != lst.end() && limit.count(&*end)) ++end;
+
+        // sort doesn't support only sorting a sublist, so move them to a temp list
+        EntryList<AssDialogue> tmp;
+        tmp.splice(tmp.begin(), lst, begin, end);
+        tmp.sort(comp);
+        lst.splice(end, tmp);
+
+        begin = --end;
+    }
 }
 
-uint32_t AssFile::AddExtradata(std::string const& key, std::string const& value) {
-	for (auto const& data : Extradata) {
-		// perform brute-force deduplication by simple key and value comparison
-		if (key == data.key && value == data.value) {
-			return data.id;
-		}
-	}
-	Extradata.push_back(ExtradataEntry{next_extradata_id, key, value});
-	return next_extradata_id++; // return old value, then post-increment
+uint32_t AssFile::AddExtradata(std::string const &key, std::string const &value)
+{
+    for (auto const &data : Extradata) {
+        // perform brute-force deduplication by simple key and value comparison
+        if (key == data.key && value == data.value) {
+            return data.id;
+        }
+    }
+    Extradata.push_back(ExtradataEntry{next_extradata_id, key, value});
+    return next_extradata_id++; // return old value, then post-increment
 }
 
 namespace {
 struct extradata_id_cmp {
-	bool operator()(ExtradataEntry const& e, uint32_t id) {
-		return e.id < id;
-	}
-	bool operator()(uint32_t id, ExtradataEntry const& e) {
-		return id < e.id;
-	}
+    bool operator()(ExtradataEntry const &e, uint32_t id) {
+        return e.id < id;
+    }
+    bool operator()(uint32_t id, ExtradataEntry const &e) {
+        return id < e.id;
+    }
 };
 
 template<typename ExtradataType, typename Func>
-void enumerate_extradata(ExtradataType&& extradata, std::vector<uint32_t> const& id_list, Func&& f) {
-	auto begin = extradata.begin(), end = extradata.end();
-	for (auto id : id_list) {
-		auto it = lower_bound(begin, end, id, extradata_id_cmp{});
-		if (it != end) {
-			f(*it);
-			begin = it;
-		}
-	}
+void enumerate_extradata(ExtradataType &&extradata, std::vector<uint32_t> const &id_list, Func &&f)
+{
+    auto begin = extradata.begin(), end = extradata.end();
+    for (auto id : id_list) {
+        auto it = lower_bound(begin, end, id, extradata_id_cmp{});
+        if (it != end) {
+            f(*it);
+            begin = it;
+        }
+    }
 }
 
 template<typename K, typename V>
 using reference_map = std::unordered_map<std::reference_wrapper<const K>, V, std::hash<K>, std::equal_to<K>>;
 }
 
-std::vector<ExtradataEntry> AssFile::GetExtradata(std::vector<uint32_t> const& id_list) const {
-	std::vector<ExtradataEntry> result;
-	enumerate_extradata(Extradata, id_list, [&](ExtradataEntry const& e) {
-		result.push_back(e);
-	});
-	return result;
+std::vector<ExtradataEntry> AssFile::GetExtradata(std::vector<uint32_t> const &id_list) const
+{
+    std::vector<ExtradataEntry> result;
+    enumerate_extradata(Extradata, id_list, [&](ExtradataEntry const & e) {
+        result.push_back(e);
+    });
+    return result;
 }
 
-void AssFile::CleanExtradata() {
-	if (Extradata.empty()) return;
-
-	std::unordered_set<uint32_t> ids_used;
-	for (auto& line : Events) {
-		if (line.ExtradataIds.get().empty()) continue;
-
-		// Find the ID for each unique key in the line
-		reference_map<std::string, uint32_t> keys_used;
-		enumerate_extradata(Extradata, line.ExtradataIds.get(), [&](ExtradataEntry const& e) {
-			keys_used[e.key] = e.id;
-		});
-
-		for (auto const& used : keys_used)
-			ids_used.insert(used.second);
-
-		// If any keys were duplicated or missing, update the id list
-		if (keys_used.size() != line.ExtradataIds.get().size()) {
-			std::vector<uint32_t> ids;
-			ids.reserve(keys_used.size());
-			for (auto const& used : keys_used)
-				ids.push_back(used.second);
-			std::sort(begin(ids), end(ids));
-			line.ExtradataIds = std::move(ids);
-		}
-	}
-
-	if (ids_used.size() != Extradata.size()) {
-		// Erase all no-longer-used extradata entries
-		Extradata.erase(std::remove_if(begin(Extradata), end(Extradata), [&](ExtradataEntry const& e) {
-			return !ids_used.count(e.id);
-		}), end(Extradata));
-	}
+void AssFile::CleanExtradata()
+{
+    if (Extradata.empty()) return;
+
+    std::unordered_set<uint32_t> ids_used;
+    for (auto &line : Events) {
+        if (line.ExtradataIds.get().empty()) continue;
+
+        // Find the ID for each unique key in the line
+        reference_map<std::string, uint32_t> keys_used;
+        enumerate_extradata(Extradata, line.ExtradataIds.get(), [&](ExtradataEntry const & e) {
+            keys_used[e.key] = e.id;
+        });
+
+        for (auto const &used : keys_used)
+            ids_used.insert(used.second);
+
+        // If any keys were duplicated or missing, update the id list
+        if (keys_used.size() != line.ExtradataIds.get().size()) {
+            std::vector<uint32_t> ids;
+            ids.reserve(keys_used.size());
+            for (auto const &used : keys_used)
+                ids.push_back(used.second);
+            std::sort(begin(ids), end(ids));
+            line.ExtradataIds = std::move(ids);
+        }
+    }
+
+    if (ids_used.size() != Extradata.size()) {
+        // Erase all no-longer-used extradata entries
+        Extradata.erase(std::remove_if(begin(Extradata), end(Extradata), [&](ExtradataEntry const & e) {
+            return !ids_used.count(e.id);
+        }), end(Extradata));
+    }
 }
diff --git a/src/ass_file.h b/src/ass_file.h
index db848cd1effaaba102104cffc05ba3fadbb3f911..d597d43343e9a9bf8fb4fb2f056ce07b43a346d5 100644
--- a/src/ass_file.h
+++ b/src/ass_file.h
@@ -47,160 +47,160 @@ template<typename T>
 using EntryList = typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>, boost::intrusive::base_hook<AssEntryListHook>>::type;
 
 struct ExtradataEntry {
-	uint32_t id;
-	std::string key;
-	std::string value;
+    uint32_t id;
+    std::string key;
+    std::string value;
 };
 
 struct AssFileCommit {
-	wxString const& message;
-	int *commit_id;
-	AssDialogue *single_line;
+    wxString const &message;
+    int *commit_id;
+    AssDialogue *single_line;
 };
 
 struct ProjectProperties {
-	std::string automation_scripts;
-	std::string export_filters;
-	std::string export_encoding;
-	std::string style_storage;
-	std::string audio_file;
-	std::string video_file;
-	std::string timecodes_file;
-	std::string keyframes_file;
-	std::map<std::string, std::string> automation_settings;
-
-	// UI State
-	double video_zoom = 0.;
-	double ar_value = 0.;
-	int scroll_position = 0;
-	int active_row = 0;
-	int ar_mode = 0;
-	int video_position = 0;
+    std::string automation_scripts;
+    std::string export_filters;
+    std::string export_encoding;
+    std::string style_storage;
+    std::string audio_file;
+    std::string video_file;
+    std::string timecodes_file;
+    std::string keyframes_file;
+    std::map<std::string, std::string> automation_settings;
+
+    // UI State
+    double video_zoom = 0.;
+    double ar_value = 0.;
+    int scroll_position = 0;
+    int active_row = 0;
+    int ar_mode = 0;
+    int video_position = 0;
 };
 
 class AssFile {
-	/// A set of changes has been committed to the file (AssFile::COMMITType)
-	agi::signal::Signal<int, const AssDialogue*> AnnounceCommit;
-	agi::signal::Signal<AssFileCommit> PushState;
+    /// A set of changes has been committed to the file (AssFile::COMMITType)
+    agi::signal::Signal<int, const AssDialogue *> AnnounceCommit;
+    agi::signal::Signal<AssFileCommit> PushState;
 public:
-	/// The lines in the file
-	std::vector<AssInfo> Info;
-	EntryList<AssStyle> Styles;
-	EntryList<AssDialogue> Events;
-	std::vector<AssAttachment> Attachments;
-	std::vector<ExtradataEntry> Extradata;
-	ProjectProperties Properties;
-
-	uint32_t next_extradata_id = 0;
-
-	AssFile();
-	AssFile(const AssFile &from);
-	AssFile& operator=(AssFile from);
-	~AssFile();
-
-	EntryList<AssDialogue>::iterator iterator_to(AssDialogue& line);
-
-	/// @brief Load default file
-	/// @param defline Add a blank line to the file
-	/// @param style_catalog Style catalog name to fill styles from, blank to use default style
-	void LoadDefault(bool defline = true, std::string const& style_catalog = std::string());
-	/// Attach a file to the ass file
-	void InsertAttachment(agi::fs::path const& filename);
-	/// Get the names of all of the styles available
-	std::vector<std::string> GetStyles() const;
-	/// @brief Get a style by name
-	/// @param name Style name
-	/// @return Pointer to style or nullptr
-	AssStyle *GetStyle(std::string const& name);
-
-	void swap(AssFile &) throw();
-
-	/// @brief Get the script resolution
-	/// @param[out] w Width
-	/// @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(std::string const& key) const;
-	/// Get the value in a [Script Info] key as string.
-	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(std::string const& key, std::string const& value);
-
-	/// @brief Add a new extradata entry
-	/// @param key Class identifier/owner for the extradata
-	/// @param value Data for the extradata
-	/// @return ID of the created entry
-	uint32_t AddExtradata(std::string const& key, std::string const& value);
-	/// Fetch all extradata entries from a list of IDs
-	std::vector<ExtradataEntry> GetExtradata(std::vector<uint32_t> const& id_list) const;
-	/// Remove unreferenced extradata entries
-	void CleanExtradata();
-
-	/// Type of changes made in a commit
-	enum CommitType {
-		/// Potentially the entire file has been changed; any saved information
-		/// should be discarded. Note that the active line and selected set
-		/// should not be touched in handlers for this, as they may not have
-		/// been updated yet
-		/// Note that it is intentional that this cannot be combined with
-		/// other commit types
-		COMMIT_NEW         = 0,
-		/// The order of lines in the file has changed
-		COMMIT_ORDER       = 0x1,
-		/// The script info section has changed in some way
-		COMMIT_SCRIPTINFO  = 0x2,
-		/// The styles have changed in some way
-		COMMIT_STYLES      = 0x4,
-		/// The attachments have changed in some way
-		COMMIT_ATTACHMENT  = 0x8,
-		/// Dialogue lines have been added or removed
-		/// Note that if the active dialogue line was removed, the active line
-		/// should be updated BEFORE committing
-		COMMIT_DIAG_ADDREM = 0x10,
-		/// The metadata fields of existing dialogue lines have changed
-		COMMIT_DIAG_META   = 0x20,
-		/// The start and/or end times of existing dialogue lines have changed
-		COMMIT_DIAG_TIME   = 0x40,
-		/// The text of existing dialogue lines have changed
-		COMMIT_DIAG_TEXT   = 0x80,
-		COMMIT_DIAG_FULL   = COMMIT_DIAG_META | COMMIT_DIAG_TIME | COMMIT_DIAG_TEXT,
-		/// Extradata entries were added/modified/removed
-		COMMIT_EXTRADATA   = 0x100,
-	};
-
-	DEFINE_SIGNAL_ADDERS(AnnounceCommit, AddCommitListener)
-	DEFINE_SIGNAL_ADDERS(PushState, AddUndoManager)
-
-	/// @brief Flag the file as modified and push a copy onto the undo stack
-	/// @param desc        Undo description
-	/// @param type        Type of changes made to the file in this commit
-	/// @param commitId    Commit to amend rather than pushing a new commit
-	/// @param single_line Line which was changed, if only one line was
-	/// @return Unique identifier for the new undo group
-	int Commit(wxString const& desc, int type, int commitId = -1, AssDialogue *single_line = nullptr);
-
-	/// Comparison function for use when sorting
-	typedef bool (*CompFunc)(AssDialogue const& lft, AssDialogue const& rgt);
-
-	/// Compare based on start time
-	static bool CompStart(AssDialogue const& lft, AssDialogue const& rgt);
-	/// Compare based on end time
-	static bool CompEnd(AssDialogue const& lft, AssDialogue const& rgt);
-	/// Compare based on style name
-	static bool CompStyle(AssDialogue const& lft, AssDialogue const& rgt);
-	/// Compare based on actor name
-	static bool CompActor(AssDialogue const& lft, AssDialogue const& rgt);
-	/// Compare based on effect
-	static bool CompEffect(AssDialogue const& lft, AssDialogue const& rgt);
-	/// Compare based on layer
-	static bool CompLayer(AssDialogue const& lft, AssDialogue const& rgt);
-
-	/// @brief Sort the dialogue lines in this file
-	/// @param comp Comparison function to use. Defaults to sorting by start time.
-	/// @param limit If non-empty, only lines in this set are sorted
-	void Sort(CompFunc comp = CompStart, std::set<AssDialogue*> const& limit = std::set<AssDialogue*>());
-	/// @brief Sort the dialogue lines in the given list
-	/// @param comp Comparison function to use. Defaults to sorting by start time.
-	/// @param limit If non-empty, only lines in this set are sorted
-	static void Sort(EntryList<AssDialogue>& lst, CompFunc comp = CompStart, std::set<AssDialogue*> const& limit = std::set<AssDialogue*>());
+    /// The lines in the file
+    std::vector<AssInfo> Info;
+    EntryList<AssStyle> Styles;
+    EntryList<AssDialogue> Events;
+    std::vector<AssAttachment> Attachments;
+    std::vector<ExtradataEntry> Extradata;
+    ProjectProperties Properties;
+
+    uint32_t next_extradata_id = 0;
+
+    AssFile();
+    AssFile(const AssFile &from);
+    AssFile &operator=(AssFile from);
+    ~AssFile();
+
+    EntryList<AssDialogue>::iterator iterator_to(AssDialogue &line);
+
+    /// @brief Load default file
+    /// @param defline Add a blank line to the file
+    /// @param style_catalog Style catalog name to fill styles from, blank to use default style
+    void LoadDefault(bool defline = true, std::string const &style_catalog = std::string());
+    /// Attach a file to the ass file
+    void InsertAttachment(agi::fs::path const &filename);
+    /// Get the names of all of the styles available
+    std::vector<std::string> GetStyles() const;
+    /// @brief Get a style by name
+    /// @param name Style name
+    /// @return Pointer to style or nullptr
+    AssStyle *GetStyle(std::string const &name);
+
+    void swap(AssFile &) throw();
+
+    /// @brief Get the script resolution
+    /// @param[out] w Width
+    /// @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(std::string const &key) const;
+    /// Get the value in a [Script Info] key as string.
+    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(std::string const &key, std::string const &value);
+
+    /// @brief Add a new extradata entry
+    /// @param key Class identifier/owner for the extradata
+    /// @param value Data for the extradata
+    /// @return ID of the created entry
+    uint32_t AddExtradata(std::string const &key, std::string const &value);
+    /// Fetch all extradata entries from a list of IDs
+    std::vector<ExtradataEntry> GetExtradata(std::vector<uint32_t> const &id_list) const;
+    /// Remove unreferenced extradata entries
+    void CleanExtradata();
+
+    /// Type of changes made in a commit
+    enum CommitType {
+        /// Potentially the entire file has been changed; any saved information
+        /// should be discarded. Note that the active line and selected set
+        /// should not be touched in handlers for this, as they may not have
+        /// been updated yet
+        /// Note that it is intentional that this cannot be combined with
+        /// other commit types
+        COMMIT_NEW         = 0,
+        /// The order of lines in the file has changed
+        COMMIT_ORDER       = 0x1,
+        /// The script info section has changed in some way
+        COMMIT_SCRIPTINFO  = 0x2,
+        /// The styles have changed in some way
+        COMMIT_STYLES      = 0x4,
+        /// The attachments have changed in some way
+        COMMIT_ATTACHMENT  = 0x8,
+        /// Dialogue lines have been added or removed
+        /// Note that if the active dialogue line was removed, the active line
+        /// should be updated BEFORE committing
+        COMMIT_DIAG_ADDREM = 0x10,
+        /// The metadata fields of existing dialogue lines have changed
+        COMMIT_DIAG_META   = 0x20,
+        /// The start and/or end times of existing dialogue lines have changed
+        COMMIT_DIAG_TIME   = 0x40,
+        /// The text of existing dialogue lines have changed
+        COMMIT_DIAG_TEXT   = 0x80,
+        COMMIT_DIAG_FULL   = COMMIT_DIAG_META | COMMIT_DIAG_TIME | COMMIT_DIAG_TEXT,
+        /// Extradata entries were added/modified/removed
+        COMMIT_EXTRADATA   = 0x100,
+    };
+
+    DEFINE_SIGNAL_ADDERS(AnnounceCommit, AddCommitListener)
+    DEFINE_SIGNAL_ADDERS(PushState, AddUndoManager)
+
+    /// @brief Flag the file as modified and push a copy onto the undo stack
+    /// @param desc        Undo description
+    /// @param type        Type of changes made to the file in this commit
+    /// @param commitId    Commit to amend rather than pushing a new commit
+    /// @param single_line Line which was changed, if only one line was
+    /// @return Unique identifier for the new undo group
+    int Commit(wxString const &desc, int type, int commitId = -1, AssDialogue *single_line = nullptr);
+
+    /// Comparison function for use when sorting
+    typedef bool (*CompFunc)(AssDialogue const &lft, AssDialogue const &rgt);
+
+    /// Compare based on start time
+    static bool CompStart(AssDialogue const &lft, AssDialogue const &rgt);
+    /// Compare based on end time
+    static bool CompEnd(AssDialogue const &lft, AssDialogue const &rgt);
+    /// Compare based on style name
+    static bool CompStyle(AssDialogue const &lft, AssDialogue const &rgt);
+    /// Compare based on actor name
+    static bool CompActor(AssDialogue const &lft, AssDialogue const &rgt);
+    /// Compare based on effect
+    static bool CompEffect(AssDialogue const &lft, AssDialogue const &rgt);
+    /// Compare based on layer
+    static bool CompLayer(AssDialogue const &lft, AssDialogue const &rgt);
+
+    /// @brief Sort the dialogue lines in this file
+    /// @param comp Comparison function to use. Defaults to sorting by start time.
+    /// @param limit If non-empty, only lines in this set are sorted
+    void Sort(CompFunc comp = CompStart, std::set<AssDialogue *> const &limit = std::set<AssDialogue *>());
+    /// @brief Sort the dialogue lines in the given list
+    /// @param comp Comparison function to use. Defaults to sorting by start time.
+    /// @param limit If non-empty, only lines in this set are sorted
+    static void Sort(EntryList<AssDialogue> &lst, CompFunc comp = CompStart, std::set<AssDialogue *> const &limit = std::set<AssDialogue *>());
 };
diff --git a/src/ass_info.h b/src/ass_info.h
index af6463d78af4bff8987689d97e3a3e48e460920d..1c2c41bdd75d1819baa19c47dbf8b305d978299e 100644
--- a/src/ass_info.h
+++ b/src/ass_info.h
@@ -17,17 +17,17 @@
 #include "ass_entry.h"
 
 class AssInfo final : public AssEntry {
-	std::string key;
-	std::string value;
+    std::string key;
+    std::string value;
 
 public:
-	AssInfo(AssInfo const& o) = default;
-	AssInfo(std::string key, std::string value) : key(std::move(key)), value(std::move(value)) { }
+    AssInfo(AssInfo const &o) = default;
+    AssInfo(std::string key, std::string value) : key(std::move(key)), value(std::move(value)) { }
 
-	AssEntryGroup Group() const override { return AssEntryGroup::INFO; }
-	std::string GetEntryData() const { return key + ": " + value; }
+    AssEntryGroup Group() const override { return AssEntryGroup::INFO; }
+    std::string GetEntryData() const { return key + ": " + value; }
 
-	std::string Key() const { return key; }
-	std::string Value() const { return value; }
-	void SetValue(std::string 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/src/ass_karaoke.cpp b/src/ass_karaoke.cpp
index 3e45b6a0c983db30b6d3cca14796335617038e98..4876037d4efddd10f3a8594b437a605a2c34fb5f 100644
--- a/src/ass_karaoke.cpp
+++ b/src/ass_karaoke.cpp
@@ -23,250 +23,263 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/trim.hpp>
 
-std::string AssKaraoke::Syllable::GetText(bool k_tag) const {
-	std::string ret;
-
-	if (k_tag)
-		ret = agi::format("{%s%d}", tag_type, ((duration + 5) / 10));
-
-	size_t idx = 0;
-	for (auto const& ovr : ovr_tags) {
-		ret += text.substr(idx, ovr.first - idx);
-		ret += ovr.second;
-		idx = ovr.first;
-	}
-	ret += text.substr(idx);
-	return ret;
+std::string AssKaraoke::Syllable::GetText(bool k_tag) const
+{
+    std::string ret;
+
+    if (k_tag)
+        ret = agi::format("{%s%d}", tag_type, ((duration + 5) / 10));
+
+    size_t idx = 0;
+    for (auto const &ovr : ovr_tags) {
+        ret += text.substr(idx, ovr.first - idx);
+        ret += ovr.second;
+        idx = ovr.first;
+    }
+    ret += text.substr(idx);
+    return ret;
 }
 
-AssKaraoke::AssKaraoke(const AssDialogue *line, bool auto_split, bool normalize) {
-	if (line) SetLine(line, auto_split, normalize);
+AssKaraoke::AssKaraoke(const AssDialogue *line, bool auto_split, bool normalize)
+{
+    if (line) SetLine(line, auto_split, normalize);
 }
 
-void AssKaraoke::SetLine(const AssDialogue *line, bool auto_split, bool normalize) {
-	syls.clear();
-	Syllable syl;
-	syl.start_time = line->Start;
-	syl.duration = 0;
-	syl.tag_type = "\\k";
-
-	ParseSyllables(line, syl);
-
-	if (normalize) {
-		// Normalize the syllables so that the total duration is equal to the line length
-		int end_time = line->End;
-		int last_end = syl.start_time + syl.duration;
-
-		// Total duration is shorter than the line length so just extend the last
-		// syllable; this has no effect on rendering but is easier to work with
-		if (last_end < end_time)
-			syls.back().duration += end_time - last_end;
-		else if (last_end > end_time) {
-			// Truncate any syllables that extend past the end of the line
-			for (auto& syl : syls) {
-				if (syl.start_time > end_time) {
-					syl.start_time = end_time;
-					syl.duration = 0;
-				}
-				else {
-					syl.duration = std::min(syl.duration, end_time - syl.start_time);
-				}
-			}
-		}
-	}
-
-	// Add karaoke splits at each space
-	if (auto_split && syls.size() == 1) {
-		size_t pos;
-		no_announce = true;
-		while ((pos = syls.back().text.find(' ')) != std::string::npos)
-			AddSplit(syls.size() - 1, pos + 1);
-		no_announce = false;
-	}
-
-	AnnounceSyllablesChanged();
+void AssKaraoke::SetLine(const AssDialogue *line, bool auto_split, bool normalize)
+{
+    syls.clear();
+    Syllable syl;
+    syl.start_time = line->Start;
+    syl.duration = 0;
+    syl.tag_type = "\\k";
+
+    ParseSyllables(line, syl);
+
+    if (normalize) {
+        // Normalize the syllables so that the total duration is equal to the line length
+        int end_time = line->End;
+        int last_end = syl.start_time + syl.duration;
+
+        // Total duration is shorter than the line length so just extend the last
+        // syllable; this has no effect on rendering but is easier to work with
+        if (last_end < end_time)
+            syls.back().duration += end_time - last_end;
+        else if (last_end > end_time) {
+            // Truncate any syllables that extend past the end of the line
+            for (auto &syl : syls) {
+                if (syl.start_time > end_time) {
+                    syl.start_time = end_time;
+                    syl.duration = 0;
+                }
+                else {
+                    syl.duration = std::min(syl.duration, end_time - syl.start_time);
+                }
+            }
+        }
+    }
+
+    // Add karaoke splits at each space
+    if (auto_split && syls.size() == 1) {
+        size_t pos;
+        no_announce = true;
+        while ((pos = syls.back().text.find(' ')) != std::string::npos)
+            AddSplit(syls.size() - 1, pos + 1);
+        no_announce = false;
+    }
+
+    AnnounceSyllablesChanged();
 }
 
-void AssKaraoke::ParseSyllables(const AssDialogue *line, Syllable &syl) {
-	for (auto& block : line->ParseTags()) {
-		std::string text = block->GetText();
-
-		switch (block->GetType()) {
-		case AssBlockType::PLAIN:
-			syl.text += text;
-			break;
-		case AssBlockType::COMMENT:
-		// drawings aren't override tags but they shouldn't show up in the
-		// stripped text so pretend they are
-		case AssBlockType::DRAWING:
-			syl.ovr_tags[syl.text.size()] += text;
-			break;
-		case AssBlockType::OVERRIDE:
-			auto ovr = static_cast<AssDialogueBlockOverride*>(block.get());
-			bool in_tag = false;
-			for (auto& tag : ovr->Tags) {
-				if (tag.IsValid() && boost::istarts_with(tag.Name, "\\k")) {
-					if (in_tag) {
-						syl.ovr_tags[syl.text.size()] += "}";
-						in_tag = false;
-					}
-
-					// Dealing with both \K and \kf is mildly annoying so just
-					// convert them both to \kf
-					if (tag.Name == "\\K") tag.Name = "\\kf";
-
-					// Don't bother including zero duration zero length syls
-					if (syl.duration > 0 || !syl.text.empty()) {
-						syls.push_back(syl);
-						syl.text.clear();
-						syl.ovr_tags.clear();
-					}
-
-					syl.tag_type = tag.Name;
-					syl.start_time += syl.duration;
-					syl.duration = tag.Params[0].Get(0) * 10;
-				}
-				else {
-					std::string& otext = syl.ovr_tags[syl.text.size()];
-					// Merge adjacent override tags
-					boost::trim_right_if(text, [](char c) { return c == '}'; });
-					if (!in_tag)
-						otext += "{";
-
-					in_tag = true;
-					otext += tag;
-				}
-			}
-
-			if (in_tag)
-				syl.ovr_tags[syl.text.size()] += "}";
-			break;
-		}
-	}
-
-	syls.push_back(syl);
+void AssKaraoke::ParseSyllables(const AssDialogue *line, Syllable &syl)
+{
+    for (auto &block : line->ParseTags()) {
+        std::string text = block->GetText();
+
+        switch (block->GetType()) {
+        case AssBlockType::PLAIN:
+            syl.text += text;
+            break;
+        case AssBlockType::COMMENT:
+        // drawings aren't override tags but they shouldn't show up in the
+        // stripped text so pretend they are
+        case AssBlockType::DRAWING:
+            syl.ovr_tags[syl.text.size()] += text;
+            break;
+        case AssBlockType::OVERRIDE:
+            auto ovr = static_cast<AssDialogueBlockOverride *>(block.get());
+            bool in_tag = false;
+            for (auto &tag : ovr->Tags) {
+                if (tag.IsValid() && boost::istarts_with(tag.Name, "\\k")) {
+                    if (in_tag) {
+                        syl.ovr_tags[syl.text.size()] += "}";
+                        in_tag = false;
+                    }
+
+                    // Dealing with both \K and \kf is mildly annoying so just
+                    // convert them both to \kf
+                    if (tag.Name == "\\K") tag.Name = "\\kf";
+
+                    // Don't bother including zero duration zero length syls
+                    if (syl.duration > 0 || !syl.text.empty()) {
+                        syls.push_back(syl);
+                        syl.text.clear();
+                        syl.ovr_tags.clear();
+                    }
+
+                    syl.tag_type = tag.Name;
+                    syl.start_time += syl.duration;
+                    syl.duration = tag.Params[0].Get(0) * 10;
+                }
+                else {
+                    std::string &otext = syl.ovr_tags[syl.text.size()];
+                    // Merge adjacent override tags
+                    boost::trim_right_if(text, [](char c) { return c == '}'; });
+                    if (!in_tag)
+                        otext += "{";
+
+                    in_tag = true;
+                    otext += tag;
+                }
+            }
+
+            if (in_tag)
+                syl.ovr_tags[syl.text.size()] += "}";
+            break;
+        }
+    }
+
+    syls.push_back(syl);
 }
 
-std::string AssKaraoke::GetText() const {
-	std::string text;
-	text.reserve(size() * 10);
+std::string AssKaraoke::GetText() const
+{
+    std::string text;
+    text.reserve(size() * 10);
 
-	for (auto const& syl : syls)
-		text += syl.GetText(true);
+    for (auto const &syl : syls)
+        text += syl.GetText(true);
 
-	return text;
+    return text;
 }
 
-std::string AssKaraoke::GetTagType() const {
-	return begin()->tag_type;
+std::string AssKaraoke::GetTagType() const
+{
+    return begin()->tag_type;
 }
 
-void AssKaraoke::SetTagType(std::string const& new_type) {
-	for (auto& syl : syls)
-		syl.tag_type = new_type;
+void AssKaraoke::SetTagType(std::string const &new_type)
+{
+    for (auto &syl : syls)
+        syl.tag_type = new_type;
 }
 
-void AssKaraoke::AddSplit(size_t syl_idx, size_t pos) {
-	syls.insert(syls.begin() + syl_idx + 1, Syllable());
-	Syllable &syl = syls[syl_idx];
-	Syllable &new_syl = syls[syl_idx + 1];
-
-	// If the syl is empty or the user is adding a syllable past the last
-	// character then pos will be out of bounds. Doing this is a bit goofy,
-	// but it's sometimes required for complex karaoke scripts
-	if (pos < syl.text.size()) {
-		new_syl.text = syl.text.substr(pos);
-		syl.text = syl.text.substr(0, pos);
-	}
-
-	if (new_syl.text.empty())
-		new_syl.duration = 0;
-	else if (syl.text.empty()) {
-		new_syl.duration = syl.duration;
-		syl.duration = 0;
-	}
-	else {
-		new_syl.duration = (syl.duration * new_syl.text.size() / (syl.text.size() + new_syl.text.size()) + 5) / 10 * 10;
-		syl.duration -= new_syl.duration;
-	}
-
-	assert(syl.duration >= 0);
-
-	new_syl.start_time = syl.start_time + syl.duration;
-	new_syl.tag_type = syl.tag_type;
-
-	// Move all override tags after the split to the new syllable and fix the indices
-	size_t text_len = syl.text.size();
-	for (auto it = syl.ovr_tags.begin(); it != syl.ovr_tags.end(); ) {
-		if (it->first < text_len)
-			++it;
-		else {
-			new_syl.ovr_tags[it->first - text_len] = it->second;
-			syl.ovr_tags.erase(it++);
-		}
-	}
-
-	if (!no_announce) AnnounceSyllablesChanged();
+void AssKaraoke::AddSplit(size_t syl_idx, size_t pos)
+{
+    syls.insert(syls.begin() + syl_idx + 1, Syllable());
+    Syllable &syl = syls[syl_idx];
+    Syllable &new_syl = syls[syl_idx + 1];
+
+    // If the syl is empty or the user is adding a syllable past the last
+    // character then pos will be out of bounds. Doing this is a bit goofy,
+    // but it's sometimes required for complex karaoke scripts
+    if (pos < syl.text.size()) {
+        new_syl.text = syl.text.substr(pos);
+        syl.text = syl.text.substr(0, pos);
+    }
+
+    if (new_syl.text.empty())
+        new_syl.duration = 0;
+    else if (syl.text.empty()) {
+        new_syl.duration = syl.duration;
+        syl.duration = 0;
+    }
+    else {
+        new_syl.duration = (syl.duration * new_syl.text.size() / (syl.text.size() + new_syl.text.size()) + 5) / 10 * 10;
+        syl.duration -= new_syl.duration;
+    }
+
+    assert(syl.duration >= 0);
+
+    new_syl.start_time = syl.start_time + syl.duration;
+    new_syl.tag_type = syl.tag_type;
+
+    // Move all override tags after the split to the new syllable and fix the indices
+    size_t text_len = syl.text.size();
+    for (auto it = syl.ovr_tags.begin(); it != syl.ovr_tags.end(); ) {
+        if (it->first < text_len)
+            ++it;
+        else {
+            new_syl.ovr_tags[it->first - text_len] = it->second;
+            syl.ovr_tags.erase(it++);
+        }
+    }
+
+    if (!no_announce) AnnounceSyllablesChanged();
 }
 
-void AssKaraoke::RemoveSplit(size_t syl_idx) {
-	// Don't allow removing the first syllable
-	if (syl_idx == 0) return;
+void AssKaraoke::RemoveSplit(size_t syl_idx)
+{
+    // Don't allow removing the first syllable
+    if (syl_idx == 0) return;
 
-	Syllable &syl = syls[syl_idx];
-	Syllable &prev = syls[syl_idx - 1];
+    Syllable &syl = syls[syl_idx];
+    Syllable &prev = syls[syl_idx - 1];
 
-	prev.duration += syl.duration;
-	for (auto const& tag : syl.ovr_tags)
-		prev.ovr_tags[tag.first + prev.text.size()] = tag.second;
-	prev.text += syl.text;
+    prev.duration += syl.duration;
+    for (auto const &tag : syl.ovr_tags)
+        prev.ovr_tags[tag.first + prev.text.size()] = tag.second;
+    prev.text += syl.text;
 
-	syls.erase(syls.begin() + syl_idx);
+    syls.erase(syls.begin() + syl_idx);
 
-	if (!no_announce) AnnounceSyllablesChanged();
+    if (!no_announce) AnnounceSyllablesChanged();
 }
 
-void AssKaraoke::SetStartTime(size_t syl_idx, int time) {
-	// Don't allow moving the first syllable
-	if (syl_idx == 0) return;
+void AssKaraoke::SetStartTime(size_t syl_idx, int time)
+{
+    // Don't allow moving the first syllable
+    if (syl_idx == 0) return;
 
-	Syllable &syl = syls[syl_idx];
-	Syllable &prev = syls[syl_idx - 1];
+    Syllable &syl = syls[syl_idx];
+    Syllable &prev = syls[syl_idx - 1];
 
-	assert(time >= prev.start_time);
-	assert(time <= syl.start_time + syl.duration);
+    assert(time >= prev.start_time);
+    assert(time <= syl.start_time + syl.duration);
 
-	int delta = time - syl.start_time;
-	syl.start_time = time;
-	syl.duration -= delta;
-	prev.duration += delta;
+    int delta = time - syl.start_time;
+    syl.start_time = time;
+    syl.duration -= delta;
+    prev.duration += delta;
 }
 
-void AssKaraoke::SetLineTimes(int start_time, int end_time) {
-	assert(end_time >= start_time);
-
-	size_t idx = 0;
-	// Chop off any portion of syllables starting before the new start_time
-	do {
-		int delta = start_time - syls[idx].start_time;
-		syls[idx].start_time = start_time;
-		syls[idx].duration = std::max(0, syls[idx].duration - delta);
-	} while (++idx < syls.size() && syls[idx].start_time < start_time);
-
-	// And truncate any syllables ending after the new end_time
-	idx = syls.size() - 1;
-	while (syls[idx].start_time > end_time) {
-		syls[idx].start_time = end_time;
-		syls[idx].duration = 0;
-		--idx;
-	}
-	syls[idx].duration = end_time - syls[idx].start_time;
+void AssKaraoke::SetLineTimes(int start_time, int end_time)
+{
+    assert(end_time >= start_time);
+
+    size_t idx = 0;
+    // Chop off any portion of syllables starting before the new start_time
+    do {
+        int delta = start_time - syls[idx].start_time;
+        syls[idx].start_time = start_time;
+        syls[idx].duration = std::max(0, syls[idx].duration - delta);
+    } while (++idx < syls.size() && syls[idx].start_time < start_time);
+
+    // And truncate any syllables ending after the new end_time
+    idx = syls.size() - 1;
+    while (syls[idx].start_time > end_time) {
+        syls[idx].start_time = end_time;
+        syls[idx].duration = 0;
+        --idx;
+    }
+    syls[idx].duration = end_time - syls[idx].start_time;
 }
 
-std::string AssKaraoke::GetStrippedText(int syl_idx) const {
-	return syls[syl_idx].text;
+std::string AssKaraoke::GetStrippedText(int syl_idx) const
+{
+    return syls[syl_idx].text;
 }
 
-void AssKaraoke::SetStrippedText(int syl_idx, std::string new_text) {
-	syls[syl_idx].text = new_text;
+void AssKaraoke::SetStrippedText(int syl_idx, std::string new_text)
+{
+    syls[syl_idx].text = new_text;
 }
diff --git a/src/ass_karaoke.h b/src/ass_karaoke.h
index 99cb68e32fdcc8b460810213b19a3e163a4f21d1..1c34178256cd31bbdeeab8a7b3255125116b6808 100644
--- a/src/ass_karaoke.h
+++ b/src/ass_karaoke.h
@@ -27,65 +27,65 @@ class AssDialogue;
 /// @brief Karaoke parser and parsed karaoke data model
 class AssKaraoke {
 public:
-	/// Parsed syllable data
-	struct Syllable {
-		int start_time; ///< Start time relative to time zero (not line start) in milliseconds
-		int duration;   ///< Duration in milliseconds
-		std::string text; ///< Stripped syllable text
-		std::string tag_type; ///< \k, \kf or \ko
-		/// Non-karaoke override tags in this syllable. Key is an index in text
-		/// before which the value should be inserted
-		std::map<size_t, std::string> ovr_tags;
-
-		/// Get the text of this line with override tags and optionally the karaoke tag
-		std::string GetText(bool k_tag) const;
-	};
+    /// Parsed syllable data
+    struct Syllable {
+        int start_time; ///< Start time relative to time zero (not line start) in milliseconds
+        int duration;   ///< Duration in milliseconds
+        std::string text; ///< Stripped syllable text
+        std::string tag_type; ///< \k, \kf or \ko
+        /// Non-karaoke override tags in this syllable. Key is an index in text
+        /// before which the value should be inserted
+        std::map<size_t, std::string> ovr_tags;
+
+        /// Get the text of this line with override tags and optionally the karaoke tag
+        std::string GetText(bool k_tag) const;
+    };
 private:
-	std::vector<Syllable> syls;
+    std::vector<Syllable> syls;
 
-	bool no_announce = false;
+    bool no_announce = false;
 
-	agi::signal::Signal<> AnnounceSyllablesChanged;
-	void ParseSyllables(const AssDialogue *line, Syllable &syl);
+    agi::signal::Signal<> AnnounceSyllablesChanged;
+    void ParseSyllables(const AssDialogue *line, Syllable &syl);
 
 public:
-	/// Constructor
-	/// @param line Initial line
-	/// @param auto_split Should the line automatically be split on spaces if there are no k tags?
-	/// @param normalize Should the total duration of the syllables be forced to equal the line duration?
-	AssKaraoke(const AssDialogue *line = nullptr, bool auto_split = false, bool normalize = true);
-
-	/// Parse a dialogue line
-	void SetLine(const AssDialogue *line, bool auto_split = false, bool normalize = true);
-
-	/// Add a split before character pos in syllable syl_idx
-	void AddSplit(size_t syl_idx, size_t pos);
-	/// Remove the split at the given index
-	void RemoveSplit(size_t syl_idx);
-	/// Set the start time of a syllable in ms
-	void SetStartTime(size_t syl_idx, int time);
-	/// Adjust the line's start and end times without shifting the syllables
-	void SetLineTimes(int start_time, int end_time);
-
-	typedef std::vector<Syllable>::const_iterator iterator;
-
-	iterator begin() const { return syls.begin(); }
-	iterator end() const { return syls.end(); }
-	size_t size() const { return syls.size(); }
-
-	/// Get the line's text with k tags
-	std::string GetText() const;
-
-	/// Get the karaoke tag type used, with leading slash
-	/// @returns "\k", "\kf", or "\ko"
-	std::string GetTagType() const;
-	/// Set the tag type for all karaoke tags in this line
-	void SetTagType(std::string const& new_type);
-
-	/// Get syllab's text stripped of k tag
-	std::string GetStrippedText(int syl_idx) const;
-	/// Set syllab's text stripped of k tag
-	void SetStrippedText(int syl_idx, std::string new_text);
-
-	DEFINE_SIGNAL_ADDERS(AnnounceSyllablesChanged, AddSyllablesChangedListener)
+    /// Constructor
+    /// @param line Initial line
+    /// @param auto_split Should the line automatically be split on spaces if there are no k tags?
+    /// @param normalize Should the total duration of the syllables be forced to equal the line duration?
+    AssKaraoke(const AssDialogue *line = nullptr, bool auto_split = false, bool normalize = true);
+
+    /// Parse a dialogue line
+    void SetLine(const AssDialogue *line, bool auto_split = false, bool normalize = true);
+
+    /// Add a split before character pos in syllable syl_idx
+    void AddSplit(size_t syl_idx, size_t pos);
+    /// Remove the split at the given index
+    void RemoveSplit(size_t syl_idx);
+    /// Set the start time of a syllable in ms
+    void SetStartTime(size_t syl_idx, int time);
+    /// Adjust the line's start and end times without shifting the syllables
+    void SetLineTimes(int start_time, int end_time);
+
+    typedef std::vector<Syllable>::const_iterator iterator;
+
+    iterator begin() const { return syls.begin(); }
+    iterator end() const { return syls.end(); }
+    size_t size() const { return syls.size(); }
+
+    /// Get the line's text with k tags
+    std::string GetText() const;
+
+    /// Get the karaoke tag type used, with leading slash
+    /// @returns "\k", "\kf", or "\ko"
+    std::string GetTagType() const;
+    /// Set the tag type for all karaoke tags in this line
+    void SetTagType(std::string const &new_type);
+
+    /// Get syllab's text stripped of k tag
+    std::string GetStrippedText(int syl_idx) const;
+    /// Set syllab's text stripped of k tag
+    void SetStrippedText(int syl_idx, std::string new_text);
+
+    DEFINE_SIGNAL_ADDERS(AnnounceSyllablesChanged, AddSyllablesChangedListener)
 };
diff --git a/src/ass_override.cpp b/src/ass_override.cpp
index fc8758c089fbd435aadb20304708dd65aa3664d7..a4415d1a027e2b9336f2bd6beb0ce97372b5fbcd 100644
--- a/src/ass_override.cpp
+++ b/src/ass_override.cpp
@@ -47,74 +47,85 @@
 using namespace boost::adaptors;
 
 AssOverrideParameter::AssOverrideParameter(VariableDataType type, AssParameterClass classification)
-: type(type)
-, classification(classification)
+    : type(type)
+    , classification(classification)
 {
 }
 
 AssOverrideParameter::~AssOverrideParameter() = default;
 
-template<> std::string AssOverrideParameter::Get<std::string>() const {
-	if (omitted) throw agi::InternalError("AssOverrideParameter::Get() called on omitted parameter");
-	if (block.get()) {
-		std::string str(block->GetText());
-		if (boost::starts_with(str, "{")) str.erase(begin(str));
-		if (boost::ends_with(str, "}")) str.erase(end(str) - 1);
-		return str;
-	}
-	return value;
+template<> std::string AssOverrideParameter::Get<std::string>() const
+{
+    if (omitted) throw agi::InternalError("AssOverrideParameter::Get() called on omitted parameter");
+    if (block.get()) {
+        std::string str(block->GetText());
+        if (boost::starts_with(str, "{")) str.erase(begin(str));
+        if (boost::ends_with(str, "}")) str.erase(end(str) - 1);
+        return str;
+    }
+    return value;
 }
 
-template<> int AssOverrideParameter::Get<int>() const {
-	if (classification == AssParameterClass::ALPHA)
-		// &Hxx&, but vsfilter lets you leave everything out
-		return mid<int>(0, strtol(std::find_if(value.c_str(), value.c_str() + value.size(), isxdigit), nullptr, 16), 255);
-	return atoi(Get<std::string>().c_str());
+template<> int AssOverrideParameter::Get<int>() const
+{
+    if (classification == AssParameterClass::ALPHA)
+        // &Hxx&, but vsfilter lets you leave everything out
+        return mid<int>(0, strtol(std::find_if(value.c_str(), value.c_str() + value.size(), isxdigit), nullptr, 16), 255);
+    return atoi(Get<std::string>().c_str());
 }
 
-template<> double AssOverrideParameter::Get<double>() const {
-	return atof(Get<std::string>().c_str());
+template<> double AssOverrideParameter::Get<double>() const
+{
+    return atof(Get<std::string>().c_str());
 }
 
-template<> float AssOverrideParameter::Get<float>() const {
-	return atof(Get<std::string>().c_str());
+template<> float AssOverrideParameter::Get<float>() const
+{
+    return atof(Get<std::string>().c_str());
 }
 
-template<> bool AssOverrideParameter::Get<bool>() const {
-	return Get<int>() != 0;
+template<> bool AssOverrideParameter::Get<bool>() const
+{
+    return Get<int>() != 0;
 }
 
-template<> agi::Color AssOverrideParameter::Get<agi::Color>() const {
-	return Get<std::string>();
+template<> agi::Color AssOverrideParameter::Get<agi::Color>() const
+{
+    return Get<std::string>();
 }
 
-template<> AssDialogueBlockOverride *AssOverrideParameter::Get<AssDialogueBlockOverride*>() const {
-	if (!block) {
-		block = agi::make_unique<AssDialogueBlockOverride>(Get<std::string>());
-		block->ParseTags();
-	}
-	return block.get();
+template<> AssDialogueBlockOverride *AssOverrideParameter::Get<AssDialogueBlockOverride *>() const
+{
+    if (!block) {
+        block = agi::make_unique<AssDialogueBlockOverride>(Get<std::string>());
+        block->ParseTags();
+    }
+    return block.get();
 }
 
-template<> void AssOverrideParameter::Set<std::string>(std::string new_value) {
-	omitted = false;
-	value = new_value;
-	block.reset();
+template<> void AssOverrideParameter::Set<std::string>(std::string new_value)
+{
+    omitted = false;
+    value = new_value;
+    block.reset();
 }
 
-template<> void AssOverrideParameter::Set<int>(int new_value) {
-	if (classification == AssParameterClass::ALPHA)
-		Set(agi::format("&H%02X&", mid(0, new_value, 255)));
-	else
-		Set(std::to_string(new_value));
+template<> void AssOverrideParameter::Set<int>(int new_value)
+{
+    if (classification == AssParameterClass::ALPHA)
+        Set(agi::format("&H%02X&", mid(0, new_value, 255)));
+    else
+        Set(std::to_string(new_value));
 }
 
-template<> void AssOverrideParameter::Set<double>(double new_value) {
-	Set(float_to_string(new_value));
+template<> void AssOverrideParameter::Set<double>(double new_value)
+{
+    Set(float_to_string(new_value));
 }
 
-template<> void AssOverrideParameter::Set<bool>(bool new_value) {
-	Set<int>(new_value);
+template<> void AssOverrideParameter::Set<bool>(bool new_value)
+{
+    Set<int>(new_value);
 }
 
 namespace {
@@ -123,353 +134,364 @@ namespace {
 /// as optional; this is just to know which parameters to skip when there are
 /// earlier optional arguments
 enum AssParameterOptional {
-	NOT_OPTIONAL = 0xFF,
-	OPTIONAL_1 = 0x01,
-	OPTIONAL_2 = 0x02,
-	OPTIONAL_3 = 0x04,
-	OPTIONAL_4 = 0x08,
-	OPTIONAL_5 = 0x10,
-	OPTIONAL_6 = 0x20,
-	OPTIONAL_7 = 0x40
+    NOT_OPTIONAL = 0xFF,
+    OPTIONAL_1 = 0x01,
+    OPTIONAL_2 = 0x02,
+    OPTIONAL_3 = 0x04,
+    OPTIONAL_4 = 0x08,
+    OPTIONAL_5 = 0x10,
+    OPTIONAL_6 = 0x20,
+    OPTIONAL_7 = 0x40
 };
 
 /// Prototype of a single override parameter
 struct AssOverrideParamProto {
-	/// ASS_ParameterOptional
-	int optional;
+    /// ASS_ParameterOptional
+    int optional;
 
-	/// Type of this parameter
-	VariableDataType type;
+    /// Type of this parameter
+    VariableDataType type;
 
-	/// Semantic type of this parameter
-	AssParameterClass classification;
+    /// Semantic type of this parameter
+    AssParameterClass classification;
 };
 
 struct AssOverrideTagProto {
-	/// Name of the tag, with slash
-	std::string name;
-
-	/// Parameters to this tag
-	std::vector<AssOverrideParamProto> params;
-
-	typedef std::vector<AssOverrideTagProto>::iterator iterator;
-
-	/// @brief Add a parameter to this tag prototype
-	/// @param type Data type of the parameter
-	/// @param classi Semantic type of the parameter
-	/// @param opt Situations in which this parameter is present
-	void AddParam(VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) {
-		params.push_back(AssOverrideParamProto{opt, type, classi});
-	}
-
-	/// @brief Convenience function for single-argument tags
-	/// @param name Name of the tag, with slash
-	/// @param type Data type of the parameter
-	/// @param classi Semantic type of the parameter
-	/// @param opt Situations in which this parameter is present
-	void Set(const char *name, VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) {
-		this->name = name;
-		params.push_back(AssOverrideParamProto{opt, type, classi});
-	}
+    /// Name of the tag, with slash
+    std::string name;
+
+    /// Parameters to this tag
+    std::vector<AssOverrideParamProto> params;
+
+    typedef std::vector<AssOverrideTagProto>::iterator iterator;
+
+    /// @brief Add a parameter to this tag prototype
+    /// @param type Data type of the parameter
+    /// @param classi Semantic type of the parameter
+    /// @param opt Situations in which this parameter is present
+    void AddParam(VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) {
+        params.push_back(AssOverrideParamProto{opt, type, classi});
+    }
+
+    /// @brief Convenience function for single-argument tags
+    /// @param name Name of the tag, with slash
+    /// @param type Data type of the parameter
+    /// @param classi Semantic type of the parameter
+    /// @param opt Situations in which this parameter is present
+    void Set(const char *name, VariableDataType type, AssParameterClass classi = AssParameterClass::NORMAL, int opt = NOT_OPTIONAL) {
+        this->name = name;
+        params.push_back(AssOverrideParamProto{opt, type, classi});
+    }
 };
 
 static std::vector<AssOverrideTagProto> proto;
-static void load_protos() {
-	if (!proto.empty()) return;
-
-	proto.resize(56);
-	int i = 0;
-
-	// Longer tag names must appear before shorter tag names
-
-	proto[0].Set("\\alpha", VariableDataType::TEXT, AssParameterClass::ALPHA); // \alpha&H<aa>&
-	proto[++i].Set("\\bord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \bord<depth>
-	proto[++i].Set("\\xbord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xbord<depth>
-	proto[++i].Set("\\ybord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \ybord<depth>
-	proto[++i].Set("\\shad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \shad<depth>
-	proto[++i].Set("\\xshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xshad<depth>
-	proto[++i].Set("\\yshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \yshad<depth>
-
-	// \fade(<a1>,<a2>,<a3>,<t1>,<t2>,<t3>,<t4>)
-	i++;
-	proto[i].name = "\\fade";
-	proto[i].AddParam(VariableDataType::INT);
-	proto[i].AddParam(VariableDataType::INT);
-	proto[i].AddParam(VariableDataType::INT);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-
-	// \move(<x1>,<y1>,<x2>,<y2>[,<t1>,<t2>])
-	i++;
-	proto[i].name = "\\move";
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-
-	// If these are rearranged, keep rect clip and vector clip adjacent in this order
-	// \clip(<x1>,<y1>,<x2>,<y2>)
-	i++;
-	proto[i].name = "\\clip";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
-
-	// \clip([<scale>,]<some drawings>)
-	i++;
-	proto[i].name = "\\clip";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL,OPTIONAL_2);
-	proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING);
-
-	// \iclip(<x1>,<y1>,<x2>,<y2>)
-	i++;
-	proto[i].name = "\\iclip";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
-
-	// \iclip([<scale>,]<some drawings>)
-	i++;
-	proto[i].name = "\\iclip";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL,OPTIONAL_2);
-	proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING);
-
-	proto[++i].Set("\\fscx", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_X); // \fscx<percent>
-	proto[++i].Set("\\fscy", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_Y); // \fscy<percent>
-	// \pos(<x>,<y>)
-	i++;
-	proto[i].name = "\\pos";
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
-
-	// \org(<x>,<y>)
-	i++;
-	proto[i].name = "\\org";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
-
-	proto[++i].Set("\\pbo", VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); // \pbo<y>
-	// \fad(<t1>,<t2>)
-	i++;
-	proto[i].name = "\\fad";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_END);
-
-	proto[++i].Set("\\fsp", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fsp<pixels>
-	proto[++i].Set("\\frx", VariableDataType::FLOAT); // \frx<degrees>
-	proto[++i].Set("\\fry", VariableDataType::FLOAT); // \fry<degrees>
-	proto[++i].Set("\\frz", VariableDataType::FLOAT); // \frz<degrees>
-	proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr<degrees>
-	proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax<factor>
-	proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay<factor>
-	proto[++i].Set("\\1c", VariableDataType::TEXT, AssParameterClass::COLOR); // \1c&H<bbggrr>&
-	proto[++i].Set("\\2c", VariableDataType::TEXT, AssParameterClass::COLOR); // \2c&H<bbggrr>&
-	proto[++i].Set("\\3c", VariableDataType::TEXT, AssParameterClass::COLOR); // \3c&H<bbggrr>&
-	proto[++i].Set("\\4c", VariableDataType::TEXT, AssParameterClass::COLOR); // \4c&H<bbggrr>&
-	proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H<aa>&
-	proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H<aa>&
-	proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H<aa>&
-	proto[++i].Set("\\4a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \4a&H<aa>&
-	proto[++i].Set("\\fe", VariableDataType::TEXT); // \fe<charset>
-	proto[++i].Set("\\ko", VariableDataType::INT, AssParameterClass::KARAOKE); // \ko<duration>
-	proto[++i].Set("\\kf", VariableDataType::INT, AssParameterClass::KARAOKE); // \kf<duration>
-	proto[++i].Set("\\be", VariableDataType::INT, AssParameterClass::ABSOLUTE_SIZE); // \be<strength>
-	proto[++i].Set("\\blur", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \blur<strength>
-	proto[++i].Set("\\fn", VariableDataType::TEXT); // \fn<name>
-	proto[++i].Set("\\fs+", VariableDataType::FLOAT); // \fs+<size>
-	proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs-<size>
-	proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs<size>
-	proto[++i].Set("\\an", VariableDataType::INT); // \an<alignment>
-	proto[++i].Set("\\c", VariableDataType::TEXT, AssParameterClass::COLOR); // \c&H<bbggrr>&
-	proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight>
-	proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1>
-	proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1>
-	proto[++i].Set("\\s", VariableDataType::BOOL); // \s<0/1>
-	proto[++i].Set("\\a", VariableDataType::INT); // \a<alignment>
-	proto[++i].Set("\\k", VariableDataType::INT, AssParameterClass::KARAOKE); // \k<duration>
-	proto[++i].Set("\\K", VariableDataType::INT, AssParameterClass::KARAOKE); // \K<duration>
-	proto[++i].Set("\\q", VariableDataType::INT); // \q<0-3>
-	proto[++i].Set("\\p", VariableDataType::INT); // \p<n>
-	proto[++i].Set("\\r", VariableDataType::TEXT); // \r[<name>]
-
-	// \t([<t1>,<t2>,][<accel>,]<style modifiers>)
-	i++;
-	proto[i].name = "\\t";
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START,OPTIONAL_3 | OPTIONAL_4);
-	proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START,OPTIONAL_3 | OPTIONAL_4);
-	proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::NORMAL,OPTIONAL_2 | OPTIONAL_4);
-	proto[i].AddParam(VariableDataType::BLOCK);
+static void load_protos()
+{
+    if (!proto.empty()) return;
+
+    proto.resize(56);
+    int i = 0;
+
+    // Longer tag names must appear before shorter tag names
+
+    proto[0].Set("\\alpha", VariableDataType::TEXT, AssParameterClass::ALPHA); // \alpha&H<aa>&
+    proto[++i].Set("\\bord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \bord<depth>
+    proto[++i].Set("\\xbord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xbord<depth>
+    proto[++i].Set("\\ybord", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \ybord<depth>
+    proto[++i].Set("\\shad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \shad<depth>
+    proto[++i].Set("\\xshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \xshad<depth>
+    proto[++i].Set("\\yshad", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \yshad<depth>
+
+    // \fade(<a1>,<a2>,<a3>,<t1>,<t2>,<t3>,<t4>)
+    i++;
+    proto[i].name = "\\fade";
+    proto[i].AddParam(VariableDataType::INT);
+    proto[i].AddParam(VariableDataType::INT);
+    proto[i].AddParam(VariableDataType::INT);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+
+    // \move(<x1>,<y1>,<x2>,<y2>[,<t1>,<t2>])
+    i++;
+    proto[i].name = "\\move";
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+
+    // If these are rearranged, keep rect clip and vector clip adjacent in this order
+    // \clip(<x1>,<y1>,<x2>,<y2>)
+    i++;
+    proto[i].name = "\\clip";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
+
+    // \clip([<scale>,]<some drawings>)
+    i++;
+    proto[i].name = "\\clip";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL, OPTIONAL_2);
+    proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING);
+
+    // \iclip(<x1>,<y1>,<x2>,<y2>)
+    i++;
+    proto[i].name = "\\iclip";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
+
+    // \iclip([<scale>,]<some drawings>)
+    i++;
+    proto[i].name = "\\iclip";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::NORMAL, OPTIONAL_2);
+    proto[i].AddParam(VariableDataType::TEXT, AssParameterClass::DRAWING);
+
+    proto[++i].Set("\\fscx", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_X); // \fscx<percent>
+    proto[++i].Set("\\fscy", VariableDataType::FLOAT, AssParameterClass::RELATIVE_SIZE_Y); // \fscy<percent>
+    // \pos(<x>,<y>)
+    i++;
+    proto[i].name = "\\pos";
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_POS_Y);
+
+    // \org(<x>,<y>)
+    i++;
+    proto[i].name = "\\org";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_X);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y);
+
+    proto[++i].Set("\\pbo", VariableDataType::INT, AssParameterClass::ABSOLUTE_POS_Y); // \pbo<y>
+    // \fad(<t1>,<t2>)
+    i++;
+    proto[i].name = "\\fad";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_END);
+
+    proto[++i].Set("\\fsp", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fsp<pixels>
+    proto[++i].Set("\\frx", VariableDataType::FLOAT); // \frx<degrees>
+    proto[++i].Set("\\fry", VariableDataType::FLOAT); // \fry<degrees>
+    proto[++i].Set("\\frz", VariableDataType::FLOAT); // \frz<degrees>
+    proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr<degrees>
+    proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax<factor>
+    proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay<factor>
+    proto[++i].Set("\\1c", VariableDataType::TEXT, AssParameterClass::COLOR); // \1c&H<bbggrr>&
+    proto[++i].Set("\\2c", VariableDataType::TEXT, AssParameterClass::COLOR); // \2c&H<bbggrr>&
+    proto[++i].Set("\\3c", VariableDataType::TEXT, AssParameterClass::COLOR); // \3c&H<bbggrr>&
+    proto[++i].Set("\\4c", VariableDataType::TEXT, AssParameterClass::COLOR); // \4c&H<bbggrr>&
+    proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H<aa>&
+    proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H<aa>&
+    proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H<aa>&
+    proto[++i].Set("\\4a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \4a&H<aa>&
+    proto[++i].Set("\\fe", VariableDataType::TEXT); // \fe<charset>
+    proto[++i].Set("\\ko", VariableDataType::INT, AssParameterClass::KARAOKE); // \ko<duration>
+    proto[++i].Set("\\kf", VariableDataType::INT, AssParameterClass::KARAOKE); // \kf<duration>
+    proto[++i].Set("\\be", VariableDataType::INT, AssParameterClass::ABSOLUTE_SIZE); // \be<strength>
+    proto[++i].Set("\\blur", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \blur<strength>
+    proto[++i].Set("\\fn", VariableDataType::TEXT); // \fn<name>
+    proto[++i].Set("\\fs+", VariableDataType::FLOAT); // \fs+<size>
+    proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs-<size>
+    proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs<size>
+    proto[++i].Set("\\an", VariableDataType::INT); // \an<alignment>
+    proto[++i].Set("\\c", VariableDataType::TEXT, AssParameterClass::COLOR); // \c&H<bbggrr>&
+    proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight>
+    proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1>
+    proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1>
+    proto[++i].Set("\\s", VariableDataType::BOOL); // \s<0/1>
+    proto[++i].Set("\\a", VariableDataType::INT); // \a<alignment>
+    proto[++i].Set("\\k", VariableDataType::INT, AssParameterClass::KARAOKE); // \k<duration>
+    proto[++i].Set("\\K", VariableDataType::INT, AssParameterClass::KARAOKE); // \K<duration>
+    proto[++i].Set("\\q", VariableDataType::INT); // \q<0-3>
+    proto[++i].Set("\\p", VariableDataType::INT); // \p<n>
+    proto[++i].Set("\\r", VariableDataType::TEXT); // \r[<name>]
+
+    // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
+    i++;
+    proto[i].name = "\\t";
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START, OPTIONAL_3 | OPTIONAL_4);
+    proto[i].AddParam(VariableDataType::INT, AssParameterClass::RELATIVE_TIME_START, OPTIONAL_3 | OPTIONAL_4);
+    proto[i].AddParam(VariableDataType::FLOAT, AssParameterClass::NORMAL, OPTIONAL_2 | OPTIONAL_4);
+    proto[i].AddParam(VariableDataType::BLOCK);
 }
 
-std::vector<std::string> tokenize(const std::string &text) {
-	std::vector<std::string> paramList;
-	paramList.reserve(6);
-
-	if (text.empty())
-		return paramList;
-
-	if (text[0] != '(') {
-		// There's just one parameter (because there's no parentheses)
-		// This means text is all our parameters
-		paramList.emplace_back(boost::trim_copy(text));
-		return paramList;
-	}
-
-	// Ok, so there are parentheses used here, so there may be more than one parameter
-	// Enter fullscale parsing!
-	size_t i = 0, textlen = text.size();
-	int parDepth = 1;
-	while (i < textlen && parDepth > 0) {
-		// Just skip until next ',' or ')', whichever comes first
-		// (Next ')' is achieved when parDepth == 0)
-		size_t start = ++i;
-		while (i < textlen && parDepth > 0) {
-			char c = text[i];
-			// parDepth 1 is where we start, and the tag-level we're interested in parsing on
-			if (c == ',' && parDepth == 1) break;
-			if (c == '(') parDepth++;
-			else if (c == ')') {
-				if (--parDepth == 0) {
-					// We just ate the parenthesis ending this parameter block
-					// Make sure it doesn't get included in the parameter text
-					break;
-				}
-			}
-			i++;
-		}
-		// i now points to the first character not member of this parameter
-		paramList.emplace_back(boost::trim_copy(text.substr(start, i - start)));
-	}
-
-	if (i+1 < textlen) {
-		// There's some additional garbage after the parentheses
-		// Just add it in for completeness
-		paramList.emplace_back(text.begin() + i + 1, text.end());
-	}
-	return paramList;
+std::vector<std::string> tokenize(const std::string &text)
+{
+    std::vector<std::string> paramList;
+    paramList.reserve(6);
+
+    if (text.empty())
+        return paramList;
+
+    if (text[0] != '(') {
+        // There's just one parameter (because there's no parentheses)
+        // This means text is all our parameters
+        paramList.emplace_back(boost::trim_copy(text));
+        return paramList;
+    }
+
+    // Ok, so there are parentheses used here, so there may be more than one parameter
+    // Enter fullscale parsing!
+    size_t i = 0, textlen = text.size();
+    int parDepth = 1;
+    while (i < textlen && parDepth > 0) {
+        // Just skip until next ',' or ')', whichever comes first
+        // (Next ')' is achieved when parDepth == 0)
+        size_t start = ++i;
+        while (i < textlen && parDepth > 0) {
+            char c = text[i];
+            // parDepth 1 is where we start, and the tag-level we're interested in parsing on
+            if (c == ',' && parDepth == 1) break;
+            if (c == '(') parDepth++;
+            else if (c == ')') {
+                if (--parDepth == 0) {
+                    // We just ate the parenthesis ending this parameter block
+                    // Make sure it doesn't get included in the parameter text
+                    break;
+                }
+            }
+            i++;
+        }
+        // i now points to the first character not member of this parameter
+        paramList.emplace_back(boost::trim_copy(text.substr(start, i - start)));
+    }
+
+    if (i + 1 < textlen) {
+        // There's some additional garbage after the parentheses
+        // Just add it in for completeness
+        paramList.emplace_back(text.begin() + i + 1, text.end());
+    }
+    return paramList;
 }
 
-void parse_parameters(AssOverrideTag *tag, const std::string &text, AssOverrideTagProto::iterator proto_it) {
-	tag->Clear();
+void parse_parameters(AssOverrideTag *tag, const std::string &text, AssOverrideTagProto::iterator proto_it)
+{
+    tag->Clear();
 
-	// Tokenize text, attempting to find all parameters
-	std::vector<std::string> paramList = tokenize(text);
-	size_t totalPars = paramList.size();
+    // Tokenize text, attempting to find all parameters
+    std::vector<std::string> paramList = tokenize(text);
+    size_t totalPars = paramList.size();
 
-	int parsFlag = 1 << (totalPars - 1); // Get optional parameters flag
-	// vector (i)clip is the second clip proto_ittype in the list
-	if ((tag->Name == "\\clip" || tag->Name == "\\iclip") && totalPars != 4) {
-		++proto_it;
-	}
+    int parsFlag = 1 << (totalPars - 1); // Get optional parameters flag
+    // vector (i)clip is the second clip proto_ittype in the list
+    if ((tag->Name == "\\clip" || tag->Name == "\\iclip") && totalPars != 4) {
+        ++proto_it;
+    }
 
-	unsigned curPar = 0;
-	for (auto& curproto : proto_it->params) {
-		// Create parameter
-		tag->Params.emplace_back(curproto.type, curproto.classification);
+    unsigned curPar = 0;
+    for (auto &curproto : proto_it->params) {
+        // Create parameter
+        tag->Params.emplace_back(curproto.type, curproto.classification);
 
-		// Check if it's optional and not present
-		if (!(curproto.optional & parsFlag) || curPar >= totalPars)
-			continue;
+        // Check if it's optional and not present
+        if (!(curproto.optional & parsFlag) || curPar >= totalPars)
+            continue;
 
-		tag->Params.back().Set(paramList[curPar++]);
-	}
+        tag->Params.back().Set(paramList[curPar++]);
+    }
 }
 
 }
 
 // From ass_dialogue.h
-void AssDialogueBlockOverride::ParseTags() {
-	Tags.clear();
-
-	int depth = 0;
-	size_t start = 0;
-	for (size_t i = 1; i < text.size(); ++i) {
-		if (depth > 0) {
-			if (text[i] == ')')
-				--depth;
-		}
-		else if (text[i] == '\\') {
-			Tags.emplace_back(text.substr(start, i - start));
-			start = i;
-		}
-		else if (text[i] == '(')
-			++depth;
-	}
-
-	if (!text.empty())
-		Tags.emplace_back(text.substr(start));
+void AssDialogueBlockOverride::ParseTags()
+{
+    Tags.clear();
+
+    int depth = 0;
+    size_t start = 0;
+    for (size_t i = 1; i < text.size(); ++i) {
+        if (depth > 0) {
+            if (text[i] == ')')
+                --depth;
+        }
+        else if (text[i] == '\\') {
+            Tags.emplace_back(text.substr(start, i - start));
+            start = i;
+        }
+        else if (text[i] == '(')
+            ++depth;
+    }
+
+    if (!text.empty())
+        Tags.emplace_back(text.substr(start));
 }
 
-void AssDialogueBlockOverride::AddTag(std::string const& tag) {
-	Tags.emplace_back(tag);
+void AssDialogueBlockOverride::AddTag(std::string const &tag)
+{
+    Tags.emplace_back(tag);
 }
 
-static std::string tag_str(AssOverrideTag const& t) { return t; }
-std::string AssDialogueBlockOverride::GetText() {
-	text = "{" + join(Tags | transformed(tag_str), std::string()) + "}";
-	return text;
+static std::string tag_str(AssOverrideTag const &t) { return t; }
+std::string AssDialogueBlockOverride::GetText()
+{
+    text = "{" + join(Tags | transformed(tag_str), std::string()) + "}";
+    return text;
 }
 
-void AssDialogueBlockOverride::ProcessParameters(ProcessParametersCallback callback, void *userData) {
-	for (auto& tag : Tags) {
-		for (auto& par : tag.Params) {
-			if (par.omitted) continue;
+void AssDialogueBlockOverride::ProcessParameters(ProcessParametersCallback callback, void *userData)
+{
+    for (auto &tag : Tags) {
+        for (auto &par : tag.Params) {
+            if (par.omitted) continue;
 
-			callback(tag.Name, &par, userData);
+            callback(tag.Name, &par, userData);
 
-			// Go recursive if it's a block parameter
-			if (par.GetType() == VariableDataType::BLOCK)
-				par.Get<AssDialogueBlockOverride*>()->ProcessParameters(callback, userData);
-		}
-	}
+            // Go recursive if it's a block parameter
+            if (par.GetType() == VariableDataType::BLOCK)
+                par.Get<AssDialogueBlockOverride *>()->ProcessParameters(callback, userData);
+        }
+    }
 }
 
-AssOverrideTag::AssOverrideTag(std::string const& text) {
-	SetText(text);
+AssOverrideTag::AssOverrideTag(std::string const &text)
+{
+    SetText(text);
 }
 
-void AssOverrideTag::Clear() {
-	Params.clear();
-	Params.reserve(6);
-	valid = false;
+void AssOverrideTag::Clear()
+{
+    Params.clear();
+    Params.reserve(6);
+    valid = false;
 }
 
-void AssOverrideTag::SetText(const std::string &text) {
-	load_protos();
-	for (auto cur = proto.begin(); cur != proto.end(); ++cur) {
-		if (boost::starts_with(text, cur->name)) {
-			Name = cur->name;
-			parse_parameters(this, text.substr(Name.size()), cur);
-			valid = true;
-			return;
-		}
-	}
-
-	// Junk tag
-	Name = text;
-	valid = false;
+void AssOverrideTag::SetText(const std::string &text)
+{
+    load_protos();
+    for (auto cur = proto.begin(); cur != proto.end(); ++cur) {
+        if (boost::starts_with(text, cur->name)) {
+            Name = cur->name;
+            parse_parameters(this, text.substr(Name.size()), cur);
+            valid = true;
+            return;
+        }
+    }
+
+    // Junk tag
+    Name = text;
+    valid = false;
 }
 
-static std::string param_str(AssOverrideParameter const& p) { return p.Get<std::string>(); }
-AssOverrideTag::operator std::string() const {
-	std::string result = Name;
+static std::string param_str(AssOverrideParameter const &p) { return p.Get<std::string>(); }
+AssOverrideTag::operator std::string() const
+{
+    std::string result = Name;
 
-	// Determine if it needs parentheses
-	bool parentheses = Params.size() > 1;
-	if (parentheses) result += "(";
+    // Determine if it needs parentheses
+    bool parentheses = Params.size() > 1;
+    if (parentheses) result += "(";
 
-	// Add parameters
-	result += join(Params
-		| filtered([](AssOverrideParameter const& p) { return !p.omitted; } )
-		| transformed(param_str),
-		",");
+    // Add parameters
+    result += join(Params
+    | filtered([](AssOverrideParameter const & p) { return !p.omitted; } )
+    | transformed(param_str),
+    ",");
 
-	if (parentheses) result += ")";
-	return result;
+    if (parentheses) result += ")";
+    return result;
 }
diff --git a/src/ass_override.h b/src/ass_override.h
index dfcd1f8937157cf14e64e896c997d8ec9df824d8..c48039335a799fa2c8d4de6f92893f3d6390c482 100644
--- a/src/ass_override.h
+++ b/src/ass_override.h
@@ -39,68 +39,68 @@ class AssDialogueBlockOverride;
 
 /// Type of parameter
 enum class AssParameterClass {
-	NORMAL,
-	ABSOLUTE_SIZE,
-	ABSOLUTE_POS_X,
-	ABSOLUTE_POS_Y,
-	RELATIVE_SIZE_X,
-	RELATIVE_SIZE_Y,
-	RELATIVE_TIME_START,
-	RELATIVE_TIME_END,
-	KARAOKE,
-	DRAWING,
-	ALPHA,
-	COLOR
+    NORMAL,
+    ABSOLUTE_SIZE,
+    ABSOLUTE_POS_X,
+    ABSOLUTE_POS_Y,
+    RELATIVE_SIZE_X,
+    RELATIVE_SIZE_Y,
+    RELATIVE_TIME_START,
+    RELATIVE_TIME_END,
+    KARAOKE,
+    DRAWING,
+    ALPHA,
+    COLOR
 };
 
 enum class VariableDataType {
-	INT,
-	FLOAT,
-	TEXT,
-	BOOL,
-	BLOCK
+    INT,
+    FLOAT,
+    TEXT,
+    BOOL,
+    BLOCK
 };
 
 /// A single parameter to an override tag
 class AssOverrideParameter {
-	std::string value;
-	mutable std::unique_ptr<AssDialogueBlockOverride> block;
-	VariableDataType type;
+    std::string value;
+    mutable std::unique_ptr<AssDialogueBlockOverride> block;
+    VariableDataType type;
 
 public:
-	AssOverrideParameter(VariableDataType type, AssParameterClass classification);
-	AssOverrideParameter(AssOverrideParameter&&) = default;
-	AssOverrideParameter& operator=(AssOverrideParameter&&) = default;
-	~AssOverrideParameter();
+    AssOverrideParameter(VariableDataType type, AssParameterClass classification);
+    AssOverrideParameter(AssOverrideParameter &&) = default;
+    AssOverrideParameter &operator=(AssOverrideParameter &&) = default;
+    ~AssOverrideParameter();
 
-	/// Type of parameter
-	AssParameterClass classification;
+    /// Type of parameter
+    AssParameterClass classification;
 
-	/// Is this parameter actually present?
-	bool omitted = true;
+    /// Is this parameter actually present?
+    bool omitted = true;
 
-	VariableDataType GetType() const { return type; }
-	template<class T> void Set(T param);
-	template<class T> T Get() const;
-	template<class T> T Get(T def) const {
-		return !omitted ? Get<T>() : def;
-	}
+    VariableDataType GetType() const { return type; }
+    template<class T> void Set(T param);
+    template<class T> T Get() const;
+    template<class T> T Get(T def) const {
+        return !omitted ? Get<T>() : def;
+    }
 };
 
 class AssOverrideTag {
-	bool valid = false;
+    bool valid = false;
 
 public:
-	AssOverrideTag() = default;
-	AssOverrideTag(std::string const& text);
-	AssOverrideTag(AssOverrideTag&&) = default;
-	AssOverrideTag& operator=(AssOverrideTag&&) = default;
+    AssOverrideTag() = default;
+    AssOverrideTag(std::string const &text);
+    AssOverrideTag(AssOverrideTag &&) = default;
+    AssOverrideTag &operator=(AssOverrideTag &&) = default;
 
-	std::string Name;
-	std::vector<AssOverrideParameter> Params;
+    std::string Name;
+    std::vector<AssOverrideParameter> Params;
 
-	bool IsValid() const { return valid; }
-	void Clear();
-	void SetText(const std::string &text);
-	operator std::string() const;
+    bool IsValid() const { return valid; }
+    void Clear();
+    void SetText(const std::string &text);
+    operator std::string() const;
 };
diff --git a/src/ass_parser.cpp b/src/ass_parser.cpp
index f55fc3567ff023f3d974836211b28b3a85ee4b46..070401559d0390af99ff7bf61256e7245d6e3974 100644
--- a/src/ass_parser.cpp
+++ b/src/ass_parser.cpp
@@ -36,232 +36,243 @@
 #include <unordered_map>
 
 class AssParser::HeaderToProperty {
-	using field = boost::variant<
-		std::string ProjectProperties::*,
-		int ProjectProperties::*,
-		double ProjectProperties::*
-	>;
-	std::unordered_map<std::string, field> fields;
+    using field = boost::variant <
+                  std::string ProjectProperties::*,
+                  int ProjectProperties::*,
+                  double ProjectProperties::*
+                  >;
+    std::unordered_map<std::string, field> fields;
 
 public:
-	HeaderToProperty()
-	: fields({
-		{"Automation Scripts", &ProjectProperties::automation_scripts},
-		{"Export Filters", &ProjectProperties::export_filters},
-		{"Export Encoding", &ProjectProperties::export_encoding},
-		{"Last Style Storage", &ProjectProperties::style_storage},
-		{"Audio URI", &ProjectProperties::audio_file},
-		{"Audio File", &ProjectProperties::audio_file},
-		{"Video File", &ProjectProperties::video_file},
-		{"Timecodes File", &ProjectProperties::timecodes_file},
-		{"Keyframes File", &ProjectProperties::keyframes_file},
-		{"Video Zoom Percent", &ProjectProperties::video_zoom},
-		{"Scroll Position", &ProjectProperties::scroll_position},
-		{"Active Line", &ProjectProperties::active_row},
-		{"Video Position", &ProjectProperties::video_position},
-		{"Video AR Mode", &ProjectProperties::ar_mode},
-		{"Video AR Value", &ProjectProperties::ar_value},
-		{"Aegisub Video Zoom Percent", &ProjectProperties::video_zoom},
-		{"Aegisub Scroll Position", &ProjectProperties::scroll_position},
-		{"Aegisub Active Line", &ProjectProperties::active_row},
-		{"Aegisub Video Position", &ProjectProperties::video_position}
-	})
-	{
-	}
-
-	bool ProcessProperty(AssFile *target, std::string const& key, std::string const& value) {
-		auto it = fields.find(key);
-		if (it != end(fields)) {
-			using namespace agi::util;
-			struct {
-				using result_type = void;
-				ProjectProperties &obj;
-				std::string const& value;
-				void operator()(std::string ProjectProperties::*f) const { obj.*f = value; }
-				void operator()(int ProjectProperties::*f)         const { try_parse(value, &(obj.*f)); }
-				void operator()(double ProjectProperties::*f)      const { try_parse(value, &(obj.*f)); }
-			} visitor {target->Properties, value};
-			boost::apply_visitor(visitor, it->second);
-			return true;
-		}
-
-		if (boost::starts_with(key, "Automation Settings ")) {
-			target->Properties.automation_settings[key.substr(strlen("Automation Settings"))] = value;
-			return true;
-		}
-
-		return false;
-	}
+    HeaderToProperty()
+        : fields({
+        {"Automation Scripts", &ProjectProperties::automation_scripts},
+        {"Export Filters", &ProjectProperties::export_filters},
+        {"Export Encoding", &ProjectProperties::export_encoding},
+        {"Last Style Storage", &ProjectProperties::style_storage},
+        {"Audio URI", &ProjectProperties::audio_file},
+        {"Audio File", &ProjectProperties::audio_file},
+        {"Video File", &ProjectProperties::video_file},
+        {"Timecodes File", &ProjectProperties::timecodes_file},
+        {"Keyframes File", &ProjectProperties::keyframes_file},
+        {"Video Zoom Percent", &ProjectProperties::video_zoom},
+        {"Scroll Position", &ProjectProperties::scroll_position},
+        {"Active Line", &ProjectProperties::active_row},
+        {"Video Position", &ProjectProperties::video_position},
+        {"Video AR Mode", &ProjectProperties::ar_mode},
+        {"Video AR Value", &ProjectProperties::ar_value},
+        {"Aegisub Video Zoom Percent", &ProjectProperties::video_zoom},
+        {"Aegisub Scroll Position", &ProjectProperties::scroll_position},
+        {"Aegisub Active Line", &ProjectProperties::active_row},
+        {"Aegisub Video Position", &ProjectProperties::video_position}
+    }) {
+    }
+
+    bool ProcessProperty(AssFile *target, std::string const &key, std::string const &value) {
+        auto it = fields.find(key);
+        if (it != end(fields)) {
+            using namespace agi::util;
+            struct {
+                using result_type = void;
+                ProjectProperties &obj;
+                std::string const &value;
+                void operator()(std::string ProjectProperties::*f) const { obj.*f = value; }
+                void operator()(int ProjectProperties::*f)         const { try_parse(value, &(obj.*f)); }
+                void operator()(double ProjectProperties::*f)      const { try_parse(value, &(obj.*f)); }
+            } visitor {target->Properties, value};
+            boost::apply_visitor(visitor, it->second);
+            return true;
+        }
+
+        if (boost::starts_with(key, "Automation Settings ")) {
+            target->Properties.automation_settings[key.substr(strlen("Automation Settings"))] = value;
+            return true;
+        }
+
+        return false;
+    }
 };
 
 AssParser::AssParser(AssFile *target, int version)
-: property_handler(agi::make_unique<HeaderToProperty>())
-, target(target)
-, version(version)
-, state(&AssParser::ParseScriptInfoLine)
+    : property_handler(agi::make_unique<HeaderToProperty>())
+    , target(target)
+    , version(version)
+    , state(&AssParser::ParseScriptInfoLine)
 {
 }
 
-AssParser::~AssParser() {
+AssParser::~AssParser()
+{
 }
 
-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) {
-		if (byte < 33 || byte >= 97) {
-			valid_data = false;
-			break;
-		}
-	}
-
-	// Data is over, add attachment to the file
-	if (!valid_data || is_filename) {
-		target->Attachments.push_back(*attach.release());
-		AddLine(data);
-	}
-	else {
-		attach->AddData(data);
-
-		// Done building
-		if (data.size() < 80)
-			target->Attachments.push_back(*attach.release());
-	}
+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) {
+        if (byte < 33 || byte >= 97) {
+            valid_data = false;
+            break;
+        }
+    }
+
+    // Data is over, add attachment to the file
+    if (!valid_data || is_filename) {
+        target->Attachments.push_back(*attach.release());
+        AddLine(data);
+    }
+    else {
+        attach->AddData(data);
+
+        // Done building
+        if (data.size() < 80)
+            target->Attachments.push_back(*attach.release());
+    }
 }
 
-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 (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");
-	}
-
-	// Nothing actually supports the Collisions property and malformed values
-	// crash VSFilter, so just remove it entirely
-	if (boost::starts_with(data, "Collisions:"))
-		return;
-
-	size_t pos = data.find(':');
-	if (pos == data.npos) return;
-
-	auto key = data.substr(0, pos);
-	auto value = data.substr(pos + 1);
-	boost::trim_left(value);
-
-	if (!property_handler->ProcessProperty(target, key, value))
-		target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
+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 (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");
+    }
+
+    // Nothing actually supports the Collisions property and malformed values
+    // crash VSFilter, so just remove it entirely
+    if (boost::starts_with(data, "Collisions:"))
+        return;
+
+    size_t pos = data.find(':');
+    if (pos == data.npos) return;
+
+    auto key = data.substr(0, pos);
+    auto value = data.substr(pos + 1);
+    boost::trim_left(value);
+
+    if (!property_handler->ProcessProperty(target, key, value))
+        target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
 }
 
-void AssParser::ParseMetadataLine(std::string const& data) {
-	size_t pos = data.find(':');
-	if (pos == data.npos) return;
+void AssParser::ParseMetadataLine(std::string const &data)
+{
+    size_t pos = data.find(':');
+    if (pos == data.npos) return;
 
-	auto key = data.substr(0, pos);
-	auto value = data.substr(pos + 1);
-	boost::trim_left(value);
+    auto key = data.substr(0, pos);
+    auto value = data.substr(pos + 1);
+    boost::trim_left(value);
 
-	property_handler->ProcessProperty(target, key, value);
+    property_handler->ProcessProperty(target, key, value);
 }
 
-void AssParser::ParseEventLine(std::string const& data) {
-	if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
-		target->Events.push_back(*new AssDialogue(data));
+void AssParser::ParseEventLine(std::string const &data)
+{
+    if (boost::starts_with(data, "Dialogue:") || boost::starts_with(data, "Comment:"))
+        target->Events.push_back(*new AssDialogue(data));
 }
 
-void AssParser::ParseStyleLine(std::string const& data) {
-	if (boost::starts_with(data, "Style:"))
-		target->Styles.push_back(*new AssStyle(data, version));
+void AssParser::ParseStyleLine(std::string const &data)
+{
+    if (boost::starts_with(data, "Style:"))
+        target->Styles.push_back(*new AssStyle(data, version));
 }
 
-void AssParser::ParseFontLine(std::string const& data) {
-	if (boost::starts_with(data, "fontname: "))
-		attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::FONT);
+void AssParser::ParseFontLine(std::string const &data)
+{
+    if (boost::starts_with(data, "fontname: "))
+        attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::FONT);
 }
 
-void AssParser::ParseGraphicsLine(std::string const& data) {
-	if (boost::starts_with(data, "filename: "))
-		attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::GRAPHIC);
+void AssParser::ParseGraphicsLine(std::string const &data)
+{
+    if (boost::starts_with(data, "filename: "))
+        attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::GRAPHIC);
 }
 
-void AssParser::ParseExtradataLine(std::string const &data) {
-	static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
-	boost::match_results<std::string::const_iterator> mr;
-
-	if (boost::regex_match(data, mr, matcher)) {
-		auto id = boost::lexical_cast<uint32_t>(mr.str(1));
-		auto key = inline_string_decode(mr.str(2));
-		auto valuetype = mr.str(3);
-		auto value = mr.str(4);
-		if (valuetype == "e") {
-			// escaped/inline_string encoded
-			value = inline_string_decode(value);
-		} else if (valuetype == "u") {
-			// ass uuencoded
-			auto valuedata = agi::ass::UUDecode(value.c_str(), value.c_str() + value.size());
-			value = std::string(valuedata.begin(), valuedata.end());
-		} else {
-			// unknown, error?
-			value = "";
-		}
-
-		// ensure next_extradata_id is always at least 1 more than the largest existing id
-		target->next_extradata_id = std::max(id+1, target->next_extradata_id);
-		target->Extradata.push_back(ExtradataEntry{id, std::move(key), std::move(value)});
-	}
+void AssParser::ParseExtradataLine(std::string const &data)
+{
+    static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
+    boost::match_results<std::string::const_iterator> mr;
+
+    if (boost::regex_match(data, mr, matcher)) {
+        auto id = boost::lexical_cast<uint32_t>(mr.str(1));
+        auto key = inline_string_decode(mr.str(2));
+        auto valuetype = mr.str(3);
+        auto value = mr.str(4);
+        if (valuetype == "e") {
+            // escaped/inline_string encoded
+            value = inline_string_decode(value);
+        }
+        else if (valuetype == "u") {
+            // ass uuencoded
+            auto valuedata = agi::ass::UUDecode(value.c_str(), value.c_str() + value.size());
+            value = std::string(valuedata.begin(), valuedata.end());
+        }
+        else {
+            // unknown, error?
+            value = "";
+        }
+
+        // ensure next_extradata_id is always at least 1 more than the largest existing id
+        target->next_extradata_id = std::max(id + 1, target->next_extradata_id);
+        target->Extradata.push_back(ExtradataEntry{id, std::move(key), std::move(value)});
+    }
 }
 
-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
-	if (attach.get()) {
-		ParseAttachmentLine(data);
-		return;
-	}
-
-	if (data.empty()) return;
-
-	// Section header
-	if (data[0] == '[' && data.back() == ']') {
-		// Ugly hacks to allow intermixed v4 and v4+ style sections
-		const std::string low = boost::to_lower_copy(data);
-		if (low == "[v4 styles]") {
-			version = 0;
-			state = &AssParser::ParseStyleLine;
-		}
-		else if (low == "[v4+ styles]") {
-			version = 1;
-			state = &AssParser::ParseStyleLine;
-		}
-		else if (low == "[events]")
-			state = &AssParser::ParseEventLine;
-		else if (low == "[script info]")
-			state = &AssParser::ParseScriptInfoLine;
-		else if (low == "[aegisub project garbage]")
-			state = &AssParser::ParseMetadataLine;
-		else if (low == "[aegisub extradata]")
-			state = &AssParser::ParseExtradataLine;
-		else if (low == "[graphics]")
-			state = &AssParser::ParseGraphicsLine;
-		else if (low == "[fonts]")
-			state = &AssParser::ParseFontLine;
-		else
-			state = &AssParser::UnknownLine;
-		return;
-	}
-
-	(this->*state)(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
+    if (attach.get()) {
+        ParseAttachmentLine(data);
+        return;
+    }
+
+    if (data.empty()) return;
+
+    // Section header
+    if (data[0] == '[' && data.back() == ']') {
+        // Ugly hacks to allow intermixed v4 and v4+ style sections
+        const std::string low = boost::to_lower_copy(data);
+        if (low == "[v4 styles]") {
+            version = 0;
+            state = &AssParser::ParseStyleLine;
+        }
+        else if (low == "[v4+ styles]") {
+            version = 1;
+            state = &AssParser::ParseStyleLine;
+        }
+        else if (low == "[events]")
+            state = &AssParser::ParseEventLine;
+        else if (low == "[script info]")
+            state = &AssParser::ParseScriptInfoLine;
+        else if (low == "[aegisub project garbage]")
+            state = &AssParser::ParseMetadataLine;
+        else if (low == "[aegisub extradata]")
+            state = &AssParser::ParseExtradataLine;
+        else if (low == "[graphics]")
+            state = &AssParser::ParseGraphicsLine;
+        else if (low == "[fonts]")
+            state = &AssParser::ParseFontLine;
+        else
+            state = &AssParser::UnknownLine;
+        return;
+    }
+
+    (this->*state)(data);
 }
diff --git a/src/ass_parser.h b/src/ass_parser.h
index 9cf6f50173f7dda06236b86a2292c1471d9510f2..7581d52d6f1046e910ced0c191d57c2b1b2092b5 100644
--- a/src/ass_parser.h
+++ b/src/ass_parser.h
@@ -18,26 +18,26 @@ class AssAttachment;
 class AssFile;
 
 class AssParser {
-	class HeaderToProperty;
-	std::unique_ptr<HeaderToProperty> property_handler;
+    class HeaderToProperty;
+    std::unique_ptr<HeaderToProperty> property_handler;
 
-	AssFile *target;
-	int version;
-	std::unique_ptr<AssAttachment> attach;
-	void (AssParser::*state)(std::string const&);
+    AssFile *target;
+    int version;
+    std::unique_ptr<AssAttachment> attach;
+    void (AssParser::*state)(std::string 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 ParseMetadataLine(std::string const& data);
-	void ParseFontLine(std::string const& data);
-	void ParseGraphicsLine(std::string const& data);
-	void ParseExtradataLine(std::string const &data);
-	void UnknownLine(std::string 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 ParseMetadataLine(std::string const &data);
+    void ParseFontLine(std::string const &data);
+    void ParseGraphicsLine(std::string const &data);
+    void ParseExtradataLine(std::string const &data);
+    void UnknownLine(std::string const &) { }
 public:
-	AssParser(AssFile *target, int version);
-	~AssParser();
+    AssParser(AssFile *target, int version);
+    ~AssParser();
 
-	void AddLine(std::string const& data);
+    void AddLine(std::string const &data);
 };
diff --git a/src/ass_style.cpp b/src/ass_style.cpp
index cc75c2c8162196c82259be7eb4cc4ca8f1880d57..81edc722ebb2ffadbdbd92a4d92da697baaecec4 100644
--- a/src/ass_style.cpp
+++ b/src/ass_style.cpp
@@ -44,201 +44,207 @@
 #include <boost/lexical_cast.hpp>
 #include <wx/intl.h>
 
-AssStyle::AssStyle() {
-	std::fill(Margin.begin(), Margin.end(), 30);
+AssStyle::AssStyle()
+{
+    std::fill(Margin.begin(), Margin.end(), 30);
 
-	UpdateData();
+    UpdateData();
 }
 
 AssEntryGroup AssStyle::Group() const { return AssEntryGroup::STYLE; }
 
 namespace {
 class parser {
-	agi::split_iterator<agi::StringRange::const_iterator> pos;
+    agi::split_iterator<agi::StringRange::const_iterator> pos;
 
-	std::string next_tok() {
-		if (pos.eof())
-			throw SubtitleFormatParseError("Malformed style: not enough fields");
-		return agi::str(trim_copy(*pos++));
-	}
+    std::string next_tok() {
+        if (pos.eof())
+            throw SubtitleFormatParseError("Malformed style: not enough fields");
+        return agi::str(trim_copy(*pos++));
+    }
 
 public:
-	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 (!pos.eof())
-			throw SubtitleFormatParseError("Malformed style: too many fields");
-	}
-
-	std::string next_str() { return next_tok(); }
-	agi::Color next_color() { return next_tok(); }
-
-	int next_int() {
-		try {
-			return boost::lexical_cast<int>(next_tok());
-		}
-		catch (boost::bad_lexical_cast const&) {
-			throw SubtitleFormatParseError("Malformed style: bad int field");
-		}
-	}
-
-	double next_double() {
-		try {
-			return boost::lexical_cast<double>(next_tok());
-		}
-		catch (boost::bad_lexical_cast const&) {
-			throw SubtitleFormatParseError("Malformed style: bad double field");
-		}
-	}
-
-	void skip_token() {
-		if (!pos.eof())
-			++pos;
-	}
+    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 (!pos.eof())
+            throw SubtitleFormatParseError("Malformed style: too many fields");
+    }
+
+    std::string next_str() { return next_tok(); }
+    agi::Color next_color() { return next_tok(); }
+
+    int next_int() {
+        try {
+            return boost::lexical_cast<int>(next_tok());
+        }
+        catch (boost::bad_lexical_cast const &) {
+            throw SubtitleFormatParseError("Malformed style: bad int field");
+        }
+    }
+
+    double next_double() {
+        try {
+            return boost::lexical_cast<double>(next_tok());
+        }
+        catch (boost::bad_lexical_cast const &) {
+            throw SubtitleFormatParseError("Malformed style: bad double field");
+        }
+    }
+
+    void skip_token() {
+        if (!pos.eof())
+            ++pos;
+    }
 };
 }
 
-AssStyle::AssStyle(std::string const& str, int version) {
-	parser p(str);
-
-	name = p.next_str();
-	font = p.next_str();
-	fontsize = p.next_double();
-
-	if (version != 0) {
-		primary = p.next_color();
-		secondary = p.next_color();
-		outline = p.next_color();
-		shadow = p.next_color();
-	}
-	else {
-		primary = p.next_color();
-		secondary = p.next_color();
-
-		// Skip tertiary color
-		p.skip_token();
-
-		// Read shadow/outline color
-		outline = p.next_color();
-		shadow = outline;
-	}
-
-	bold = !!p.next_int();
-	italic = !!p.next_int();
-
-	if (version != 0) {
-		underline = !!p.next_int();
-		strikeout = !!p.next_int();
-
-		scalex = p.next_double();
-		scaley = p.next_double();
-		spacing = p.next_double();
-		angle = p.next_double();
-	}
-	else {
-		// SSA defaults
-		underline = false;
-		strikeout = false;
-
-		scalex = 100;
-		scaley = 100;
-		spacing = 0;
-		angle = 0.0;
-	}
-
-	borderstyle = p.next_int();
-	outline_w = p.next_double();
-	shadow_w = p.next_double();
-	alignment = p.next_int();
-
-	if (version == 0)
-		alignment = SsaToAss(alignment);
-
-	Margin[0] = mid(0, p.next_int(), 9999);
-	Margin[1] = mid(0, p.next_int(), 9999);
-	Margin[2] = mid(0, p.next_int(), 9999);
-
-	// Skip alpha level
-	if (version == 0)
-		p.skip_token();
-
-	encoding = p.next_int();
-
-	p.check_done();
-
-	UpdateData();
+AssStyle::AssStyle(std::string const &str, int version)
+{
+    parser p(str);
+
+    name = p.next_str();
+    font = p.next_str();
+    fontsize = p.next_double();
+
+    if (version != 0) {
+        primary = p.next_color();
+        secondary = p.next_color();
+        outline = p.next_color();
+        shadow = p.next_color();
+    }
+    else {
+        primary = p.next_color();
+        secondary = p.next_color();
+
+        // Skip tertiary color
+        p.skip_token();
+
+        // Read shadow/outline color
+        outline = p.next_color();
+        shadow = outline;
+    }
+
+    bold = !!p.next_int();
+    italic = !!p.next_int();
+
+    if (version != 0) {
+        underline = !!p.next_int();
+        strikeout = !!p.next_int();
+
+        scalex = p.next_double();
+        scaley = p.next_double();
+        spacing = p.next_double();
+        angle = p.next_double();
+    }
+    else {
+        // SSA defaults
+        underline = false;
+        strikeout = false;
+
+        scalex = 100;
+        scaley = 100;
+        spacing = 0;
+        angle = 0.0;
+    }
+
+    borderstyle = p.next_int();
+    outline_w = p.next_double();
+    shadow_w = p.next_double();
+    alignment = p.next_int();
+
+    if (version == 0)
+        alignment = SsaToAss(alignment);
+
+    Margin[0] = mid(0, p.next_int(), 9999);
+    Margin[1] = mid(0, p.next_int(), 9999);
+    Margin[2] = mid(0, p.next_int(), 9999);
+
+    // Skip alpha level
+    if (version == 0)
+        p.skip_token();
+
+    encoding = p.next_int();
+
+    p.check_done();
+
+    UpdateData();
 }
 
-void AssStyle::UpdateData() {
-	replace(name.begin(), name.end(), ',', ';');
-	replace(font.begin(), font.end(), ',', ';');
-
-	data = agi::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);
+void AssStyle::UpdateData()
+{
+    replace(name.begin(), name.end(), ',', ';');
+    replace(font.begin(), font.end(), ',', ';');
+
+    data = agi::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);
 }
 
-void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
-	encodingStrings.Clear();
-	encodingStrings.Add(wxString("0 - ") + _("ANSI"));
-	encodingStrings.Add(wxString("1 - ") + _("Default"));
-	encodingStrings.Add(wxString("2 - ") + _("Symbol"));
-	encodingStrings.Add(wxString("77 - ") + _("Mac"));
-	encodingStrings.Add(wxString("128 - ") + _("Shift_JIS"));
-	encodingStrings.Add(wxString("129 - ") + _("Hangeul"));
-	encodingStrings.Add(wxString("130 - ") + _("Johab"));
-	encodingStrings.Add(wxString("134 - ") + _("GB2312"));
-	encodingStrings.Add(wxString("136 - ") + _("Chinese BIG5"));
-	encodingStrings.Add(wxString("161 - ") + _("Greek"));
-	encodingStrings.Add(wxString("162 - ") + _("Turkish"));
-	encodingStrings.Add(wxString("163 - ") + _("Vietnamese"));
-	encodingStrings.Add(wxString("177 - ") + _("Hebrew"));
-	encodingStrings.Add(wxString("178 - ") + _("Arabic"));
-	encodingStrings.Add(wxString("186 - ") + _("Baltic"));
-	encodingStrings.Add(wxString("204 - ") + _("Russian"));
-	encodingStrings.Add(wxString("222 - ") + _("Thai"));
-	encodingStrings.Add(wxString("238 - ") + _("East European"));
-	encodingStrings.Add(wxString("255 - ") + _("OEM"));
+void AssStyle::GetEncodings(wxArrayString &encodingStrings)
+{
+    encodingStrings.Clear();
+    encodingStrings.Add(wxString("0 - ") + _("ANSI"));
+    encodingStrings.Add(wxString("1 - ") + _("Default"));
+    encodingStrings.Add(wxString("2 - ") + _("Symbol"));
+    encodingStrings.Add(wxString("77 - ") + _("Mac"));
+    encodingStrings.Add(wxString("128 - ") + _("Shift_JIS"));
+    encodingStrings.Add(wxString("129 - ") + _("Hangeul"));
+    encodingStrings.Add(wxString("130 - ") + _("Johab"));
+    encodingStrings.Add(wxString("134 - ") + _("GB2312"));
+    encodingStrings.Add(wxString("136 - ") + _("Chinese BIG5"));
+    encodingStrings.Add(wxString("161 - ") + _("Greek"));
+    encodingStrings.Add(wxString("162 - ") + _("Turkish"));
+    encodingStrings.Add(wxString("163 - ") + _("Vietnamese"));
+    encodingStrings.Add(wxString("177 - ") + _("Hebrew"));
+    encodingStrings.Add(wxString("178 - ") + _("Arabic"));
+    encodingStrings.Add(wxString("186 - ") + _("Baltic"));
+    encodingStrings.Add(wxString("204 - ") + _("Russian"));
+    encodingStrings.Add(wxString("222 - ") + _("Thai"));
+    encodingStrings.Add(wxString("238 - ") + _("East European"));
+    encodingStrings.Add(wxString("255 - ") + _("OEM"));
 }
 
-int AssStyle::AssToSsa(int ass_align) {
-	switch (ass_align) {
-		case 1:  return 1;
-		case 2:  return 2;
-		case 3:  return 3;
-		case 4:  return 9;
-		case 5:  return 10;
-		case 6:  return 11;
-		case 7:  return 5;
-		case 8:  return 6;
-		case 9:  return 7;
-		default: return 2;
-	}
+int AssStyle::AssToSsa(int ass_align)
+{
+    switch (ass_align) {
+    case 1:  return 1;
+    case 2:  return 2;
+    case 3:  return 3;
+    case 4:  return 9;
+    case 5:  return 10;
+    case 6:  return 11;
+    case 7:  return 5;
+    case 8:  return 6;
+    case 9:  return 7;
+    default: return 2;
+    }
 }
 
-int AssStyle::SsaToAss(int ssa_align) {
-	switch(ssa_align) {
-		case 1:  return 1;
-		case 2:  return 2;
-		case 3:  return 3;
-		case 5:  return 7;
-		case 6:  return 8;
-		case 7:  return 9;
-		case 9:  return 4;
-		case 10: return 5;
-		case 11: return 6;
-		default: return 2;
-	}
+int AssStyle::SsaToAss(int ssa_align)
+{
+    switch (ssa_align) {
+    case 1:  return 1;
+    case 2:  return 2;
+    case 3:  return 3;
+    case 5:  return 7;
+    case 6:  return 8;
+    case 7:  return 9;
+    case 9:  return 4;
+    case 10: return 5;
+    case 11: return 6;
+    default: return 2;
+    }
 }
diff --git a/src/ass_style.h b/src/ass_style.h
index c5181c05712dcef3b8437f174b29f63c1dab5b08..8a411c1be0bbfd09d9eb1c2ff755b7d22fef47e6 100644
--- a/src/ass_style.h
+++ b/src/ass_style.h
@@ -36,48 +36,48 @@
 class wxArrayString;
 
 class AssStyle final : public AssEntry, public AssEntryListHook {
-	std::string data;
+    std::string data;
 
 public:
-	std::string name = "Default"; ///< Name of the style; must be case-insensitively unique within a file despite being case-sensitive
-	std::string font = "Amaranth";   ///< Font face name
-	double fontsize = 60.;        ///< Font size
+    std::string name = "Default"; ///< Name of the style; must be case-insensitively unique within a file despite being case-sensitive
+    std::string font = "Amaranth";   ///< Font face name
+    double fontsize = 60.;        ///< Font size
 
-	agi::Color primary{ 255, 132, 0 }; ///< Default text color
-	agi::Color secondary{ 255, 255, 255 };   ///< Text color for not-yet-reached karaoke syllables
-	agi::Color outline{ 0, 0, 0 };       ///< Outline color
-	agi::Color shadow{ 0, 0, 0 };        ///< Shadow color
+    agi::Color primary{ 255, 132, 0 }; ///< Default text color
+    agi::Color secondary{ 255, 255, 255 };   ///< Text color for not-yet-reached karaoke syllables
+    agi::Color outline{ 0, 0, 0 };       ///< Outline color
+    agi::Color shadow{ 0, 0, 0 };        ///< Shadow color
 
-	bool bold = false;
-	bool italic = false;
-	bool underline = false;
-	bool strikeout = false;
+    bool bold = false;
+    bool italic = false;
+    bool underline = false;
+    bool strikeout = false;
 
-	double scalex = 100.;      ///< Font x scale with 100 = 100%
-	double scaley = 100.;      ///< Font y scale with 100 = 100%
-	double spacing = 0.;       ///< Additional spacing between characters in pixels
-	double angle = 0.;         ///< Counterclockwise z rotation in degrees
-	int borderstyle = 1;       ///< 1: Normal; 3: Opaque box; others are unused in Aegisub
-	double outline_w = 2.;     ///< Outline width in pixels
-	double shadow_w = 2.;      ///< Shadow distance in pixels
-	int alignment = 8;         ///< \an-style line alignment
-	std::array<int, 3> Margin; ///< Left / Right / Vertical
-	int encoding = 1;          ///< ASS font encoding needed for some non-unicode fonts
+    double scalex = 100.;      ///< Font x scale with 100 = 100%
+    double scaley = 100.;      ///< Font y scale with 100 = 100%
+    double spacing = 0.;       ///< Additional spacing between characters in pixels
+    double angle = 0.;         ///< Counterclockwise z rotation in degrees
+    int borderstyle = 1;       ///< 1: Normal; 3: Opaque box; others are unused in Aegisub
+    double outline_w = 2.;     ///< Outline width in pixels
+    double shadow_w = 2.;      ///< Shadow distance in pixels
+    int alignment = 8;         ///< \an-style line alignment
+    std::array<int, 3> Margin; ///< Left / Right / Vertical
+    int encoding = 1;          ///< ASS font encoding needed for some non-unicode fonts
 
-	/// Update the raw line data after one or more of the public members have been changed
-	void UpdateData();
+    /// Update the raw line data after one or more of the public members have been changed
+    void UpdateData();
 
-	/// @brief Get a list of valid ASS font encodings
-	static void GetEncodings(wxArrayString &encodingStrings);
+    /// @brief Get a list of valid ASS font encodings
+    static void GetEncodings(wxArrayString &encodingStrings);
 
-	AssStyle();
-	AssStyle(std::string const& data, int version=1);
+    AssStyle();
+    AssStyle(std::string const &data, int version = 1);
 
-	std::string const& GetEntryData() const { return data; }
-	AssEntryGroup Group() const override;
+    std::string const &GetEntryData() const { return data; }
+    AssEntryGroup Group() const override;
 
-	/// Convert an ASS alignment to the equivalent SSA alignment
-	static int AssToSsa(int ass_align);
-	/// Convert a SSA  alignment to the equivalent ASS alignment
-	static int SsaToAss(int ssa_align);
+    /// Convert an ASS alignment to the equivalent SSA alignment
+    static int AssToSsa(int ass_align);
+    /// Convert a SSA  alignment to the equivalent ASS alignment
+    static int SsaToAss(int ssa_align);
 };
diff --git a/src/ass_style_storage.cpp b/src/ass_style_storage.cpp
index d6dc6d8354d5cf35d39d0023de01546de0f9a581..b8f660a5e523dc13f884844624e115cd8b774ee9 100644
--- a/src/ass_style_storage.cpp
+++ b/src/ass_style_storage.cpp
@@ -50,78 +50,88 @@ AssStyleStorage::~AssStyleStorage() { }
 void AssStyleStorage::clear() { style.clear(); }
 void AssStyleStorage::push_back(std::unique_ptr<AssStyle> new_style) { style.emplace_back(std::move(new_style)); }
 
-void AssStyleStorage::Save() const {
-	if (file.empty()) return;
+void AssStyleStorage::Save() const
+{
+    if (file.empty()) return;
 
-	agi::fs::CreateDirectory(file.parent_path());
+    agi::fs::CreateDirectory(file.parent_path());
 
-	agi::io::Save out(file);
-	out.Get() << "\xEF\xBB\xBF";
+    agi::io::Save out(file);
+    out.Get() << "\xEF\xBB\xBF";
 
-	for (auto const& cur : style)
-		out.Get() << cur->GetEntryData() << std::endl;
+    for (auto const &cur : style)
+        out.Get() << cur->GetEntryData() << std::endl;
 }
 
-void AssStyleStorage::Load(agi::fs::path const& filename) {
-	file = filename;
-	clear();
-
-	try {
-		auto in = agi::io::Open(file);
-		for (auto const& line : agi::line_iterator<std::string>(*in)) {
-			try {
-				style.emplace_back(agi::make_unique<AssStyle>(line));
-			} catch(...) {
-				/* just ignore invalid lines for now */
-			}
-		}
-	}
-	catch (agi::fs::FileNotAccessible const&) {
-		// Just treat a missing file as an empty file
-	}
+void AssStyleStorage::Load(agi::fs::path const &filename)
+{
+    file = filename;
+    clear();
+
+    try {
+        auto in = agi::io::Open(file);
+        for (auto const &line : agi::line_iterator<std::string>(*in)) {
+            try {
+                style.emplace_back(agi::make_unique<AssStyle>(line));
+            }
+            catch (...) {
+                /* just ignore invalid lines for now */
+            }
+        }
+    }
+    catch (agi::fs::FileNotAccessible const &) {
+        // Just treat a missing file as an empty file
+    }
 }
 
-void AssStyleStorage::LoadCatalog(std::string const& catalogname) {
-	auto filename = config::path->Decode("?user/catalog/" + catalogname + ".sty");
-	Load(filename);
+void AssStyleStorage::LoadCatalog(std::string const &catalogname)
+{
+    auto filename = config::path->Decode("?user/catalog/" + catalogname + ".sty");
+    Load(filename);
 }
 
-void AssStyleStorage::Delete(int idx) {
-	style.erase(style.begin() + idx);
+void AssStyleStorage::Delete(int idx)
+{
+    style.erase(style.begin() + idx);
 }
 
-std::vector<std::string> AssStyleStorage::GetNames() {
-	std::vector<std::string> names;
-	for (auto const& cur : style)
-		names.emplace_back(cur->name);
-	return names;
+std::vector<std::string> AssStyleStorage::GetNames()
+{
+    std::vector<std::string> names;
+    for (auto const &cur : style)
+        names.emplace_back(cur->name);
+    return names;
 }
 
-AssStyle *AssStyleStorage::GetStyle(std::string const& name) {
-	for (auto& cur : style) {
-		if (boost::iequals(cur->name, name))
-			return cur.get();
-	}
-	return nullptr;
+AssStyle *AssStyleStorage::GetStyle(std::string const &name)
+{
+    for (auto &cur : style) {
+        if (boost::iequals(cur->name, name))
+            return cur.get();
+    }
+    return nullptr;
 }
 
-std::vector<std::string> AssStyleStorage::GetCatalogs() {
-	std::vector<std::string> catalogs;
-	for (auto const& file : agi::fs::DirectoryIterator(config::path->Decode("?user/catalog/"), "*.sty"))
-		catalogs.push_back(agi::fs::path(file).stem().string());
-	return catalogs;
+std::vector<std::string> AssStyleStorage::GetCatalogs()
+{
+    std::vector<std::string> catalogs;
+    for (auto const &file : agi::fs::DirectoryIterator(config::path->Decode("?user/catalog/"), "*.sty"))
+        catalogs.push_back(agi::fs::path(file).stem().string());
+    return catalogs;
 }
 
-bool AssStyleStorage::CatalogExists(std::string const& catalogname) {
-	if (catalogname.empty()) return false;
-	auto filename = config::path->Decode("?user/catalog/" + catalogname + ".sty");
-	return agi::fs::FileExists(filename);
+bool AssStyleStorage::CatalogExists(std::string const &catalogname)
+{
+    if (catalogname.empty()) return false;
+    auto filename = config::path->Decode("?user/catalog/" + catalogname + ".sty");
+    return agi::fs::FileExists(filename);
 }
 
-void AssStyleStorage::ReplaceIntoFile(AssFile &file) {
-	for (auto const& s : style) {
-		delete file.GetStyle(s->name);
-		file.Styles.push_back(*new AssStyle(*s));
-	}
+void AssStyleStorage::ReplaceIntoFile(AssFile &file)
+{
+    for (auto const &s : style) {
+        delete file.GetStyle(s->name);
+        file.Styles.push_back(*new AssStyle(*s));
+    }
 }
 
diff --git a/src/ass_style_storage.h b/src/ass_style_storage.h
index 37f396c6ffd7846a7a13f5acc462c69bca81ed32..24b8dfe699f7c2ca520cad23d7e682766272d9f0 100644
--- a/src/ass_style_storage.h
+++ b/src/ass_style_storage.h
@@ -43,54 +43,54 @@ class AssFile;
 class AssStyle;
 
 class AssStyleStorage {
-	agi::fs::path file;
-	std::vector<std::unique_ptr<AssStyle>> style;
+    agi::fs::path file;
+    std::vector<std::unique_ptr<AssStyle>> style;
 
 public:
-	~AssStyleStorage();
-
-	typedef std::vector<std::unique_ptr<AssStyle>>::iterator iterator;
-	typedef std::vector<std::unique_ptr<AssStyle>>::const_iterator const_iterator;
-	iterator begin() { return style.begin(); }
-	iterator end() { return style.end(); }
-	const_iterator begin() const { return style.begin(); }
-	const_iterator end() const { return style.end(); }
-	void push_back(std::unique_ptr<AssStyle> new_style);
-	AssStyle *back() { return style.back().get(); }
-	AssStyle *operator[](size_t idx) const { return style[idx].get(); }
-	size_t size() const { return style.size(); }
-	void clear();
-
-	/// Get the names of all styles in this storage
-	std::vector<std::string> GetNames();
-
-	/// Delete the style at the given index
-	void Delete(int idx);
-
-	/// Get the style with the given name
-	/// @param name Case-insensitive style name
-	/// @return Style or nullptr if the requested style is not found
-	AssStyle *GetStyle(std::string const& name);
-
-	/// Save stored styles to a file
-	void Save() const;
-
-	/// Load stored styles from a file
-	/// @param filename Catalog filename. Does not have to exist.
-	void Load(agi::fs::path const& filename);
-
-	/// Load stored styles from a file in the default location
-	/// @param catalogname Basename for the catalog file. Does not have to exist.
-	void LoadCatalog(std::string const& catalogname);
-
-	/// Make a list of all existing style catalogs in the default location
-	static std::vector<std::string> GetCatalogs();
-
-	/// Check whether the name catalog exists in the default location
-	/// @param catalogname Basename for the catalog file to check for.
-	static bool CatalogExists(std::string const& catalogname);
-
-	/// Insert all styles into a file, replacing existing styles with the same names
-	/// @param file File to replace styles in
-	void ReplaceIntoFile(AssFile &file);
+    ~AssStyleStorage();
+
+    typedef std::vector<std::unique_ptr<AssStyle>>::iterator iterator;
+    typedef std::vector<std::unique_ptr<AssStyle>>::const_iterator const_iterator;
+    iterator begin() { return style.begin(); }
+    iterator end() { return style.end(); }
+    const_iterator begin() const { return style.begin(); }
+    const_iterator end() const { return style.end(); }
+    void push_back(std::unique_ptr<AssStyle> new_style);
+    AssStyle *back() { return style.back().get(); }
+    AssStyle *operator[](size_t idx) const { return style[idx].get(); }
+    size_t size() const { return style.size(); }
+    void clear();
+
+    /// Get the names of all styles in this storage
+    std::vector<std::string> GetNames();
+
+    /// Delete the style at the given index
+    void Delete(int idx);
+
+    /// Get the style with the given name
+    /// @param name Case-insensitive style name
+    /// @return Style or nullptr if the requested style is not found
+    AssStyle *GetStyle(std::string const &name);
+
+    /// Save stored styles to a file
+    void Save() const;
+
+    /// Load stored styles from a file
+    /// @param filename Catalog filename. Does not have to exist.
+    void Load(agi::fs::path const &filename);
+
+    /// Load stored styles from a file in the default location
+    /// @param catalogname Basename for the catalog file. Does not have to exist.
+    void LoadCatalog(std::string const &catalogname);
+
+    /// Make a list of all existing style catalogs in the default location
+    static std::vector<std::string> GetCatalogs();
+
+    /// Check whether the name catalog exists in the default location
+    /// @param catalogname Basename for the catalog file to check for.
+    static bool CatalogExists(std::string const &catalogname);
+
+    /// Insert all styles into a file, replacing existing styles with the same names
+    /// @param file File to replace styles in
+    void ReplaceIntoFile(AssFile &file);
 };
diff --git a/src/async_video_provider.cpp b/src/async_video_provider.cpp
index 33dbe4bca15e288800cbbf12c65c234cb2f04358..3435e18abb0538e925965d12997cf3548ff7e6d5 100644
--- a/src/async_video_provider.cpp
+++ b/src/async_video_provider.cpp
@@ -26,203 +26,213 @@
 #include <libaegisub/dispatch.h>
 
 enum {
-	NEW_SUBS_FILE = -1,
-	SUBS_FILE_ALREADY_LOADED = -2
+    NEW_SUBS_FILE = -1,
+    SUBS_FILE_ALREADY_LOADED = -2
 };
 
-std::shared_ptr<VideoFrame> AsyncVideoProvider::ProcFrame(int frame_number, double time, bool raw) {
-	// Find an unused buffer to use or allocate a new one if needed
-	std::shared_ptr<VideoFrame> frame;
-	for (auto& buffer : buffers) {
-		if (buffer.use_count() == 1) {
-			frame = buffer;
-			break;
-		}
-	}
-
-	if (!frame) {
-		frame = std::make_shared<VideoFrame>();
-		buffers.push_back(frame);
-	}
-
-	try {
-		source_provider->GetFrame(frame_number, *frame);
-	}
-	catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); }
-
-	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());
-				single_frame = frame_number;
-				subs_provider->LoadSubtitles(subs.get(), time);
-			}
-		}
-	}
-	catch (agi::Exception const& err) { throw SubtitlesProviderErrorEvent(err.GetMessage()); }
-
-	try {
-		subs_provider->DrawSubtitles(*frame, time / 1000.);
-	}
-	catch (agi::UserCancelException const&) { }
-
-	return frame;
+std::shared_ptr<VideoFrame> AsyncVideoProvider::ProcFrame(int frame_number, double time, bool raw)
+{
+    // Find an unused buffer to use or allocate a new one if needed
+    std::shared_ptr<VideoFrame> frame;
+    for (auto &buffer : buffers) {
+        if (buffer.use_count() == 1) {
+            frame = buffer;
+            break;
+        }
+    }
+
+    if (!frame) {
+        frame = std::make_shared<VideoFrame>();
+        buffers.push_back(frame);
+    }
+
+    try {
+        source_provider->GetFrame(frame_number, *frame);
+    }
+    catch (VideoProviderError const &err) { throw VideoProviderErrorEvent(err); }
+
+    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());
+                single_frame = frame_number;
+                subs_provider->LoadSubtitles(subs.get(), time);
+            }
+        }
+    }
+    catch (agi::Exception const &err) { throw SubtitlesProviderErrorEvent(err.GetMessage()); }
+
+    try {
+        subs_provider->DrawSubtitles(*frame, time / 1000.);
+    }
+    catch (agi::UserCancelException const &) { }
+
+    return frame;
 }
 
-static std::unique_ptr<SubtitlesProvider> get_subs_provider(wxEvtHandler *evt_handler, agi::BackgroundRunner *br) {
-	try {
-		return SubtitlesProviderFactory::GetProvider(br);
-	}
-	catch (agi::Exception const& err) {
-		evt_handler->AddPendingEvent(SubtitlesProviderErrorEvent(err.GetMessage()));
-		return nullptr;
-	}
+static std::unique_ptr<SubtitlesProvider> get_subs_provider(wxEvtHandler *evt_handler, agi::BackgroundRunner *br)
+{
+    try {
+        return SubtitlesProviderFactory::GetProvider(br);
+    }
+    catch (agi::Exception const &err) {
+        evt_handler->AddPendingEvent(SubtitlesProviderErrorEvent(err.GetMessage()));
+        return nullptr;
+    }
 }
 
-AsyncVideoProvider::AsyncVideoProvider(agi::fs::path const& video_filename, std::string const& colormatrix, wxEvtHandler *parent, agi::BackgroundRunner *br)
-: worker(agi::dispatch::Create())
-, subs_provider(get_subs_provider(parent, br))
-, source_provider(VideoProviderFactory::GetProvider(video_filename, colormatrix, br))
-, parent(parent)
+AsyncVideoProvider::AsyncVideoProvider(agi::fs::path const &video_filename, std::string const &colormatrix, wxEvtHandler *parent, agi::BackgroundRunner *br)
+    : worker(agi::dispatch::Create())
+    , subs_provider(get_subs_provider(parent, br))
+    , source_provider(VideoProviderFactory::GetProvider(video_filename, colormatrix, br))
+    , parent(parent)
 {
 }
 
-AsyncVideoProvider::~AsyncVideoProvider() {
-	// Block until all currently queued jobs are complete
-	worker->Sync([]{});
+AsyncVideoProvider::~AsyncVideoProvider()
+{
+    // Block until all currently queued jobs are complete
+    worker->Sync([] {});
 }
 
-void AsyncVideoProvider::LoadSubtitles(const AssFile *new_subs) throw() {
-	uint_fast32_t req_version = ++version;
-
-	auto copy = new AssFile(*new_subs);
-	worker->Async([=]{
-		subs.reset(copy);
-		single_frame = NEW_SUBS_FILE;
-		ProcAsync(req_version, false);
-	});
+void AsyncVideoProvider::LoadSubtitles(const AssFile *new_subs) throw()
+{
+    uint_fast32_t req_version = ++version;
+
+    auto copy = new AssFile(*new_subs);
+    worker->Async([ = ] {
+        subs.reset(copy);
+        single_frame = NEW_SUBS_FILE;
+        ProcAsync(req_version, false);
+    });
 }
 
-void AsyncVideoProvider::UpdateSubtitles(const AssFile *new_subs, const AssDialogue *changed) throw() {
-	uint_fast32_t req_version = ++version;
-
-	// Copy just the line which were changed, then replace the line at the
-	// same index in the worker's copy of the file with the new entry
-	auto copy = new AssDialogue(*changed);
-	worker->Async([=]{
-		int i = 0;
-		auto it = subs->Events.begin();
-		std::advance(it, copy->Row - i);
-		i = copy->Row;
-		subs->Events.insert(it, *copy);
-		delete &*it--;
-
-		single_frame = NEW_SUBS_FILE;
-		ProcAsync(req_version, true);
-	});
+void AsyncVideoProvider::UpdateSubtitles(const AssFile *new_subs, const AssDialogue *changed) throw()
+{
+    uint_fast32_t req_version = ++version;
+
+    // Copy just the line which were changed, then replace the line at the
+    // same index in the worker's copy of the file with the new entry
+    auto copy = new AssDialogue(*changed);
+    worker->Async([ = ] {
+        int i = 0;
+        auto it = subs->Events.begin();
+        std::advance(it, copy->Row - i);
+        i = copy->Row;
+        subs->Events.insert(it, *copy);
+        delete &*it--;
+
+        single_frame = NEW_SUBS_FILE;
+        ProcAsync(req_version, true);
+    });
 }
 
-void AsyncVideoProvider::RequestFrame(int new_frame, double new_time) throw() {
-	uint_fast32_t req_version = ++version;
+void AsyncVideoProvider::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, false);
-	});
+    worker->Async([ = ] {
+        time = new_time;
+        frame_number = new_frame;
+        ProcAsync(req_version, false);
+    });
 }
 
-bool AsyncVideoProvider::NeedUpdate(std::vector<AssDialogueBase const*> const& visible_lines) {
-	// Always need to render after a seek
-	if (single_frame != NEW_SUBS_FILE || frame_number != last_rendered)
-		return true;
-
-	// Obviously need to render if the number of visible lines has changed
-	if (visible_lines.size() != last_lines.size())
-		return true;
-
-	for (size_t i = 0; i < last_lines.size(); ++i) {
-		auto const& last = last_lines[i];
-		auto const& cur = *visible_lines[i];
-		if (last.Layer  != cur.Layer)  return true;
-		if (last.Margin != cur.Margin) return true;
-		if (last.Style  != cur.Style)  return true;
-		if (last.Effect != cur.Effect) return true;
-		if (last.Text   != cur.Text)   return true;
-
-		// Changing the start/end time effects the appearance only if the
-		// line is animated. This is obviously not a very accurate check for
-		// animated lines, but false positives aren't the end of the world
-		if ((last.Start != cur.Start || last.End != cur.End) &&
-			(!cur.Effect.get().empty() || cur.Text.get().find('\\') != std::string::npos))
-			return true;
-	}
-
-	return false;
+bool AsyncVideoProvider::NeedUpdate(std::vector<AssDialogueBase const *> const &visible_lines)
+{
+    // Always need to render after a seek
+    if (single_frame != NEW_SUBS_FILE || frame_number != last_rendered)
+        return true;
+
+    // Obviously need to render if the number of visible lines has changed
+    if (visible_lines.size() != last_lines.size())
+        return true;
+
+    for (size_t i = 0; i < last_lines.size(); ++i) {
+        auto const &last = last_lines[i];
+        auto const &cur = *visible_lines[i];
+        if (last.Layer  != cur.Layer)  return true;
+        if (last.Margin != cur.Margin) return true;
+        if (last.Style  != cur.Style)  return true;
+        if (last.Effect != cur.Effect) return true;
+        if (last.Text   != cur.Text)   return true;
+
+        // Changing the start/end time effects the appearance only if the
+        // line is animated. This is obviously not a very accurate check for
+        // animated lines, but false positives aren't the end of the world
+        if ((last.Start != cur.Start || last.End != cur.End) &&
+            (!cur.Effect.get().empty() || cur.Text.get().find('\\') != std::string::npos))
+            return true;
+    }
+
+    return false;
 }
 
-void AsyncVideoProvider::ProcAsync(uint_fast32_t req_version, bool check_updated) {
-	// Only actually produce the frame if there's no queued changes waiting
-	if (req_version < version || frame_number < 0) return;
-
-	std::vector<AssDialogueBase const*> visible_lines;
-	for (auto const& line : subs->Events) {
-		if (!line.Comment && !(line.Start > time || line.End <= time))
-			visible_lines.push_back(&line);
-	}
-
-	if (check_updated && !NeedUpdate(visible_lines)) return;
-
-	last_lines.clear();
-	last_lines.reserve(visible_lines.size());
-	for (auto line : visible_lines)
-		last_lines.push_back(*line);
-	last_rendered = frame_number;
-
-	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());
-	}
+void AsyncVideoProvider::ProcAsync(uint_fast32_t req_version, bool check_updated)
+{
+    // Only actually produce the frame if there's no queued changes waiting
+    if (req_version < version || frame_number < 0) return;
+
+    std::vector<AssDialogueBase const *> visible_lines;
+    for (auto const &line : subs->Events) {
+        if (!line.Comment && !(line.Start > time || line.End <= time))
+            visible_lines.push_back(&line);
+    }
+
+    if (check_updated && !NeedUpdate(visible_lines)) return;
+
+    last_lines.clear();
+    last_lines.reserve(visible_lines.size());
+    for (auto line : visible_lines)
+        last_lines.push_back(*line);
+    last_rendered = frame_number;
+
+    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<VideoFrame> AsyncVideoProvider::GetFrame(int frame, double time, bool raw) {
-	std::shared_ptr<VideoFrame> ret;
-	worker->Sync([&]{ ret = ProcFrame(frame, time, raw); });
-	return ret;
+std::shared_ptr<VideoFrame> AsyncVideoProvider::GetFrame(int frame, double time, bool raw)
+{
+    std::shared_ptr<VideoFrame> ret;
+    worker->Sync([&] { ret = ProcFrame(frame, time, raw); });
+    return ret;
 }
 
-void AsyncVideoProvider::SetColorSpace(std::string const& matrix) {
-	worker->Async([=] { source_provider->SetColorSpace(matrix); });
+void AsyncVideoProvider::SetColorSpace(std::string const &matrix)
+{
+    worker->Async([ = ] { source_provider->SetColorSpace(matrix); });
 }
 
 wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
 wxDEFINE_EVENT(EVT_VIDEO_ERROR, VideoProviderErrorEvent);
 wxDEFINE_EVENT(EVT_SUBTITLES_ERROR, SubtitlesProviderErrorEvent);
 
-VideoProviderErrorEvent::VideoProviderErrorEvent(VideoProviderError const& err)
-: agi::Exception(err.GetMessage())
+VideoProviderErrorEvent::VideoProviderErrorEvent(VideoProviderError const &err)
+    : agi::Exception(err.GetMessage())
 {
-	SetEventType(EVT_VIDEO_ERROR);
+    SetEventType(EVT_VIDEO_ERROR);
 }
-SubtitlesProviderErrorEvent::SubtitlesProviderErrorEvent(std::string const& err)
-: agi::Exception(err)
+SubtitlesProviderErrorEvent::SubtitlesProviderErrorEvent(std::string const &err)
+    : agi::Exception(err)
 {
-	SetEventType(EVT_SUBTITLES_ERROR);
+    SetEventType(EVT_SUBTITLES_ERROR);
 }
diff --git a/src/async_video_provider.h b/src/async_video_provider.h
index ce5c83dbc45ff0ae3e34bfdb4c0b6e64af87afa8..2de56169273bc420d94e28492f8e8d573e9f8265 100644
--- a/src/async_video_provider.h
+++ b/src/async_video_provider.h
@@ -32,126 +32,126 @@ class VideoProviderError;
 struct AssDialogueBase;
 struct VideoFrame;
 namespace agi {
-	class BackgroundRunner;
-	namespace dispatch { class Queue; }
+class BackgroundRunner;
+namespace dispatch { class Queue; }
 }
 
 /// An asynchronous video decoding and subtitle rendering wrapper
 class AsyncVideoProvider {
-	/// Asynchronous work queue
-	std::unique_ptr<agi::dispatch::Queue> worker;
+    /// Asynchronous work queue
+    std::unique_ptr<agi::dispatch::Queue> worker;
 
-	/// Subtitles provider
-	std::unique_ptr<SubtitlesProvider> subs_provider;
-	/// Video provider
-	std::unique_ptr<VideoProvider> source_provider;
-	/// Event handler to send FrameReady events to
-	wxEvtHandler *parent;
+    /// Subtitles provider
+    std::unique_ptr<SubtitlesProvider> subs_provider;
+    /// Video provider
+    std::unique_ptr<VideoProvider> source_provider;
+    /// Event handler to send FrameReady events to
+    wxEvtHandler *parent;
 
-	int frame_number = -1; ///< Last frame number requested
-	double time = -1.; ///< Time of the frame to pass to the subtitle renderer
+    int frame_number = -1; ///< Last frame number requested
+    double time = -1.; ///< Time of the frame to pass to the subtitle renderer
 
-	/// Copy of the subtitles file to avoid having to touch the project context
-	std::unique_ptr<AssFile> subs;
+    /// Copy of the subtitles file to avoid having to touch the project context
+    std::unique_ptr<AssFile> subs;
 
-	/// 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 = -1;
+    /// 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 = -1;
 
-	/// Last rendered frame number
-	int last_rendered = -1;
-	/// Last rendered subtitles on that frame
-	std::vector<AssDialogueBase> last_lines;
-	/// Check if we actually need to honor a frame request or if no visible
-	/// lines have actually changed
-	bool NeedUpdate(std::vector<AssDialogueBase const*> const& visible_lines);
+    /// Last rendered frame number
+    int last_rendered = -1;
+    /// Last rendered subtitles on that frame
+    std::vector<AssDialogueBase> last_lines;
+    /// Check if we actually need to honor a frame request or if no visible
+    /// lines have actually changed
+    bool NeedUpdate(std::vector<AssDialogueBase const *> const &visible_lines);
 
-	std::shared_ptr<VideoFrame> ProcFrame(int frame, double time, bool raw = false);
+    std::shared_ptr<VideoFrame> ProcFrame(int frame, double time, bool raw = false);
 
-	/// Produce a frame if req_version is still the current version
-	void ProcAsync(uint_fast32_t req_version, bool check_updated);
+    /// Produce a frame if req_version is still the current version
+    void ProcAsync(uint_fast32_t req_version, bool check_updated);
 
-	/// Monotonic counter used to drop frames when changes arrive faster than
-	/// they can be rendered
-	std::atomic<uint_fast32_t> version{ 0 };
+    /// Monotonic counter used to drop frames when changes arrive faster than
+    /// they can be rendered
+    std::atomic<uint_fast32_t> version{ 0 };
 
-	std::vector<std::shared_ptr<VideoFrame>> buffers;
+    std::vector<std::shared_ptr<VideoFrame>> buffers;
 
 public:
-	/// @brief Load the passed subtitle file
-	/// @param subs File to load
-	///
-	/// This function blocks until is it is safe for the calling thread to
-	/// modify subs
-	void LoadSubtitles(const AssFile *subs) throw();
-
-	/// @brief Update a previously loaded subtitle file
-	/// @param subs Subtitle file which was last passed to LoadSubtitles
-	/// @param changes Set of lines which have changed
-	///
-	/// This function only supports changes to existing lines, and not
-	/// insertions or deletions.
-	void UpdateSubtitles(const AssFile *subs, const AssDialogue *changes) throw();
-
-	/// @brief Queue a request for a frame
-	/// @brief frame Frame number
-	/// @brief time  Exact start time of the frame in seconds
-	///
-	/// This merely queues up a request and deletes any pending requests; there
-	/// is no guarantee that the requested frame will ever actually be produced
-	void RequestFrame(int frame, double time) throw();
-
-	/// @brief Synchronously get a frame
-	/// @brief frame Frame number
-	/// @brief time  Exact start time of the frame in seconds
-	/// @brief raw   Get raw frame without subtitles
-	std::shared_ptr<VideoFrame> GetFrame(int frame, double time, bool raw = false);
-
-	/// Ask the video provider to change YCbCr matricies
-	void SetColorSpace(std::string const& matrix);
-
-	int GetFrameCount() const             { return source_provider->GetFrameCount(); }
-	int GetWidth() const                  { return source_provider->GetWidth(); }
-	int GetHeight() const                 { return source_provider->GetHeight(); }
-	double GetDAR() const                 { return source_provider->GetDAR(); }
-	agi::vfr::Framerate GetFPS() const    { return source_provider->GetFPS(); }
-	std::vector<int> GetKeyFrames() const { return source_provider->GetKeyFrames(); }
-	std::string GetColorSpace() const     { return source_provider->GetColorSpace(); }
-	std::string GetRealColorSpace() const { return source_provider->GetRealColorSpace(); }
-	std::string GetWarning() const        { return source_provider->GetWarning(); }
-	std::string GetDecoderName() const    { return source_provider->GetDecoderName(); }
-	bool ShouldSetVideoProperties() const { return source_provider->ShouldSetVideoProperties(); }
-	bool HasAudio() const                 { return source_provider->HasAudio(); }
-
-	/// @brief Constructor
-	/// @param videoFileName File to open
-	/// @param parent Event handler to send FrameReady events to
-	AsyncVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, wxEvtHandler *parent, agi::BackgroundRunner *br);
-	~AsyncVideoProvider();
+    /// @brief Load the passed subtitle file
+    /// @param subs File to load
+    ///
+    /// This function blocks until is it is safe for the calling thread to
+    /// modify subs
+    void LoadSubtitles(const AssFile *subs) throw();
+
+    /// @brief Update a previously loaded subtitle file
+    /// @param subs Subtitle file which was last passed to LoadSubtitles
+    /// @param changes Set of lines which have changed
+    ///
+    /// This function only supports changes to existing lines, and not
+    /// insertions or deletions.
+    void UpdateSubtitles(const AssFile *subs, const AssDialogue *changes) throw();
+
+    /// @brief Queue a request for a frame
+    /// @brief frame Frame number
+    /// @brief time  Exact start time of the frame in seconds
+    ///
+    /// This merely queues up a request and deletes any pending requests; there
+    /// is no guarantee that the requested frame will ever actually be produced
+    void RequestFrame(int frame, double time) throw();
+
+    /// @brief Synchronously get a frame
+    /// @brief frame Frame number
+    /// @brief time  Exact start time of the frame in seconds
+    /// @brief raw   Get raw frame without subtitles
+    std::shared_ptr<VideoFrame> GetFrame(int frame, double time, bool raw = false);
+
+    /// Ask the video provider to change YCbCr matricies
+    void SetColorSpace(std::string const &matrix);
+
+    int GetFrameCount() const             { return source_provider->GetFrameCount(); }
+    int GetWidth() const                  { return source_provider->GetWidth(); }
+    int GetHeight() const                 { return source_provider->GetHeight(); }
+    double GetDAR() const                 { return source_provider->GetDAR(); }
+    agi::vfr::Framerate GetFPS() const    { return source_provider->GetFPS(); }
+    std::vector<int> GetKeyFrames() const { return source_provider->GetKeyFrames(); }
+    std::string GetColorSpace() const     { return source_provider->GetColorSpace(); }
+    std::string GetRealColorSpace() const { return source_provider->GetRealColorSpace(); }
+    std::string GetWarning() const        { return source_provider->GetWarning(); }
+    std::string GetDecoderName() const    { return source_provider->GetDecoderName(); }
+    bool ShouldSetVideoProperties() const { return source_provider->ShouldSetVideoProperties(); }
+    bool HasAudio() const                 { return source_provider->HasAudio(); }
+
+    /// @brief Constructor
+    /// @param videoFileName File to open
+    /// @param parent Event handler to send FrameReady events to
+    AsyncVideoProvider(agi::fs::path const &filename, std::string const &colormatrix, wxEvtHandler *parent, agi::BackgroundRunner *br);
+    ~AsyncVideoProvider();
 };
 
 /// Event which signals that a requested frame is ready
 struct FrameReadyEvent final : public wxEvent {
-	/// Frame which is ready
-	std::shared_ptr<VideoFrame> frame;
-	/// Time which was used for subtitle rendering
-	double time;
-	wxEvent *Clone() const override { return new FrameReadyEvent(*this); };
-	FrameReadyEvent(std::shared_ptr<VideoFrame> frame, double time)
-	: frame(std::move(frame)), time(time) { }
+    /// Frame which is ready
+    std::shared_ptr<VideoFrame> frame;
+    /// Time which was used for subtitle rendering
+    double time;
+    wxEvent *Clone() const override { return new FrameReadyEvent(*this); };
+    FrameReadyEvent(std::shared_ptr<VideoFrame> frame, double time)
+        : frame(std::move(frame)), time(time) { }
 };
 
 // These exceptions are wxEvents so that they can be passed directly back to
 // the parent thread as events
 struct VideoProviderErrorEvent final : public wxEvent, public agi::Exception {
-	wxEvent *Clone() const override { return new VideoProviderErrorEvent(*this); };
-	VideoProviderErrorEvent(VideoProviderError const& err);
+    wxEvent *Clone() const override { return new VideoProviderErrorEvent(*this); };
+    VideoProviderErrorEvent(VideoProviderError const &err);
 };
 
 struct SubtitlesProviderErrorEvent final : public wxEvent, public agi::Exception {
-	wxEvent *Clone() const override { return new SubtitlesProviderErrorEvent(*this); };
-	SubtitlesProviderErrorEvent(std::string const& msg);
+    wxEvent *Clone() const override { return new SubtitlesProviderErrorEvent(*this); };
+    SubtitlesProviderErrorEvent(std::string const &msg);
 };
 
 wxDECLARE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
diff --git a/src/audio_box.cpp b/src/audio_box.cpp
index 4d1f1518ff55cec739045c064180374230e82e15..c56b09297b2e2988d6c23acc8746b362eb33757b 100644
--- a/src/audio_box.cpp
+++ b/src/audio_box.cpp
@@ -51,187 +51,198 @@
 #include <wx/toolbar.h>
 
 enum {
-	Audio_Horizontal_Zoom = 1600,
-	Audio_Vertical_Zoom,
-	Audio_Volume
+    Audio_Horizontal_Zoom = 1600,
+    Audio_Vertical_Zoom,
+    Audio_Volume
 };
 
 AudioBox::AudioBox(wxWindow *parent, agi::Context *context)
-: wxSashWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxSW_3D | wxCLIP_CHILDREN)
-, controller(context->audioController.get())
-, context(context)
-, audio_open_connection(context->audioController->AddAudioPlayerOpenListener(&AudioBox::OnAudioOpen, this))
-, panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_RAISED))
-, audioDisplay(new AudioDisplay(panel, context->audioController.get(), context))
-, HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH))
-, VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
-, VolumeBar(new wxSlider(panel, Audio_Volume, OPT_GET("Audio/Volume")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
+    : wxSashWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxSW_3D | wxCLIP_CHILDREN)
+    , controller(context->audioController.get())
+    , context(context)
+    , audio_open_connection(context->audioController->AddAudioPlayerOpenListener(&AudioBox::OnAudioOpen, this))
+    , panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_RAISED))
+    , audioDisplay(new AudioDisplay(panel, context->audioController.get(), context))
+    , HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_BOTH))
+    , VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_BOTH | wxSL_INVERSE))
+    , VolumeBar(new wxSlider(panel, Audio_Volume, OPT_GET("Audio/Volume")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL | wxSL_BOTH | wxSL_INVERSE))
 {
-	SetSashVisible(wxSASH_BOTTOM, true);
-	Bind(wxEVT_SASH_DRAGGED, &AudioBox::OnSashDrag, this);
-
-	HorizontalZoom->SetToolTip(_("Horizontal zoom"));
-	VerticalZoom->SetToolTip(_("Vertical zoom"));
-	VolumeBar->SetToolTip(_("Audio Volume"));
-
-	bool link = OPT_GET("Audio/Link")->GetBool();
-	if (link) {
-		VolumeBar->SetValue(VerticalZoom->GetValue());
-		VolumeBar->Enable(false);
-	}
-
-	// VertVol sider
-	wxSizer *VertVol = new wxBoxSizer(wxHORIZONTAL);
-	VertVol->Add(VerticalZoom,1,wxEXPAND,0);
-	VertVol->Add(VolumeBar,1,wxEXPAND,0);
-	wxSizer *VertVolArea = new wxBoxSizer(wxVERTICAL);
-	VertVolArea->Add(VertVol,1,wxEXPAND,0);
-
-	auto link_btn = new ToggleBitmap(panel, context, "audio/opt/vertical_link", 16, "Audio", wxSize(20, -1));
-	link_btn->SetMaxSize(wxDefaultSize);
-	VertVolArea->Add(link_btn, 0, wxRIGHT | wxEXPAND, 0);
-	OPT_SUB("Audio/Link", &AudioBox::OnVerticalLink, this);
-
-	// Top sizer
-	wxSizer *TopSizer = new wxBoxSizer(wxHORIZONTAL);
-	TopSizer->Add(audioDisplay,1,wxEXPAND,0);
-	TopSizer->Add(HorizontalZoom,0,wxEXPAND,0);
-	TopSizer->Add(VertVolArea,0,wxEXPAND,0);
-
-	context->karaoke = new AudioKaraoke(panel, context);
-
-	// Main sizer
-	auto MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(TopSizer,1,wxEXPAND|wxALL,3);
-	MainSizer->Add(toolbar::GetToolbar(panel, "audio", context, "Audio"),0,wxEXPAND|wxLEFT|wxRIGHT,3);
-	MainSizer->Add(context->karaoke,0,wxEXPAND|wxALL,3);
-	MainSizer->Show(context->karaoke, false);
-	panel->SetSizer(MainSizer);
-
-	wxSizer *audioSashSizer = new wxBoxSizer(wxHORIZONTAL);
-	audioSashSizer->Add(panel, 1, wxEXPAND);
-	SetSizerAndFit(audioSashSizer);
-	SetMinSize(wxSize(-1, OPT_GET("Audio/Display Height")->GetInt()));
-	SetMinimumSizeY(panel->GetSize().GetHeight());
-
-	audioDisplay->Bind(wxEVT_MOUSEWHEEL, &AudioBox::OnMouseWheel, this);
-
-	audioDisplay->SetZoomLevel(-HorizontalZoom->GetValue());
-	audioDisplay->SetAmplitudeScale(pow(mid(1, VerticalZoom->GetValue(), 100) / 50.0, 3));
+    SetSashVisible(wxSASH_BOTTOM, true);
+    Bind(wxEVT_SASH_DRAGGED, &AudioBox::OnSashDrag, this);
+
+    HorizontalZoom->SetToolTip(_("Horizontal zoom"));
+    VerticalZoom->SetToolTip(_("Vertical zoom"));
+    VolumeBar->SetToolTip(_("Audio Volume"));
+
+    bool link = OPT_GET("Audio/Link")->GetBool();
+    if (link) {
+        VolumeBar->SetValue(VerticalZoom->GetValue());
+        VolumeBar->Enable(false);
+    }
+
+    // VertVol sider
+    wxSizer *VertVol = new wxBoxSizer(wxHORIZONTAL);
+    VertVol->Add(VerticalZoom, 1, wxEXPAND, 0);
+    VertVol->Add(VolumeBar, 1, wxEXPAND, 0);
+    wxSizer *VertVolArea = new wxBoxSizer(wxVERTICAL);
+    VertVolArea->Add(VertVol, 1, wxEXPAND, 0);
+
+    auto link_btn = new ToggleBitmap(panel, context, "audio/opt/vertical_link", 16, "Audio", wxSize(20, -1));
+    link_btn->SetMaxSize(wxDefaultSize);
+    VertVolArea->Add(link_btn, 0, wxRIGHT | wxEXPAND, 0);
+    OPT_SUB("Audio/Link", &AudioBox::OnVerticalLink, this);
+
+    // Top sizer
+    wxSizer *TopSizer = new wxBoxSizer(wxHORIZONTAL);
+    TopSizer->Add(audioDisplay, 1, wxEXPAND, 0);
+    TopSizer->Add(HorizontalZoom, 0, wxEXPAND, 0);
+    TopSizer->Add(VertVolArea, 0, wxEXPAND, 0);
+
+    context->karaoke = new AudioKaraoke(panel, context);
+
+    // Main sizer
+    auto MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(TopSizer, 1, wxEXPAND | wxALL, 3);
+    MainSizer->Add(toolbar::GetToolbar(panel, "audio", context, "Audio"), 0, wxEXPAND | wxLEFT | wxRIGHT, 3);
+    MainSizer->Add(context->karaoke, 0, wxEXPAND | wxALL, 3);
+    MainSizer->Show(context->karaoke, false);
+    panel->SetSizer(MainSizer);
+
+    wxSizer *audioSashSizer = new wxBoxSizer(wxHORIZONTAL);
+    audioSashSizer->Add(panel, 1, wxEXPAND);
+    SetSizerAndFit(audioSashSizer);
+    SetMinSize(wxSize(-1, OPT_GET("Audio/Display Height")->GetInt()));
+    SetMinimumSizeY(panel->GetSize().GetHeight());
+
+    audioDisplay->Bind(wxEVT_MOUSEWHEEL, &AudioBox::OnMouseWheel, this);
+
+    audioDisplay->SetZoomLevel(-HorizontalZoom->GetValue());
+    audioDisplay->SetAmplitudeScale(pow(mid(1, VerticalZoom->GetValue(), 100) / 50.0, 3));
 }
 
-BEGIN_EVENT_TABLE(AudioBox,wxSashWindow)
-	EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom)
-	EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom)
-	EVT_COMMAND_SCROLL(Audio_Volume, AudioBox::OnVolume)
+BEGIN_EVENT_TABLE(AudioBox, wxSashWindow)
+    EVT_COMMAND_SCROLL(Audio_Horizontal_Zoom, AudioBox::OnHorizontalZoom)
+    EVT_COMMAND_SCROLL(Audio_Vertical_Zoom, AudioBox::OnVerticalZoom)
+    EVT_COMMAND_SCROLL(Audio_Volume, AudioBox::OnVolume)
 END_EVENT_TABLE()
 
-void AudioBox::OnMouseWheel(wxMouseEvent &evt) {
-	if (!ForwardMouseWheelEvent(audioDisplay, evt))
-		return;
-
-	bool zoom = evt.CmdDown() != OPT_GET("Audio/Wheel Default to Zoom")->GetBool();
-	if (!zoom) {
-		int amount = -evt.GetWheelRotation() * GetClientSize().GetWidth() / (evt.GetWheelDelta() * 3);
-		// If the user did a horizontal scroll the amount should be inverted
-		// for it to be natural.
-		if (evt.GetWheelAxis() == 1) amount = -amount;
-
-		// Reset any accumulated zoom
-		mouse_zoom_accum = 0;
-
-		audioDisplay->ScrollBy(amount);
-	}
-	else if (evt.GetWheelAxis() == 0) {
-		mouse_zoom_accum += evt.GetWheelRotation();
-		int zoom_delta = mouse_zoom_accum / evt.GetWheelDelta();
-		mouse_zoom_accum %= evt.GetWheelDelta();
-		SetHorizontalZoom(audioDisplay->GetZoomLevel() + zoom_delta);
-	}
+void AudioBox::OnMouseWheel(wxMouseEvent &evt)
+{
+    if (!ForwardMouseWheelEvent(audioDisplay, evt))
+        return;
+
+    bool zoom = evt.CmdDown() != OPT_GET("Audio/Wheel Default to Zoom")->GetBool();
+    if (!zoom) {
+        int amount = -evt.GetWheelRotation() * GetClientSize().GetWidth() / (evt.GetWheelDelta() * 3);
+        // If the user did a horizontal scroll the amount should be inverted
+        // for it to be natural.
+        if (evt.GetWheelAxis() == 1) amount = -amount;
+
+        // Reset any accumulated zoom
+        mouse_zoom_accum = 0;
+
+        audioDisplay->ScrollBy(amount);
+    }
+    else if (evt.GetWheelAxis() == 0) {
+        mouse_zoom_accum += evt.GetWheelRotation();
+        int zoom_delta = mouse_zoom_accum / evt.GetWheelDelta();
+        mouse_zoom_accum %= evt.GetWheelDelta();
+        SetHorizontalZoom(audioDisplay->GetZoomLevel() + zoom_delta);
+    }
 }
 
-void AudioBox::OnSashDrag(wxSashEvent &event) {
-	if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
-		return;
+void AudioBox::OnSashDrag(wxSashEvent &event)
+{
+    if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
+        return;
 
-	int new_height = std::min(event.GetDragRect().GetHeight(), GetParent()->GetSize().GetHeight() - 1);
+    int new_height = std::min(event.GetDragRect().GetHeight(), GetParent()->GetSize().GetHeight() - 1);
 
-	SetMinSize(wxSize(-1, new_height));
-	GetParent()->Layout();
+    SetMinSize(wxSize(-1, new_height));
+    GetParent()->Layout();
 
-	// Karaoke mode is always disabled when the audio box is first opened, so
-	// the initial height shouldn't include it
-	if (context->karaoke->IsEnabled())
-		new_height -= context->karaoke->GetSize().GetHeight() + 6;
+    // Karaoke mode is always disabled when the audio box is first opened, so
+    // the initial height shouldn't include it
+    if (context->karaoke->IsEnabled())
+        new_height -= context->karaoke->GetSize().GetHeight() + 6;
 
-	OPT_SET("Audio/Display Height")->SetInt(new_height);
+    OPT_SET("Audio/Display Height")->SetInt(new_height);
 }
 
-void AudioBox::OnHorizontalZoom(wxScrollEvent &event) {
-	// Negate the value since we want zoom out to be on bottom and zoom in on top,
-	// but the control doesn't want negative on bottom and positive on top.
-	SetHorizontalZoom(-event.GetPosition());
+void AudioBox::OnHorizontalZoom(wxScrollEvent &event)
+{
+    // Negate the value since we want zoom out to be on bottom and zoom in on top,
+    // but the control doesn't want negative on bottom and positive on top.
+    SetHorizontalZoom(-event.GetPosition());
 }
 
-void AudioBox::SetHorizontalZoom(int new_zoom) {
-	audioDisplay->SetZoomLevel(new_zoom);
-	HorizontalZoom->SetValue(-new_zoom);
-	OPT_SET("Audio/Zoom/Horizontal")->SetInt(new_zoom);
+void AudioBox::SetHorizontalZoom(int new_zoom)
+{
+    audioDisplay->SetZoomLevel(new_zoom);
+    HorizontalZoom->SetValue(-new_zoom);
+    OPT_SET("Audio/Zoom/Horizontal")->SetInt(new_zoom);
 }
 
-void AudioBox::OnVerticalZoom(wxScrollEvent &event) {
-	int pos = mid(1, event.GetPosition(), 100);
-	OPT_SET("Audio/Zoom/Vertical")->SetInt(pos);
-	double value = pow(pos / 50.0, 3);
-	audioDisplay->SetAmplitudeScale(value);
-	if (!VolumeBar->IsEnabled()) {
-		VolumeBar->SetValue(pos);
-		controller->SetVolume(value);
-	}
+void AudioBox::OnVerticalZoom(wxScrollEvent &event)
+{
+    int pos = mid(1, event.GetPosition(), 100);
+    OPT_SET("Audio/Zoom/Vertical")->SetInt(pos);
+    double value = pow(pos / 50.0, 3);
+    audioDisplay->SetAmplitudeScale(value);
+    if (!VolumeBar->IsEnabled()) {
+        VolumeBar->SetValue(pos);
+        controller->SetVolume(value);
+    }
 }
 
-void AudioBox::OnVolume(wxScrollEvent &event) {
-	int pos = mid(1, event.GetPosition(), 100);
-	OPT_SET("Audio/Volume")->SetInt(pos);
-	controller->SetVolume(pow(pos / 50.0, 3));
+void AudioBox::OnVolume(wxScrollEvent &event)
+{
+    int pos = mid(1, event.GetPosition(), 100);
+    OPT_SET("Audio/Volume")->SetInt(pos);
+    controller->SetVolume(pow(pos / 50.0, 3));
 }
 
-void AudioBox::OnVerticalLink(agi::OptionValue const& opt) {
-	if (opt.GetBool()) {
-		int pos = mid(1, VerticalZoom->GetValue(), 100);
-		double value = pow(pos / 50.0, 3);
-		controller->SetVolume(value);
-		VolumeBar->SetValue(pos);
-	}
-	VolumeBar->Enable(!opt.GetBool());
+void AudioBox::OnVerticalLink(agi::OptionValue const &opt)
+{
+    if (opt.GetBool()) {
+        int pos = mid(1, VerticalZoom->GetValue(), 100);
+        double value = pow(pos / 50.0, 3);
+        controller->SetVolume(value);
+        VolumeBar->SetValue(pos);
+    }
+    VolumeBar->Enable(!opt.GetBool());
 }
 
-void AudioBox::OnAudioOpen() {
-	controller->SetVolume(pow(mid(1, VolumeBar->GetValue(), 100) / 50.0, 3));
+void AudioBox::OnAudioOpen()
+{
+    controller->SetVolume(pow(mid(1, VolumeBar->GetValue(), 100) / 50.0, 3));
 }
 
-void AudioBox::ShowKaraokeBar(bool show) {
-	wxSizer *panel_sizer = panel->GetSizer();
-	if (panel_sizer->IsShown(context->karaoke) == show) return;
+void AudioBox::ShowKaraokeBar(bool show)
+{
+    wxSizer *panel_sizer = panel->GetSizer();
+    if (panel_sizer->IsShown(context->karaoke) == show) return;
 
-	int new_height = GetSize().GetHeight();
-	int kara_height = context->karaoke->GetSize().GetHeight() + 6;
+    int new_height = GetSize().GetHeight();
+    int kara_height = context->karaoke->GetSize().GetHeight() + 6;
 
-	if (show)
-		new_height += kara_height;
-	else
-		new_height -= kara_height;
+    if (show)
+        new_height += kara_height;
+    else
+        new_height -= kara_height;
 
-	panel_sizer->Show(context->karaoke, show);
-	SetMinSize(wxSize(-1, new_height));
-	GetParent()->Layout();
+    panel_sizer->Show(context->karaoke, show);
+    SetMinSize(wxSize(-1, new_height));
+    GetParent()->Layout();
 }
 
-void AudioBox::ScrollAudioBy(int pixel_amount) {
-	audioDisplay->ScrollBy(pixel_amount);
+void AudioBox::ScrollAudioBy(int pixel_amount)
+{
+    audioDisplay->ScrollBy(pixel_amount);
 }
 
-void AudioBox::ScrollToActiveLine() {
-	if (controller->GetTimingController())
-		audioDisplay->ScrollTimeRangeInView(controller->GetTimingController()->GetIdealVisibleTimeRange());
+void AudioBox::ScrollToActiveLine()
+{
+    if (controller->GetTimingController())
+        audioDisplay->ScrollTimeRangeInView(controller->GetTimingController()->GetIdealVisibleTimeRange());
 }
diff --git a/src/audio_box.h b/src/audio_box.h
index ea633eba74d17d79f51e09ef51a5cf08de8bae13..5d2164ba497615ed351d68ac7e46f9e5b1ebf0ef 100644
--- a/src/audio_box.h
+++ b/src/audio_box.h
@@ -33,8 +33,8 @@
 #include <libaegisub/signal.h>
 
 namespace agi {
-	struct Context;
-	class OptionValue;
+struct Context;
+class OptionValue;
 }
 
 class AudioController;
@@ -50,50 +50,50 @@ class wxSlider;
 /// @class AudioBox
 /// @brief Panel with audio playback and timing controls, also containing an AudioDisplay
 class AudioBox final : public wxSashWindow {
-	/// The controller controlling this audio box
-	AudioController *controller;
+    /// The controller controlling this audio box
+    AudioController *controller;
 
-	/// Project context this operates on
-	agi::Context *context;
+    /// Project context this operates on
+    agi::Context *context;
 
-	agi::signal::Connection audio_open_connection;
+    agi::signal::Connection audio_open_connection;
 
 
-	/// Panel containing the children
-	wxPanel *panel;
+    /// Panel containing the children
+    wxPanel *panel;
 
-	/// The audio display in the box
-	AudioDisplay *audioDisplay;
+    /// The audio display in the box
+    AudioDisplay *audioDisplay;
 
-	wxSlider *HorizontalZoom;
-	wxSlider *VerticalZoom;
-	wxSlider *VolumeBar;
+    wxSlider *HorizontalZoom;
+    wxSlider *VerticalZoom;
+    wxSlider *VolumeBar;
 
-	// Mouse wheel zoom accumulator
-	int mouse_zoom_accum = 0;
+    // Mouse wheel zoom accumulator
+    int mouse_zoom_accum = 0;
 
-	void SetHorizontalZoom(int new_zoom);
-	void OnAudioOpen();
-	void OnHorizontalZoom(wxScrollEvent &event);
-	void OnMouseWheel(wxMouseEvent &evt);
-	void OnSashDrag(wxSashEvent &event);
-	void OnVerticalLink(agi::OptionValue const& opt);
-	void OnVerticalZoom(wxScrollEvent &event);
-	void OnVolume(wxScrollEvent &event);
+    void SetHorizontalZoom(int new_zoom);
+    void OnAudioOpen();
+    void OnHorizontalZoom(wxScrollEvent &event);
+    void OnMouseWheel(wxMouseEvent &evt);
+    void OnSashDrag(wxSashEvent &event);
+    void OnVerticalLink(agi::OptionValue const &opt);
+    void OnVerticalZoom(wxScrollEvent &event);
+    void OnVolume(wxScrollEvent &event);
 
 public:
-	AudioBox(wxWindow *parent, agi::Context *context);
+    AudioBox(wxWindow *parent, agi::Context *context);
 
-	void ShowKaraokeBar(bool show);
+    void ShowKaraokeBar(bool show);
 
-	/// @brief Scroll the audio display
-	/// @param pixel_amount Number of pixels to scroll the view
-	///
-	/// A positive amount moves the display to the right, making later parts of the audio visible.
-	void ScrollAudioBy(int pixel_amount);
+    /// @brief Scroll the audio display
+    /// @param pixel_amount Number of pixels to scroll the view
+    ///
+    /// A positive amount moves the display to the right, making later parts of the audio visible.
+    void ScrollAudioBy(int pixel_amount);
 
-	/// Make the currently active line visible in the audio display
-	void ScrollToActiveLine();
+    /// Make the currently active line visible in the audio display
+    void ScrollToActiveLine();
 
-	DECLARE_EVENT_TABLE()
+    DECLARE_EVENT_TABLE()
 };
diff --git a/src/audio_colorscheme.cpp b/src/audio_colorscheme.cpp
index a7a1142c9fbf33256fd523e28421838a1b832c57..6f8524d16e018adf977c0aff7f19759d033382c8 100644
--- a/src/audio_colorscheme.cpp
+++ b/src/audio_colorscheme.cpp
@@ -35,36 +35,34 @@
 
 #include <libaegisub/exception.h>
 
-AudioColorScheme::AudioColorScheme(int prec, std::string const& scheme_name, int audio_rendering_style)
-: palette((3<<prec) + 3)
-, factor(1<<prec)
+AudioColorScheme::AudioColorScheme(int prec, std::string const &scheme_name, int audio_rendering_style)
+    : palette((3 << prec) + 3)
+    , factor(1 << prec)
 {
-	std::string opt_base = "Colour/Schemes/" + scheme_name + "/";
-	switch (static_cast<AudioRenderingStyle>(audio_rendering_style))
-	{
-		case AudioStyle_Normal:   opt_base += "Normal/"; break;
-		case AudioStyle_Inactive: opt_base += "Inactive/"; break;
-		case AudioStyle_Selected: opt_base += "Selection/"; break;
-		case AudioStyle_Primary:  opt_base += "Primary/"; break;
-		default: throw agi::InternalError("Unknown audio rendering styling");
-	}
+    std::string opt_base = "Colour/Schemes/" + scheme_name + "/";
+    switch (static_cast<AudioRenderingStyle>(audio_rendering_style)) {
+    case AudioStyle_Normal:   opt_base += "Normal/"; break;
+    case AudioStyle_Inactive: opt_base += "Inactive/"; break;
+    case AudioStyle_Selected: opt_base += "Selection/"; break;
+    case AudioStyle_Primary:  opt_base += "Primary/"; break;
+    default: throw agi::InternalError("Unknown audio rendering styling");
+    }
 
-	double h_base  = OPT_GET(opt_base + "Hue Offset")->GetDouble();
-	double h_scale = OPT_GET(opt_base + "Hue Scale")->GetDouble();
-	double s_base  = OPT_GET(opt_base + "Saturation Offset")->GetDouble();
-	double s_scale = OPT_GET(opt_base + "Saturation Scale")->GetDouble();
-	double l_base  = OPT_GET(opt_base + "Lightness Offset")->GetDouble();
-	double l_scale = OPT_GET(opt_base + "Lightness Scale")->GetDouble();
+    double h_base  = OPT_GET(opt_base + "Hue Offset")->GetDouble();
+    double h_scale = OPT_GET(opt_base + "Hue Scale")->GetDouble();
+    double s_base  = OPT_GET(opt_base + "Saturation Offset")->GetDouble();
+    double s_scale = OPT_GET(opt_base + "Saturation Scale")->GetDouble();
+    double l_base  = OPT_GET(opt_base + "Lightness Offset")->GetDouble();
+    double l_scale = OPT_GET(opt_base + "Lightness Scale")->GetDouble();
 
-	for (size_t i = 0; i <= factor; ++i)
-	{
-		auto t = (double)i / factor;
-		hsl_to_rgb(
-			mid<int>(0, h_base + t * h_scale, 255),
-			mid<int>(0, s_base + t * s_scale, 255),
-			mid<int>(0, l_base + t * l_scale, 255),
-			&palette[i * 3 + 0],
-			&palette[i * 3 + 1],
-			&palette[i * 3 + 2]);
-	}
+    for (size_t i = 0; i <= factor; ++i) {
+        auto t = (double)i / factor;
+        hsl_to_rgb(
+            mid<int>(0, h_base + t * h_scale, 255),
+            mid<int>(0, s_base + t * s_scale, 255),
+            mid<int>(0, l_base + t * l_scale, 255),
+            &palette[i * 3 + 0],
+            &palette[i * 3 + 1],
+            &palette[i * 3 + 2]);
+    }
 }
diff --git a/src/audio_colorscheme.h b/src/audio_colorscheme.h
index d526a1352bfe29e970ea125cafd688c081e8c3e8..5437caaef851bd488a86ed49ed328b4760e84401 100644
--- a/src/audio_colorscheme.h
+++ b/src/audio_colorscheme.h
@@ -49,55 +49,52 @@
 /// First create an instance of this class, then call an initialisation function
 /// in it to fill the palette with a colour map.
 class AudioColorScheme {
-	/// The palette data for the map
-	std::vector<unsigned char> palette;
+    /// The palette data for the map
+    std::vector<unsigned char> palette;
 
-	/// Factor to multiply 0..1 values by to map them into the palette range
-	size_t factor;
+    /// Factor to multiply 0..1 values by to map them into the palette range
+    size_t factor;
 
-	/// @brief Get a floating point value's colour as a 24-bit RGB pixel
-	/// @param val The value to map from
-	const unsigned char *get_color(float val) const
-	{
-		return &palette[mid<size_t>(0, val * factor, factor) * 3];
-	}
+    /// @brief Get a floating point value's colour as a 24-bit RGB pixel
+    /// @param val The value to map from
+    const unsigned char *get_color(float val) const {
+        return &palette[mid<size_t>(0, val * factor, factor) * 3];
+    }
 
 public:
-	/// @brief Constructor
-	/// @param prec Bit precision to create the colour map with
-	/// @param scheme_name Name of the colour scheme to use
-	/// @param audio_rendering_style AudioRenderingStyle to init this colorscheme for
-	///
-	/// Allocates the palette array to 2^prec entries
-	AudioColorScheme(int prec, std::string const& scheme_name, int audio_rendering_style);
+    /// @brief Constructor
+    /// @param prec Bit precision to create the colour map with
+    /// @param scheme_name Name of the colour scheme to use
+    /// @param audio_rendering_style AudioRenderingStyle to init this colorscheme for
+    ///
+    /// Allocates the palette array to 2^prec entries
+    AudioColorScheme(int prec, std::string const &scheme_name, int audio_rendering_style);
 
-	/// @brief Map a floating point value to RGB
-	/// @param val   [in] The value to map from
-	/// @param pixel [out] First byte of the pixel to write
-	///
-	/// Writes into the XRGB pixel (assumed 32 bit without alpha) passed.
-	/// The pixel format is assumed to be the same as that in the palette.
-	void map(float val, unsigned char *pixel) const
-	{
-		// Find the colour in the palette
-		const unsigned char *color = get_color(val);
-		// Copy to the destination.
-		// Has to be done one byte at a time since we're writing RGB and not RGBX or RGBA
-		// data, and we otherwise write past the end of the pixel we're writing, possibly
-		// hitting adjacent memory blocks or just overwriting the start of the following
-		// scanline in the image.
-		// As the image is 24 bpp, 3 of every 4 uint32_t writes would  be unaligned anyway.
-		pixel[0] = color[0];
-		pixel[1] = color[1];
-		pixel[2] = color[2];
-	}
+    /// @brief Map a floating point value to RGB
+    /// @param val   [in] The value to map from
+    /// @param pixel [out] First byte of the pixel to write
+    ///
+    /// Writes into the XRGB pixel (assumed 32 bit without alpha) passed.
+    /// The pixel format is assumed to be the same as that in the palette.
+    void map(float val, unsigned char *pixel) const {
+        // Find the colour in the palette
+        const unsigned char *color = get_color(val);
+        // Copy to the destination.
+        // Has to be done one byte at a time since we're writing RGB and not RGBX or RGBA
+        // data, and we otherwise write past the end of the pixel we're writing, possibly
+        // hitting adjacent memory blocks or just overwriting the start of the following
+        // scanline in the image.
+        // As the image is 24 bpp, 3 of every 4 uint32_t writes would  be unaligned anyway.
+        pixel[0] = color[0];
+        pixel[1] = color[1];
+        pixel[2] = color[2];
+    }
 
-	/// @brief Get a floating point value's colour as a wxColour
-	/// @param val The value to map from
-	/// @return The corresponding wxColour
-	wxColour get(float val) const
-	{
-		const unsigned char *color = get_color(val);
-		return wxColour(color[0], color[1], color[2]);
-	}
+    /// @brief Get a floating point value's colour as a wxColour
+    /// @param val The value to map from
+    /// @return The corresponding wxColour
+    wxColour get(float val) const {
+        const unsigned char *color = get_color(val);
+        return wxColour(color[0], color[1], color[2]);
+    }
 };
diff --git a/src/audio_controller.cpp b/src/audio_controller.cpp
index 4de1928d177bbe1dee35fb68e8658ff8e76eb929..d4fc4c2a331ef2592bbc6d2fb9629184bc27689a 100644
--- a/src/audio_controller.cpp
+++ b/src/audio_controller.cpp
@@ -40,186 +40,182 @@
 #include <algorithm>
 
 AudioController::AudioController(agi::Context *context)
-: context(context)
-, playback_timer(this)
-, provider_connection(context->project->AddAudioProviderListener(&AudioController::OnAudioProvider, this))
+    : context(context)
+    , playback_timer(this)
+    , provider_connection(context->project->AddAudioProviderListener(&AudioController::OnAudioProvider, this))
 {
-	Bind(wxEVT_TIMER, &AudioController::OnPlaybackTimer, this, playback_timer.GetId());
+    Bind(wxEVT_TIMER, &AudioController::OnPlaybackTimer, this, playback_timer.GetId());
 
 #ifdef wxHAS_POWER_EVENTS
-	Bind(wxEVT_POWER_SUSPENDED, &AudioController::OnComputerSuspending, this);
-	Bind(wxEVT_POWER_RESUME, &AudioController::OnComputerResuming, this);
+    Bind(wxEVT_POWER_SUSPENDED, &AudioController::OnComputerSuspending, this);
+    Bind(wxEVT_POWER_RESUME, &AudioController::OnComputerResuming, this);
 #endif
 
-	OPT_SUB("Audio/Player", &AudioController::OnAudioPlayerChanged, this);
+    OPT_SUB("Audio/Player", &AudioController::OnAudioPlayerChanged, this);
 }
 
 AudioController::~AudioController()
 {
-	Stop();
+    Stop();
 }
 
 void AudioController::OnPlaybackTimer(wxTimerEvent &)
 {
-	if (!player) return;
+    if (!player) return;
 
-	int64_t pos = player->GetCurrentPosition();
-	if (!player->IsPlaying() ||
-		(playback_mode != PM_ToEnd && pos >= player->GetEndPosition()+200))
-	{
-		// The +200 is to allow the player to end the sound output cleanly,
-		// otherwise a popping artifact can sometimes be heard.
-		Stop();
-	}
-	else
-	{
-		AnnouncePlaybackPosition(MillisecondsFromSamples(pos));
-	}
+    int64_t pos = player->GetCurrentPosition();
+    if (!player->IsPlaying() ||
+        (playback_mode != PM_ToEnd && pos >= player->GetEndPosition() + 200)) {
+        // The +200 is to allow the player to end the sound output cleanly,
+        // otherwise a popping artifact can sometimes be heard.
+        Stop();
+    }
+    else {
+        AnnouncePlaybackPosition(MillisecondsFromSamples(pos));
+    }
 }
 
 #ifdef wxHAS_POWER_EVENTS
 void AudioController::OnComputerSuspending(wxPowerEvent &)
 {
-	Stop();
-	player.reset();
+    Stop();
+    player.reset();
 }
 
 void AudioController::OnComputerResuming(wxPowerEvent &)
 {
-	OnAudioPlayerChanged();
+    OnAudioPlayerChanged();
 }
 #endif
 
 void AudioController::OnAudioPlayerChanged()
 {
-	if (!provider) return;
+    if (!provider) return;
 
-	Stop();
-	player.reset();
+    Stop();
+    player.reset();
 
-	try
-	{
-		player = AudioPlayerFactory::GetAudioPlayer(provider, context->parent);
-	}
-	catch (...)
-	{
-		/// @todo This really shouldn't be just swallowing all audio player open errors
-		context->project->CloseAudio();
-	}
-	AnnounceAudioPlayerOpened();
+    try {
+        player = AudioPlayerFactory::GetAudioPlayer(provider, context->parent);
+    }
+    catch (...) {
+        /// @todo This really shouldn't be just swallowing all audio player open errors
+        context->project->CloseAudio();
+    }
+    AnnounceAudioPlayerOpened();
 }
 
 void AudioController::OnAudioProvider(agi::AudioProvider *new_provider)
 {
-	provider = new_provider;
-	Stop();
-	player.reset();
-	OnAudioPlayerChanged();
+    provider = new_provider;
+    Stop();
+    player.reset();
+    OnAudioPlayerChanged();
 }
 
 void AudioController::SetTimingController(std::unique_ptr<AudioTimingController> new_controller)
 {
-	timing_controller = std::move(new_controller);
-	if (timing_controller)
-		timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
+    timing_controller = std::move(new_controller);
+    if (timing_controller)
+        timing_controller->AddUpdatedPrimaryRangeListener(&AudioController::OnTimingControllerUpdatedPrimaryRange, this);
 
-	AnnounceTimingControllerChanged();
+    AnnounceTimingControllerChanged();
 }
 
 void AudioController::OnTimingControllerUpdatedPrimaryRange()
 {
-	if (playback_mode == PM_PrimaryRange)
-		player->SetEndPosition(SamplesFromMilliseconds(timing_controller->GetPrimaryPlaybackRange().end()));
+    if (playback_mode == PM_PrimaryRange)
+        player->SetEndPosition(SamplesFromMilliseconds(timing_controller->GetPrimaryPlaybackRange().end()));
 }
 
 void AudioController::PlayRange(const TimeRange &range)
 {
-	if (!player) return;
+    if (!player) return;
 
-	player->Play(SamplesFromMilliseconds(range.begin()), SamplesFromMilliseconds(range.length()));
-	playback_mode = PM_Range;
-	playback_timer.Start(20);
+    player->Play(SamplesFromMilliseconds(range.begin()), SamplesFromMilliseconds(range.length()));
+    playback_mode = PM_Range;
+    playback_timer.Start(20);
 
-	AnnouncePlaybackPosition(range.begin());
+    AnnouncePlaybackPosition(range.begin());
 }
 
 void AudioController::PlayPrimaryRange()
 {
-	PlayRange(GetPrimaryPlaybackRange());
-	if (playback_mode == PM_Range)
-		playback_mode = PM_PrimaryRange;
+    PlayRange(GetPrimaryPlaybackRange());
+    if (playback_mode == PM_Range)
+        playback_mode = PM_PrimaryRange;
 }
 
 void AudioController::PlayToEndOfPrimary(int start_ms)
 {
-	PlayRange(TimeRange(start_ms, GetPrimaryPlaybackRange().end()));
-	if (playback_mode == PM_Range)
-		playback_mode = PM_PrimaryRange;
+    PlayRange(TimeRange(start_ms, GetPrimaryPlaybackRange().end()));
+    if (playback_mode == PM_Range)
+        playback_mode = PM_PrimaryRange;
 }
 
 void AudioController::PlayToEnd(int start_ms)
 {
-	if (!player) return;
+    if (!player) return;
 
-	int64_t start_sample = SamplesFromMilliseconds(start_ms);
-	player->Play(start_sample, provider->GetNumSamples()-start_sample);
-	playback_mode = PM_ToEnd;
-	playback_timer.Start(20);
+    int64_t start_sample = SamplesFromMilliseconds(start_ms);
+    player->Play(start_sample, provider->GetNumSamples() - start_sample);
+    playback_mode = PM_ToEnd;
+    playback_timer.Start(20);
 
-	AnnouncePlaybackPosition(start_ms);
+    AnnouncePlaybackPosition(start_ms);
 }
 
 void AudioController::Stop()
 {
-	if (!player) return;
+    if (!player) return;
 
-	player->Stop();
-	playback_mode = PM_NotPlaying;
-	playback_timer.Stop();
+    player->Stop();
+    playback_mode = PM_NotPlaying;
+    playback_timer.Stop();
 
-	AnnouncePlaybackStop();
+    AnnouncePlaybackStop();
 }
 
 bool AudioController::IsPlaying()
 {
-	return player && playback_mode != PM_NotPlaying;
+    return player && playback_mode != PM_NotPlaying;
 }
 
 int AudioController::GetPlaybackPosition()
 {
-	if (!IsPlaying()) return 0;
+    if (!IsPlaying()) return 0;
 
-	return MillisecondsFromSamples(player->GetCurrentPosition());
+    return MillisecondsFromSamples(player->GetCurrentPosition());
 }
 
 int AudioController::GetDuration() const
 {
-	if (!provider) return 0;
-	return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
+    if (!provider) return 0;
+    return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
 }
 
 TimeRange AudioController::GetPrimaryPlaybackRange() const
 {
-	if (timing_controller)
-		return timing_controller->GetPrimaryPlaybackRange();
-	else
-		return TimeRange{0, 0};
+    if (timing_controller)
+        return timing_controller->GetPrimaryPlaybackRange();
+    else
+        return TimeRange{0, 0};
 }
 
 void AudioController::SetVolume(double volume)
 {
-	if (!player) return;
-	player->SetVolume(volume);
+    if (!player) return;
+    player->SetVolume(volume);
 }
 
 int64_t AudioController::SamplesFromMilliseconds(int64_t ms) const
 {
-	if (!provider) return 0;
-	return (ms * provider->GetSampleRate() + 999) / 1000;
+    if (!provider) return 0;
+    return (ms * provider->GetSampleRate() + 999) / 1000;
 }
 
 int64_t AudioController::MillisecondsFromSamples(int64_t samples) const
 {
-	if (!provider) return 0;
-	return samples * 1000 / provider->GetSampleRate();
+    if (!provider) return 0;
+    return samples * 1000 / provider->GetSampleRate();
 }
diff --git a/src/audio_controller.h b/src/audio_controller.h
index 6bc1927626c79c9e4d7268bde9e9291a5609bbe3..0c933ce2823715d6008f3f67744bc6fd710be766 100644
--- a/src/audio_controller.h
+++ b/src/audio_controller.h
@@ -47,145 +47,145 @@ namespace agi { struct Context; }
 /// AudioController owns an AudioPlayer and uses it to play audio from the
 /// project's current audio provider.
 class AudioController final : public wxEvtHandler {
-	/// Project context this controller belongs to
-	agi::Context *context;
+    /// Project context this controller belongs to
+    agi::Context *context;
 
-	/// Slot for subtitles save signal
-	agi::signal::Connection subtitle_save_slot;
+    /// Slot for subtitles save signal
+    agi::signal::Connection subtitle_save_slot;
 
-	/// Playback is in progress and the current position was updated
-	agi::signal::Signal<int> AnnouncePlaybackPosition;
+    /// Playback is in progress and the current position was updated
+    agi::signal::Signal<int> AnnouncePlaybackPosition;
 
-	/// Playback has stopped
-	agi::signal::Signal<> AnnouncePlaybackStop;
+    /// Playback has stopped
+    agi::signal::Signal<> AnnouncePlaybackStop;
 
-	/// The timing controller was replaced
-	agi::signal::Signal<> AnnounceTimingControllerChanged;
+    /// The timing controller was replaced
+    agi::signal::Signal<> AnnounceTimingControllerChanged;
 
-	/// A new audio player was created
-	agi::signal::Signal<> AnnounceAudioPlayerOpened;
+    /// A new audio player was created
+    agi::signal::Signal<> AnnounceAudioPlayerOpened;
 
-	/// The audio output object
-	std::unique_ptr<AudioPlayer> player;
+    /// The audio output object
+    std::unique_ptr<AudioPlayer> player;
 
-	/// The current timing mode, if any; owned by the audio controller
-	std::unique_ptr<AudioTimingController> timing_controller;
+    /// The current timing mode, if any; owned by the audio controller
+    std::unique_ptr<AudioTimingController> timing_controller;
 
-	enum PlaybackMode {
-		PM_NotPlaying,
-		PM_Range,
-		PM_PrimaryRange,
-		PM_ToEnd
-	};
-	/// The current playback mode
-	PlaybackMode playback_mode = PM_NotPlaying;
+    enum PlaybackMode {
+        PM_NotPlaying,
+        PM_Range,
+        PM_PrimaryRange,
+        PM_ToEnd
+    };
+    /// The current playback mode
+    PlaybackMode playback_mode = PM_NotPlaying;
 
-	/// Timer used for playback position updates
-	wxTimer playback_timer;
+    /// Timer used for playback position updates
+    wxTimer playback_timer;
 
-	/// The audio provider
-	agi::AudioProvider *provider = nullptr;
-	agi::signal::Connection provider_connection;
+    /// The audio provider
+    agi::AudioProvider *provider = nullptr;
+    agi::signal::Connection provider_connection;
 
-	void OnAudioProvider(agi::AudioProvider *new_provider);
+    void OnAudioProvider(agi::AudioProvider *new_provider);
 
-	/// Event handler for the playback timer
-	void OnPlaybackTimer(wxTimerEvent &event);
+    /// Event handler for the playback timer
+    void OnPlaybackTimer(wxTimerEvent &event);
 
-	/// @brief Timing controller signals primary playback range changed
-	void OnTimingControllerUpdatedPrimaryRange();
+    /// @brief Timing controller signals primary playback range changed
+    void OnTimingControllerUpdatedPrimaryRange();
 
-	/// @brief Timing controller signals that the rendering style ranges have changed
-	void OnTimingControllerUpdatedStyleRanges();
+    /// @brief Timing controller signals that the rendering style ranges have changed
+    void OnTimingControllerUpdatedStyleRanges();
 
-	/// Handler for the current audio player changing
-	void OnAudioPlayerChanged();
+    /// Handler for the current audio player changing
+    void OnAudioPlayerChanged();
 
 #ifdef wxHAS_POWER_EVENTS
-	/// Handle computer going into suspend mode by stopping audio and closing device
-	void OnComputerSuspending(wxPowerEvent &event);
-	/// Handle computer resuming from suspend by re-opening the audio device
-	void OnComputerResuming(wxPowerEvent &event);
+    /// Handle computer going into suspend mode by stopping audio and closing device
+    void OnComputerSuspending(wxPowerEvent &event);
+    /// Handle computer resuming from suspend by re-opening the audio device
+    void OnComputerResuming(wxPowerEvent &event);
 #endif
 
-	/// @brief Convert a count of audio samples to a time in milliseconds
-	/// @param samples Sample count to convert
-	/// @return The number of milliseconds equivalent to the sample-count, rounded down
-	int64_t MillisecondsFromSamples(int64_t samples) const;
+    /// @brief Convert a count of audio samples to a time in milliseconds
+    /// @param samples Sample count to convert
+    /// @return The number of milliseconds equivalent to the sample-count, rounded down
+    int64_t MillisecondsFromSamples(int64_t samples) const;
 
-	/// @brief Convert a time in milliseconds to a count of audio samples
-	/// @param ms Time in milliseconds to convert
-	/// @return The index of the first sample that is wholly inside the millisecond
-	int64_t SamplesFromMilliseconds(int64_t ms) const;
+    /// @brief Convert a time in milliseconds to a count of audio samples
+    /// @param ms Time in milliseconds to convert
+    /// @return The index of the first sample that is wholly inside the millisecond
+    int64_t SamplesFromMilliseconds(int64_t ms) const;
 
-	/// Get the duration of the currently open audio in milliseconds, or 0 if none
-	/// @return Duration in milliseconds
-	int GetDuration() const;
+    /// Get the duration of the currently open audio in milliseconds, or 0 if none
+    /// @return Duration in milliseconds
+    int GetDuration() const;
 
 public:
-	AudioController(agi::Context *context);
-	~AudioController();
-
-	/// @brief Start or restart audio playback, playing a range
-	/// @param range The range of audio to play back
-	///
-	/// The end of the played back range may be requested changed, but is not
-	/// changed automatically from any other operations.
-	void PlayRange(const TimeRange &range);
-
-	/// @brief Start or restart audio playback, playing the primary playback range
-	///
-	/// If the primary playback range is updated during playback, the end of
-	/// the active playback range will be updated to match the new selection.
-	/// The playback end can not be changed in any other way.
-	void PlayPrimaryRange();
-
-	/// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range
-	/// @param start_ms Time in milliseconds to start playback at
-	///
-	/// This behaves like PlayPrimaryRange, but the start point can differ from
-	/// the beginning of the primary range.
-	void PlayToEndOfPrimary(int start_ms);
-
-	/// @brief Start or restart audio playback, playing from a point to the end of stream
-	/// @param start_ms Time in milliseconds to start playback at
-	///
-	/// Playback to end cannot be converted to a range playback like range
-	/// playback can, it will continue until the end is reached, it is stopped,
-	/// or restarted.
-	void PlayToEnd(int start_ms);
-
-	/// @brief Stop all audio playback
-	void Stop();
-
-	/// @brief Determine whether playback is ongoing
-	/// @return True if audio is being played back
-	bool IsPlaying();
-
-	/// @brief Get the current playback position
-	/// @return Approximate current time in milliseconds being heard by the user
-	///
-	/// Returns 0 if playback is stopped. The return value is only approximate.
-	int GetPlaybackPosition();
-
-	/// @brief Get the primary playback range
-	/// @return An immutable TimeRange object
-	TimeRange GetPrimaryPlaybackRange() const;
-
-	/// @brief Set the playback audio volume
-	/// @param volume The new amplification factor for the audio
-	void SetVolume(double volume);
-
-	/// @brief Return the current timing controller
-	/// @return The current timing controller or 0
-	AudioTimingController *GetTimingController() const { return timing_controller.get(); }
-
-	/// @brief Change the current timing controller
-	/// @param new_mode The new timing controller or nullptr
-	void SetTimingController(std::unique_ptr<AudioTimingController> new_controller);
-
-	DEFINE_SIGNAL_ADDERS(AnnouncePlaybackPosition,        AddPlaybackPositionListener)
-	DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop,            AddPlaybackStopListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceAudioPlayerOpened,       AddAudioPlayerOpenListener)
+    AudioController(agi::Context *context);
+    ~AudioController();
+
+    /// @brief Start or restart audio playback, playing a range
+    /// @param range The range of audio to play back
+    ///
+    /// The end of the played back range may be requested changed, but is not
+    /// changed automatically from any other operations.
+    void PlayRange(const TimeRange &range);
+
+    /// @brief Start or restart audio playback, playing the primary playback range
+    ///
+    /// If the primary playback range is updated during playback, the end of
+    /// the active playback range will be updated to match the new selection.
+    /// The playback end can not be changed in any other way.
+    void PlayPrimaryRange();
+
+    /// @brief Start or restart audio playback, playing from a point to the end of of the primary playback range
+    /// @param start_ms Time in milliseconds to start playback at
+    ///
+    /// This behaves like PlayPrimaryRange, but the start point can differ from
+    /// the beginning of the primary range.
+    void PlayToEndOfPrimary(int start_ms);
+
+    /// @brief Start or restart audio playback, playing from a point to the end of stream
+    /// @param start_ms Time in milliseconds to start playback at
+    ///
+    /// Playback to end cannot be converted to a range playback like range
+    /// playback can, it will continue until the end is reached, it is stopped,
+    /// or restarted.
+    void PlayToEnd(int start_ms);
+
+    /// @brief Stop all audio playback
+    void Stop();
+
+    /// @brief Determine whether playback is ongoing
+    /// @return True if audio is being played back
+    bool IsPlaying();
+
+    /// @brief Get the current playback position
+    /// @return Approximate current time in milliseconds being heard by the user
+    ///
+    /// Returns 0 if playback is stopped. The return value is only approximate.
+    int GetPlaybackPosition();
+
+    /// @brief Get the primary playback range
+    /// @return An immutable TimeRange object
+    TimeRange GetPrimaryPlaybackRange() const;
+
+    /// @brief Set the playback audio volume
+    /// @param volume The new amplification factor for the audio
+    void SetVolume(double volume);
+
+    /// @brief Return the current timing controller
+    /// @return The current timing controller or 0
+    AudioTimingController *GetTimingController() const { return timing_controller.get(); }
+
+    /// @brief Change the current timing controller
+    /// @param new_mode The new timing controller or nullptr
+    void SetTimingController(std::unique_ptr<AudioTimingController> new_controller);
+
+    DEFINE_SIGNAL_ADDERS(AnnouncePlaybackPosition,        AddPlaybackPositionListener)
+    DEFINE_SIGNAL_ADDERS(AnnouncePlaybackStop,            AddPlaybackStopListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceTimingControllerChanged, AddTimingControllerListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceAudioPlayerOpened,       AddAudioPlayerOpenListener)
 };
diff --git a/src/audio_display.cpp b/src/audio_display.cpp
index 8cdb3d804ee746ec7fce4cf82a3507fa038f57d4..39e1992751adfc398dbb326b037d3415f638af49 100644
--- a/src/audio_display.cpp
+++ b/src/audio_display.cpp
@@ -57,31 +57,31 @@
 /// @brief Interface for objects on the audio display that can respond to mouse events
 class AudioDisplayInteractionObject {
 public:
-	/// @brief The user is interacting with the object using the mouse
-	/// @param event Mouse event data
-	/// @return True to take mouse capture, false to release mouse capture
-	///
-	/// Assuming no object has the mouse capture, the audio display uses other methods
-	/// in the object implementing this interface to determine whether a mouse event
-	/// should go to the object. If the mouse event goes to the object, this method
-	/// is called.
-	///
-	/// If this method returns true, the audio display takes the mouse capture and
-	/// stores a pointer to the AudioDisplayInteractionObject interface for the object
-	/// and redirects the next mouse event to that object.
-	///
-	/// If the object that has the mouse capture returns false from this method, the
-	/// capture is released and regular processing is done for the next event.
-	///
-	/// If the object does not have mouse capture and returns false from this method,
-	/// no capture is taken or released and regular processing is done for the next
-	/// mouse event.
-	virtual bool OnMouseEvent(wxMouseEvent &event) = 0;
-
-	/// @brief Destructor
-	///
-	/// Empty virtual destructor for the cases that need it.
-	virtual ~AudioDisplayInteractionObject() = default;
+    /// @brief The user is interacting with the object using the mouse
+    /// @param event Mouse event data
+    /// @return True to take mouse capture, false to release mouse capture
+    ///
+    /// Assuming no object has the mouse capture, the audio display uses other methods
+    /// in the object implementing this interface to determine whether a mouse event
+    /// should go to the object. If the mouse event goes to the object, this method
+    /// is called.
+    ///
+    /// If this method returns true, the audio display takes the mouse capture and
+    /// stores a pointer to the AudioDisplayInteractionObject interface for the object
+    /// and redirects the next mouse event to that object.
+    ///
+    /// If the object that has the mouse capture returns false from this method, the
+    /// capture is released and regular processing is done for the next event.
+    ///
+    /// If the object does not have mouse capture and returns false from this method,
+    /// no capture is taken or released and regular processing is done for the next
+    /// mouse event.
+    virtual bool OnMouseEvent(wxMouseEvent &event) = 0;
+
+    /// @brief Destructor
+    ///
+    /// Empty virtual destructor for the cases that need it.
+    virtual ~AudioDisplayInteractionObject() = default;
 };
 
 namespace {
@@ -93,516 +93,486 @@ namespace {
 /// SetColourScheme must be called to set the active colour scheme before
 /// colours can be retrieved
 class UIColours {
-	wxColour light_colour;         ///< Light unfocused colour from the colour scheme
-	wxColour dark_colour;          ///< Dark unfocused colour from the colour scheme
-	wxColour sel_colour;           ///< Selection unfocused colour from the colour scheme
-	wxColour light_focused_colour; ///< Light focused colour from the colour scheme
-	wxColour dark_focused_colour;  ///< Dark focused colour from the colour scheme
-	wxColour sel_focused_colour;   ///< Selection focused colour from the colour scheme
-
-	bool focused = false; ///< Use the focused colours?
+    wxColour light_colour;         ///< Light unfocused colour from the colour scheme
+    wxColour dark_colour;          ///< Dark unfocused colour from the colour scheme
+    wxColour sel_colour;           ///< Selection unfocused colour from the colour scheme
+    wxColour light_focused_colour; ///< Light focused colour from the colour scheme
+    wxColour dark_focused_colour;  ///< Dark focused colour from the colour scheme
+    wxColour sel_focused_colour;   ///< Selection focused colour from the colour scheme
+
+    bool focused = false; ///< Use the focused colours?
 public:
-	/// Set the colour scheme to load colours from
-	/// @param name Name of the colour scheme
-	void SetColourScheme(std::string const& name)
-	{
-		std::string opt_prefix = "Colour/Schemes/" + name + "/UI/";
-		light_colour = to_wx(OPT_GET(opt_prefix + "Light")->GetColor());
-		dark_colour = to_wx(OPT_GET(opt_prefix + "Dark")->GetColor());
-		sel_colour = to_wx(OPT_GET(opt_prefix + "Selection")->GetColor());
-
-		opt_prefix = "Colour/Schemes/" + name + "/UI Focused/";
-		light_focused_colour = to_wx(OPT_GET(opt_prefix + "Light")->GetColor());
-		dark_focused_colour = to_wx(OPT_GET(opt_prefix + "Dark")->GetColor());
-		sel_focused_colour = to_wx(OPT_GET(opt_prefix + "Selection")->GetColor());
-	}
-
-	/// Set whether to use the focused or unfocused colours
-	/// @param focused If true, focused colours will be returned
-	void SetFocused(bool focused) { this->focused = focused; }
-
-	/// Get the current Light colour
-	wxColour Light() const { return focused ? light_focused_colour : light_colour; }
-	/// Get the current Dark colour
-	wxColour Dark() const { return focused ? dark_focused_colour : dark_colour; }
-	/// Get the current Selection colour
-	wxColour Selection() const { return focused ? sel_focused_colour : sel_colour; }
+    /// Set the colour scheme to load colours from
+    /// @param name Name of the colour scheme
+    void SetColourScheme(std::string const &name) {
+        std::string opt_prefix = "Colour/Schemes/" + name + "/UI/";
+        light_colour = to_wx(OPT_GET(opt_prefix + "Light")->GetColor());
+        dark_colour = to_wx(OPT_GET(opt_prefix + "Dark")->GetColor());
+        sel_colour = to_wx(OPT_GET(opt_prefix + "Selection")->GetColor());
+
+        opt_prefix = "Colour/Schemes/" + name + "/UI Focused/";
+        light_focused_colour = to_wx(OPT_GET(opt_prefix + "Light")->GetColor());
+        dark_focused_colour = to_wx(OPT_GET(opt_prefix + "Dark")->GetColor());
+        sel_focused_colour = to_wx(OPT_GET(opt_prefix + "Selection")->GetColor());
+    }
+
+    /// Set whether to use the focused or unfocused colours
+    /// @param focused If true, focused colours will be returned
+    void SetFocused(bool focused) { this->focused = focused; }
+
+    /// Get the current Light colour
+    wxColour Light() const { return focused ? light_focused_colour : light_colour; }
+    /// Get the current Dark colour
+    wxColour Dark() const { return focused ? dark_focused_colour : dark_colour; }
+    /// Get the current Selection colour
+    wxColour Selection() const { return focused ? sel_focused_colour : sel_colour; }
 };
 
 class AudioDisplayScrollbar final : public AudioDisplayInteractionObject {
-	static const int height = 15;
-	static const int min_width = 10;
+    static const int height = 15;
+    static const int min_width = 10;
 
-	wxRect bounds;
-	wxRect thumb;
+    wxRect bounds;
+    wxRect thumb;
 
-	bool dragging = false;   ///< user is dragging with the primary mouse button
+    bool dragging = false;   ///< user is dragging with the primary mouse button
 
-	int data_length = 1; ///< total amount of data in control
-	int page_length = 1; ///< amount of data in one page
-	int position    = 0; ///< first item displayed
+    int data_length = 1; ///< total amount of data in control
+    int page_length = 1; ///< amount of data in one page
+    int position    = 0; ///< first item displayed
 
-	int sel_start  = -1; ///< first data item in selection
-	int sel_length = 0;  ///< number of data items in selection
+    int sel_start  = -1; ///< first data item in selection
+    int sel_length = 0;  ///< number of data items in selection
 
-	UIColours colours; ///< Colour provider
+    UIColours colours; ///< Colour provider
 
-	/// Containing display to send scroll events to
-	AudioDisplay *display;
+    /// Containing display to send scroll events to
+    AudioDisplay *display;
 
-	// Recalculate thumb bounds from position and length data
-	void RecalculateThumb()
-	{
-		thumb.width = std::max<int>(min_width, (int64_t)bounds.width * page_length / data_length);
-		thumb.height = height;
-		thumb.x = int((int64_t)bounds.width * position / data_length);
-		thumb.y = bounds.y;
-	}
+    // Recalculate thumb bounds from position and length data
+    void RecalculateThumb() {
+        thumb.width = std::max<int>(min_width, (int64_t)bounds.width * page_length / data_length);
+        thumb.height = height;
+        thumb.x = int((int64_t)bounds.width * position / data_length);
+        thumb.y = bounds.y;
+    }
 
 public:
-	AudioDisplayScrollbar(AudioDisplay *display)
-	: display(display)
-	{
-	}
-
-	/// The audio display has changed size
-	void SetDisplaySize(const wxSize &display_size)
-	{
-		bounds.x = 0;
-		bounds.y = display_size.y - height;
-		bounds.width = display_size.x;
-		bounds.height = height;
-		page_length = display_size.x;
-
-		RecalculateThumb();
-	}
-
-	void SetColourScheme(std::string const& name)
-	{
-		colours.SetColourScheme(name);
-	}
-
-	const wxRect & GetBounds() const { return bounds; }
-	int GetPosition() const { return position; }
-
-	int SetPosition(int new_position)
-	{
-		// These two conditionals can't be swapped, otherwise the position can become
-		// negative if the entire data is shorter than one page.
-		if (new_position + page_length >= data_length)
-			new_position = data_length - page_length - 1;
-		if (new_position < 0)
-			new_position = 0;
-
-		position = new_position;
-		RecalculateThumb();
-
-		return position;
-	}
-
-	void SetSelection(int new_start, int new_length)
-	{
-		sel_start = (int64_t)new_start * bounds.width / data_length;
-		sel_length = (int64_t)new_length * bounds.width / data_length;
-	}
-
-	void ChangeLengths(int new_data_length, int new_page_length)
-	{
-		data_length = new_data_length;
-		page_length = new_page_length;
-
-		RecalculateThumb();
-	}
-
-	bool OnMouseEvent(wxMouseEvent &event) override
-	{
-		if (event.LeftIsDown())
-		{
-			const int thumb_left = event.GetPosition().x - thumb.width/2;
-			const int data_length_less_page = data_length - page_length;
-			const int shaft_length_less_thumb = bounds.width - thumb.width;
-
-			display->ScrollPixelToLeft((int64_t)data_length_less_page * thumb_left / shaft_length_less_thumb);
-
-			dragging = true;
-		}
-		else if (event.LeftUp())
-		{
-			dragging = false;
-		}
-
-		return dragging;
-	}
-
-	void Paint(wxDC &dc, bool has_focus, int load_progress)
-	{
-		colours.SetFocused(has_focus);
-
-		dc.SetPen(wxPen(colours.Light()));
-		dc.SetBrush(wxBrush(colours.Dark()));
-		dc.DrawRectangle(bounds);
-
-		if (sel_length > 0 && sel_start >= 0)
-		{
-			dc.SetPen(wxPen(colours.Selection()));
-			dc.SetBrush(wxBrush(colours.Selection()));
-			dc.DrawRectangle(wxRect(sel_start, bounds.y, sel_length, bounds.height));
-		}
-
-		dc.SetPen(wxPen(colours.Light()));
-		dc.SetBrush(*wxTRANSPARENT_BRUSH);
-		dc.DrawRectangle(bounds);
-
-		if (load_progress > 0 && load_progress < data_length)
-		{
-			wxRect marker(
-				(int64_t)bounds.width * load_progress / data_length - 25, bounds.y + 1,
-				25, bounds.height - 2);
-			dc.GradientFillLinear(marker, colours.Dark(), colours.Light());
-		}
-
-		dc.SetPen(wxPen(colours.Light()));
-		dc.SetBrush(wxBrush(colours.Light()));
-		dc.DrawRectangle(thumb);
-	}
+    AudioDisplayScrollbar(AudioDisplay *display)
+        : display(display) {
+    }
+
+    /// The audio display has changed size
+    void SetDisplaySize(const wxSize &display_size) {
+        bounds.x = 0;
+        bounds.y = display_size.y - height;
+        bounds.width = display_size.x;
+        bounds.height = height;
+        page_length = display_size.x;
+
+        RecalculateThumb();
+    }
+
+    void SetColourScheme(std::string const &name) {
+        colours.SetColourScheme(name);
+    }
+
+    const wxRect &GetBounds() const { return bounds; }
+    int GetPosition() const { return position; }
+
+    int SetPosition(int new_position) {
+        // These two conditionals can't be swapped, otherwise the position can become
+        // negative if the entire data is shorter than one page.
+        if (new_position + page_length >= data_length)
+            new_position = data_length - page_length - 1;
+        if (new_position < 0)
+            new_position = 0;
+
+        position = new_position;
+        RecalculateThumb();
+
+        return position;
+    }
+
+    void SetSelection(int new_start, int new_length) {
+        sel_start = (int64_t)new_start * bounds.width / data_length;
+        sel_length = (int64_t)new_length * bounds.width / data_length;
+    }
+
+    void ChangeLengths(int new_data_length, int new_page_length) {
+        data_length = new_data_length;
+        page_length = new_page_length;
+
+        RecalculateThumb();
+    }
+
+    bool OnMouseEvent(wxMouseEvent &event) override {
+        if (event.LeftIsDown()) {
+            const int thumb_left = event.GetPosition().x - thumb.width / 2;
+            const int data_length_less_page = data_length - page_length;
+            const int shaft_length_less_thumb = bounds.width - thumb.width;
+
+            display->ScrollPixelToLeft((int64_t)data_length_less_page * thumb_left / shaft_length_less_thumb);
+
+            dragging = true;
+        }
+        else if (event.LeftUp()) {
+            dragging = false;
+        }
+
+        return dragging;
+    }
+
+    void Paint(wxDC &dc, bool has_focus, int load_progress) {
+        colours.SetFocused(has_focus);
+
+        dc.SetPen(wxPen(colours.Light()));
+        dc.SetBrush(wxBrush(colours.Dark()));
+        dc.DrawRectangle(bounds);
+
+        if (sel_length > 0 && sel_start >= 0) {
+            dc.SetPen(wxPen(colours.Selection()));
+            dc.SetBrush(wxBrush(colours.Selection()));
+            dc.DrawRectangle(wxRect(sel_start, bounds.y, sel_length, bounds.height));
+        }
+
+        dc.SetPen(wxPen(colours.Light()));
+        dc.SetBrush(*wxTRANSPARENT_BRUSH);
+        dc.DrawRectangle(bounds);
+
+        if (load_progress > 0 && load_progress < data_length) {
+            wxRect marker(
+                (int64_t)bounds.width * load_progress / data_length - 25, bounds.y + 1,
+                25, bounds.height - 2);
+            dc.GradientFillLinear(marker, colours.Dark(), colours.Light());
+        }
+
+        dc.SetPen(wxPen(colours.Light()));
+        dc.SetBrush(wxBrush(colours.Light()));
+        dc.DrawRectangle(thumb);
+    }
 };
 
 const int AudioDisplayScrollbar::min_width;
 
 class AudioDisplayTimeline final : public AudioDisplayInteractionObject {
-	int duration = 0;          ///< Total duration in ms
-	double ms_per_pixel = 1.0; ///< Milliseconds per pixel
-	int pixel_left = 0;        ///< Leftmost visible pixel (i.e. scroll position)
-
-	wxRect bounds;
-
-	wxPoint drag_lastpos;
-	bool dragging = false;
-
-	enum Scale {
-		Sc_Millisecond,
-		Sc_Centisecond,
-		Sc_Decisecond,
-		Sc_Second,
-		Sc_Decasecond,
-		Sc_Minute,
-		Sc_Decaminute,
-		Sc_Hour,
-		Sc_Decahour, // If anyone needs this they should reconsider their project
-		Sc_MAX = Sc_Decahour
-	};
-	Scale scale_minor;
-	int scale_major_modulo; ///< If minor_scale_mark_index % scale_major_modulo == 0 the mark is a major mark
-	double scale_minor_divisor; ///< Absolute scale-mark index multiplied by this number gives sample index for scale mark
-
-	AudioDisplay *display; ///< Containing audio display
-
-	UIColours colours; ///< Colour provider
+    int duration = 0;          ///< Total duration in ms
+    double ms_per_pixel = 1.0; ///< Milliseconds per pixel
+    int pixel_left = 0;        ///< Leftmost visible pixel (i.e. scroll position)
+
+    wxRect bounds;
+
+    wxPoint drag_lastpos;
+    bool dragging = false;
+
+    enum Scale {
+        Sc_Millisecond,
+        Sc_Centisecond,
+        Sc_Decisecond,
+        Sc_Second,
+        Sc_Decasecond,
+        Sc_Minute,
+        Sc_Decaminute,
+        Sc_Hour,
+        Sc_Decahour, // If anyone needs this they should reconsider their project
+        Sc_MAX = Sc_Decahour
+    };
+    Scale scale_minor;
+    int scale_major_modulo; ///< If minor_scale_mark_index % scale_major_modulo == 0 the mark is a major mark
+    double scale_minor_divisor; ///< Absolute scale-mark index multiplied by this number gives sample index for scale mark
+
+    AudioDisplay *display; ///< Containing audio display
+
+    UIColours colours; ///< Colour provider
 
 public:
-	AudioDisplayTimeline(AudioDisplay *display)
-	: display(display)
-	{
-		int width, height;
-		display->GetTextExtent("0123456789:.", &width, &height);
-		bounds.height = height + 4;
-	}
-
-	void SetColourScheme(std::string const& name)
-	{
-		colours.SetColourScheme(name);
-	}
-
-	void SetDisplaySize(const wxSize &display_size)
-	{
-		// The size is without anything that goes below the timeline (like scrollbar)
-		bounds.width = display_size.x;
-		bounds.x = 0;
-		bounds.y = 0;
-	}
-
-	int GetHeight() const { return bounds.height; }
-	const wxRect & GetBounds() const { return bounds; }
-
-	void ChangeAudio(int new_duration)
-	{
-		duration = new_duration;
-	}
-
-	void ChangeZoom(double new_ms_per_pixel)
-	{
-		ms_per_pixel = new_ms_per_pixel;
-
-		double px_sec = 1000.0 / ms_per_pixel;
-
-		if (px_sec > 3000) {
-			scale_minor = Sc_Millisecond;
-			scale_minor_divisor = 1.0;
-			scale_major_modulo = 10;
-		} else if (px_sec > 300) {
-			scale_minor = Sc_Centisecond;
-			scale_minor_divisor = 10.0;
-			scale_major_modulo = 10;
-		} else if (px_sec > 30) {
-			scale_minor = Sc_Decisecond;
-			scale_minor_divisor = 100.0;
-			scale_major_modulo = 10;
-		} else if (px_sec > 3) {
-			scale_minor = Sc_Second;
-			scale_minor_divisor = 1000.0;
-			scale_major_modulo = 10;
-		} else if (px_sec > 1.0/3.0) {
-			scale_minor = Sc_Decasecond;
-			scale_minor_divisor = 10000.0;
-			scale_major_modulo = 6;
-		} else if (px_sec > 1.0/9.0) {
-			scale_minor = Sc_Minute;
-			scale_minor_divisor = 60000.0;
-			scale_major_modulo = 10;
-		} else if (px_sec > 1.0/90.0) {
-			scale_minor = Sc_Decaminute;
-			scale_minor_divisor = 600000.0;
-			scale_major_modulo = 6;
-		} else {
-			scale_minor = Sc_Hour;
-			scale_minor_divisor = 3600000.0;
-			scale_major_modulo = 10;
-		}
-	}
-
-	void SetPosition(int new_pixel_left)
-	{
-		pixel_left = std::max(new_pixel_left, 0);
-	}
-
-	bool OnMouseEvent(wxMouseEvent &event) override
-	{
-		if (event.LeftDown())
-		{
-			drag_lastpos = event.GetPosition();
-			dragging = true;
-		}
-		else if (event.LeftIsDown())
-		{
-			display->ScrollPixelToLeft(pixel_left - event.GetPosition().x + drag_lastpos.x);
-
-			drag_lastpos = event.GetPosition();
-			dragging = true;
-		}
-		else if (event.LeftUp())
-		{
-			dragging = false;
-		}
-
-		return dragging;
-	}
-
-	void Paint(wxDC &dc)
-	{
-		int bottom = bounds.y + bounds.height;
-
-		// Background
-		dc.SetPen(wxPen(colours.Dark()));
-		dc.SetBrush(wxBrush(colours.Dark()));
-		dc.DrawRectangle(bounds);
-
-		// Top line
-		dc.SetPen(wxPen(colours.Light()));
-		dc.DrawLine(bounds.x, bottom-1, bounds.x+bounds.width, bottom-1);
-
-		// Prepare for writing text
-		dc.SetTextBackground(colours.Dark());
-		dc.SetTextForeground(colours.Light());
-
-		// Figure out the first scale mark to show
-		int ms_left = int(pixel_left * ms_per_pixel);
-		int next_scale_mark = int(ms_left / scale_minor_divisor);
-		if (next_scale_mark * scale_minor_divisor < ms_left)
-			next_scale_mark += 1;
-		assert(next_scale_mark * scale_minor_divisor >= ms_left);
-
-		// Draw scale marks
-		int next_scale_mark_pos;
-		int last_text_right = -1;
-		int last_hour = -1, last_minute = -1;
-		if (duration < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
-		do {
-			next_scale_mark_pos = int(next_scale_mark * scale_minor_divisor / ms_per_pixel) - pixel_left;
-			bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
-
-			if (mark_is_major)
-				dc.DrawLine(next_scale_mark_pos, bottom-6, next_scale_mark_pos, bottom-1);
-			else
-				dc.DrawLine(next_scale_mark_pos, bottom-4, next_scale_mark_pos, bottom-1);
-
-			// Print time labels on major scale marks
-			if (mark_is_major && next_scale_mark_pos > last_text_right)
-			{
-				double mark_time = next_scale_mark * scale_minor_divisor / 1000.0;
-				int mark_hour = (int)(mark_time / 3600);
-				int mark_minute = (int)(mark_time / 60) % 60;
-				double mark_second = mark_time - mark_hour*3600.0 - mark_minute*60.0;
-
-				wxString time_string;
-				bool changed_hour = mark_hour != last_hour;
-				bool changed_minute = mark_minute != last_minute;
-
-				if (changed_hour)
-				{
-					time_string = fmt_wx("%d:%02d:", mark_hour, mark_minute);
-					last_hour = mark_hour;
-					last_minute = mark_minute;
-				}
-				else if (changed_minute)
-				{
-					time_string = fmt_wx("%d:", mark_minute);
-					last_minute = mark_minute;
-				}
-				if (scale_minor >= Sc_Decisecond)
-					time_string += fmt_wx("%02d", mark_second);
-				else if (scale_minor == Sc_Centisecond)
-					time_string += fmt_wx("%02.1f", mark_second);
-				else
-					time_string += fmt_wx("%02.2f", mark_second);
-
-				int tw, th;
-				dc.GetTextExtent(time_string, &tw, &th);
-				last_text_right = next_scale_mark_pos + tw;
-
-				dc.DrawText(time_string, next_scale_mark_pos, 0);
-			}
-
-			next_scale_mark += 1;
-
-		} while (next_scale_mark_pos < bounds.width);
-	}
+    AudioDisplayTimeline(AudioDisplay *display)
+        : display(display) {
+        int width, height;
+        display->GetTextExtent("0123456789:.", &width, &height);
+        bounds.height = height + 4;
+    }
+
+    void SetColourScheme(std::string const &name) {
+        colours.SetColourScheme(name);
+    }
+
+    void SetDisplaySize(const wxSize &display_size) {
+        // The size is without anything that goes below the timeline (like scrollbar)
+        bounds.width = display_size.x;
+        bounds.x = 0;
+        bounds.y = 0;
+    }
+
+    int GetHeight() const { return bounds.height; }
+    const wxRect &GetBounds() const { return bounds; }
+
+    void ChangeAudio(int new_duration) {
+        duration = new_duration;
+    }
+
+    void ChangeZoom(double new_ms_per_pixel) {
+        ms_per_pixel = new_ms_per_pixel;
+
+        double px_sec = 1000.0 / ms_per_pixel;
+
+        if (px_sec > 3000) {
+            scale_minor = Sc_Millisecond;
+            scale_minor_divisor = 1.0;
+            scale_major_modulo = 10;
+        }
+        else if (px_sec > 300) {
+            scale_minor = Sc_Centisecond;
+            scale_minor_divisor = 10.0;
+            scale_major_modulo = 10;
+        }
+        else if (px_sec > 30) {
+            scale_minor = Sc_Decisecond;
+            scale_minor_divisor = 100.0;
+            scale_major_modulo = 10;
+        }
+        else if (px_sec > 3) {
+            scale_minor = Sc_Second;
+            scale_minor_divisor = 1000.0;
+            scale_major_modulo = 10;
+        }
+        else if (px_sec > 1.0 / 3.0) {
+            scale_minor = Sc_Decasecond;
+            scale_minor_divisor = 10000.0;
+            scale_major_modulo = 6;
+        }
+        else if (px_sec > 1.0 / 9.0) {
+            scale_minor = Sc_Minute;
+            scale_minor_divisor = 60000.0;
+            scale_major_modulo = 10;
+        }
+        else if (px_sec > 1.0 / 90.0) {
+            scale_minor = Sc_Decaminute;
+            scale_minor_divisor = 600000.0;
+            scale_major_modulo = 6;
+        }
+        else {
+            scale_minor = Sc_Hour;
+            scale_minor_divisor = 3600000.0;
+            scale_major_modulo = 10;
+        }
+    }
+
+    void SetPosition(int new_pixel_left) {
+        pixel_left = std::max(new_pixel_left, 0);
+    }
+
+    bool OnMouseEvent(wxMouseEvent &event) override {
+        if (event.LeftDown()) {
+            drag_lastpos = event.GetPosition();
+            dragging = true;
+        }
+        else if (event.LeftIsDown()) {
+            display->ScrollPixelToLeft(pixel_left - event.GetPosition().x + drag_lastpos.x);
+
+            drag_lastpos = event.GetPosition();
+            dragging = true;
+        }
+        else if (event.LeftUp()) {
+            dragging = false;
+        }
+
+        return dragging;
+    }
+
+    void Paint(wxDC &dc) {
+        int bottom = bounds.y + bounds.height;
+
+        // Background
+        dc.SetPen(wxPen(colours.Dark()));
+        dc.SetBrush(wxBrush(colours.Dark()));
+        dc.DrawRectangle(bounds);
+
+        // Top line
+        dc.SetPen(wxPen(colours.Light()));
+        dc.DrawLine(bounds.x, bottom - 1, bounds.x + bounds.width, bottom - 1);
+
+        // Prepare for writing text
+        dc.SetTextBackground(colours.Dark());
+        dc.SetTextForeground(colours.Light());
+
+        // Figure out the first scale mark to show
+        int ms_left = int(pixel_left * ms_per_pixel);
+        int next_scale_mark = int(ms_left / scale_minor_divisor);
+        if (next_scale_mark * scale_minor_divisor < ms_left)
+            next_scale_mark += 1;
+        assert(next_scale_mark * scale_minor_divisor >= ms_left);
+
+        // Draw scale marks
+        int next_scale_mark_pos;
+        int last_text_right = -1;
+        int last_hour = -1, last_minute = -1;
+        if (duration < 3600) last_hour = 0; // Trick to only show hours if audio is longer than 1 hour
+        do {
+            next_scale_mark_pos = int(next_scale_mark * scale_minor_divisor / ms_per_pixel) - pixel_left;
+            bool mark_is_major = next_scale_mark % scale_major_modulo == 0;
+
+            if (mark_is_major)
+                dc.DrawLine(next_scale_mark_pos, bottom - 6, next_scale_mark_pos, bottom - 1);
+            else
+                dc.DrawLine(next_scale_mark_pos, bottom - 4, next_scale_mark_pos, bottom - 1);
+
+            // Print time labels on major scale marks
+            if (mark_is_major && next_scale_mark_pos > last_text_right) {
+                double mark_time = next_scale_mark * scale_minor_divisor / 1000.0;
+                int mark_hour = (int)(mark_time / 3600);
+                int mark_minute = (int)(mark_time / 60) % 60;
+                double mark_second = mark_time - mark_hour * 3600.0 - mark_minute * 60.0;
+
+                wxString time_string;
+                bool changed_hour = mark_hour != last_hour;
+                bool changed_minute = mark_minute != last_minute;
+
+                if (changed_hour) {
+                    time_string = fmt_wx("%d:%02d:", mark_hour, mark_minute);
+                    last_hour = mark_hour;
+                    last_minute = mark_minute;
+                }
+                else if (changed_minute) {
+                    time_string = fmt_wx("%d:", mark_minute);
+                    last_minute = mark_minute;
+                }
+                if (scale_minor >= Sc_Decisecond)
+                    time_string += fmt_wx("%02d", mark_second);
+                else if (scale_minor == Sc_Centisecond)
+                    time_string += fmt_wx("%02.1f", mark_second);
+                else
+                    time_string += fmt_wx("%02.2f", mark_second);
+
+                int tw, th;
+                dc.GetTextExtent(time_string, &tw, &th);
+                last_text_right = next_scale_mark_pos + tw;
+
+                dc.DrawText(time_string, next_scale_mark_pos, 0);
+            }
+
+            next_scale_mark += 1;
+
+        } while (next_scale_mark_pos < bounds.width);
+    }
 };
 
 class AudioStyleRangeMerger final : public AudioRenderingStyleRanges {
-	typedef std::map<int, AudioRenderingStyle> style_map;
+    typedef std::map<int, AudioRenderingStyle> style_map;
 public:
-	typedef style_map::iterator iterator;
+    typedef style_map::iterator iterator;
 
 private:
-	style_map points;
-
-	void Split(int point)
-	{
-		auto it = points.lower_bound(point);
-		if (it == points.end() || it->first != point)
-		{
-			assert(it != points.begin());
-			points[point] = (--it)->second;
-		}
-	}
-
-	void Restyle(int start, int end, AudioRenderingStyle style)
-	{
-		assert(points.lower_bound(end) != points.end());
-		for (auto pt = points.lower_bound(start); pt->first < end; ++pt)
-		{
-			if (style > pt->second)
-				pt->second = style;
-		}
-	}
+    style_map points;
+
+    void Split(int point) {
+        auto it = points.lower_bound(point);
+        if (it == points.end() || it->first != point) {
+            assert(it != points.begin());
+            points[point] = (--it)->second;
+        }
+    }
+
+    void Restyle(int start, int end, AudioRenderingStyle style) {
+        assert(points.lower_bound(end) != points.end());
+        for (auto pt = points.lower_bound(start); pt->first < end; ++pt) {
+            if (style > pt->second)
+                pt->second = style;
+        }
+    }
 
 public:
-	AudioStyleRangeMerger()
-	{
-		points[0] = AudioStyle_Normal;
-	}
+    AudioStyleRangeMerger() {
+        points[0] = AudioStyle_Normal;
+    }
 
-	void AddRange(int start, int end, AudioRenderingStyle style) override
-	{
+    void AddRange(int start, int end, AudioRenderingStyle style) override {
 
-		if (start < 0) start = 0;
-		if (end < start) return;
+        if (start < 0) start = 0;
+        if (end < start) return;
 
-		Split(start);
-		Split(end);
-		Restyle(start, end, style);
-	}
+        Split(start);
+        Split(end);
+        Restyle(start, end, style);
+    }
 
-	iterator begin() { return points.begin(); }
-	iterator end() { return points.end(); }
+    iterator begin() { return points.begin(); }
+    iterator end() { return points.end(); }
 };
 
 }
 
 class AudioMarkerInteractionObject final : public AudioDisplayInteractionObject {
-	// Object-pair being interacted with
-	std::vector<AudioMarker*> markers;
-	AudioTimingController *timing_controller;
-	// Audio display drag is happening on
-	AudioDisplay *display;
-	// Mouse button used to initiate the drag
-	wxMouseButton button_used;
-	// Default to snapping to snappable markers
-	bool default_snap = OPT_GET("Audio/Snap/Enable")->GetBool();
-	// Range in pixels to snap at
-	int snap_range = OPT_GET("Audio/Snap/Distance")->GetInt();
+    // Object-pair being interacted with
+    std::vector<AudioMarker *> markers;
+    AudioTimingController *timing_controller;
+    // Audio display drag is happening on
+    AudioDisplay *display;
+    // Mouse button used to initiate the drag
+    wxMouseButton button_used;
+    // Default to snapping to snappable markers
+    bool default_snap = OPT_GET("Audio/Snap/Enable")->GetBool();
+    // Range in pixels to snap at
+    int snap_range = OPT_GET("Audio/Snap/Distance")->GetInt();
 
 public:
-	AudioMarkerInteractionObject(std::vector<AudioMarker*> markers, AudioTimingController *timing_controller, AudioDisplay *display, wxMouseButton button_used)
-	: markers(std::move(markers))
-	, timing_controller(timing_controller)
-	, display(display)
-	, button_used(button_used)
-	{
-	}
-
-	bool OnMouseEvent(wxMouseEvent &event) override
-	{
-		if (event.Dragging())
-		{
-			timing_controller->OnMarkerDrag(
-				markers,
-				display->TimeFromRelativeX(event.GetPosition().x),
-				default_snap != event.ShiftDown() ? display->TimeFromAbsoluteX(snap_range) : 0);
-		}
-
-		// We lose the marker drag if the button used to initiate it goes up
-		return !event.ButtonUp(button_used);
-	}
-
-	/// Get the position in milliseconds of this group of markers
-	int GetPosition() const { return markers.front()->GetPosition(); }
+    AudioMarkerInteractionObject(std::vector<AudioMarker *> markers, AudioTimingController *timing_controller, AudioDisplay *display, wxMouseButton button_used)
+        : markers(std::move(markers))
+        , timing_controller(timing_controller)
+        , display(display)
+        , button_used(button_used) {
+    }
+
+    bool OnMouseEvent(wxMouseEvent &event) override {
+        if (event.Dragging()) {
+            timing_controller->OnMarkerDrag(
+                markers,
+                display->TimeFromRelativeX(event.GetPosition().x),
+                default_snap != event.ShiftDown() ? display->TimeFromAbsoluteX(snap_range) : 0);
+        }
+
+        // We lose the marker drag if the button used to initiate it goes up
+        return !event.ButtonUp(button_used);
+    }
+
+    /// Get the position in milliseconds of this group of markers
+    int GetPosition() const { return markers.front()->GetPosition(); }
 };
 
 AudioDisplay::AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context)
-: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS|wxBORDER_SIMPLE)
-, audio_open_connection(context->project->AddAudioProviderListener(&AudioDisplay::OnAudioOpen, this))
-, context(context)
-, audio_renderer(agi::make_unique<AudioRenderer>())
-, controller(controller)
-, scrollbar(agi::make_unique<AudioDisplayScrollbar>(this))
-, timeline(agi::make_unique<AudioDisplayTimeline>(this))
-, style_ranges({{0, 0}})
+    : wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_SIMPLE)
+    , audio_open_connection(context->project->AddAudioProviderListener(&AudioDisplay::OnAudioOpen, this))
+    , context(context)
+    , audio_renderer(agi::make_unique<AudioRenderer>())
+    , controller(controller)
+    , scrollbar(agi::make_unique<AudioDisplayScrollbar>(this))
+    , timeline(agi::make_unique<AudioDisplayTimeline>(this))
+    , style_ranges({{0, 0}})
 {
-	audio_renderer->SetAmplitudeScale(scale_amplitude);
-	SetZoomLevel(0);
-
-	SetMinClientSize(wxSize(-1, 70));
-	SetBackgroundStyle(wxBG_STYLE_PAINT);
-	SetThemeEnabled(false);
-
-	Bind(wxEVT_LEFT_DOWN, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_MIDDLE_DOWN, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_RIGHT_DOWN, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_LEFT_UP, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_MIDDLE_UP, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_RIGHT_UP, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_MOTION, &AudioDisplay::OnMouseEvent, this);
-	Bind(wxEVT_ENTER_WINDOW, &AudioDisplay::OnMouseEnter, this);
-	Bind(wxEVT_LEAVE_WINDOW, &AudioDisplay::OnMouseLeave, this);
-	Bind(wxEVT_PAINT, &AudioDisplay::OnPaint, this);
-	Bind(wxEVT_SIZE, &AudioDisplay::OnSize, this);
-	Bind(wxEVT_KILL_FOCUS, &AudioDisplay::OnFocus, this);
-	Bind(wxEVT_SET_FOCUS, &AudioDisplay::OnFocus, this);
-	Bind(wxEVT_CHAR_HOOK, &AudioDisplay::OnKeyDown, this);
-	Bind(wxEVT_KEY_DOWN, &AudioDisplay::OnKeyDown, this);
-	scroll_timer.Bind(wxEVT_TIMER, &AudioDisplay::OnScrollTimer, this);
-	load_timer.Bind(wxEVT_TIMER, &AudioDisplay::OnLoadTimer, this);
+    audio_renderer->SetAmplitudeScale(scale_amplitude);
+    SetZoomLevel(0);
+
+    SetMinClientSize(wxSize(-1, 70));
+    SetBackgroundStyle(wxBG_STYLE_PAINT);
+    SetThemeEnabled(false);
+
+    Bind(wxEVT_LEFT_DOWN, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_MIDDLE_DOWN, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_RIGHT_DOWN, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_LEFT_UP, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_MIDDLE_UP, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_RIGHT_UP, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_MOTION, &AudioDisplay::OnMouseEvent, this);
+    Bind(wxEVT_ENTER_WINDOW, &AudioDisplay::OnMouseEnter, this);
+    Bind(wxEVT_LEAVE_WINDOW, &AudioDisplay::OnMouseLeave, this);
+    Bind(wxEVT_PAINT, &AudioDisplay::OnPaint, this);
+    Bind(wxEVT_SIZE, &AudioDisplay::OnSize, this);
+    Bind(wxEVT_KILL_FOCUS, &AudioDisplay::OnFocus, this);
+    Bind(wxEVT_SET_FOCUS, &AudioDisplay::OnFocus, this);
+    Bind(wxEVT_CHAR_HOOK, &AudioDisplay::OnKeyDown, this);
+    Bind(wxEVT_KEY_DOWN, &AudioDisplay::OnKeyDown, this);
+    scroll_timer.Bind(wxEVT_TIMER, &AudioDisplay::OnScrollTimer, this);
+    load_timer.Bind(wxEVT_TIMER, &AudioDisplay::OnLoadTimer, this);
 }
 
 AudioDisplay::~AudioDisplay()
@@ -611,760 +581,717 @@ AudioDisplay::~AudioDisplay()
 
 void AudioDisplay::ScrollBy(int pixel_amount)
 {
-	ScrollPixelToLeft(scroll_left + pixel_amount);
+    ScrollPixelToLeft(scroll_left + pixel_amount);
 }
 
 void AudioDisplay::ScrollPixelToLeft(int pixel_position)
 {
-	const int client_width = GetClientRect().GetWidth();
+    const int client_width = GetClientRect().GetWidth();
 
-	if (pixel_position + client_width >= pixel_audio_width)
-		pixel_position = pixel_audio_width - client_width;
-	if (pixel_position < 0)
-		pixel_position = 0;
+    if (pixel_position + client_width >= pixel_audio_width)
+        pixel_position = pixel_audio_width - client_width;
+    if (pixel_position < 0)
+        pixel_position = 0;
 
-	scroll_left = pixel_position;
-	scrollbar->SetPosition(scroll_left);
-	timeline->SetPosition(scroll_left);
-	Refresh();
+    scroll_left = pixel_position;
+    scrollbar->SetPosition(scroll_left);
+    timeline->SetPosition(scroll_left);
+    Refresh();
 }
 
 void AudioDisplay::ScrollTimeRangeInView(const TimeRange &range)
 {
-	int client_width = GetClientRect().GetWidth();
-	int range_begin = AbsoluteXFromTime(range.begin());
-	int range_end = AbsoluteXFromTime(range.end());
-	int range_len = range_end - range_begin;
-
-	// Remove 5 % from each side of the client area.
-	int leftadjust = client_width / 20;
-	int client_left = scroll_left + leftadjust;
-	client_width = client_width * 9 / 10;
-
-	// Is everything already in view?
-	if (range_begin >= client_left && range_end <= client_left+client_width)
-		return;
-
-	// The entire range can fit inside the view, center it
-	if (range_len < client_width)
-	{
-		ScrollPixelToLeft(range_begin - (client_width-range_len)/2 - leftadjust);
-	}
-
-	// Range doesn't fit in view and we're viewing a middle part of it, just leave it alone
-	else if (range_begin < client_left && range_end > client_left+client_width)
-	{
-		// nothing
-	}
-
-	// Right edge is in view, scroll it as far to the right as possible
-	else if (range_end >= client_left && range_end < client_left+client_width)
-	{
-		ScrollPixelToLeft(range_end - client_width - leftadjust);
-	}
-
-	// Nothing is in view or the left edge is in view, scroll left edge as far to the left as possible
-	else
-	{
-		ScrollPixelToLeft(range_begin - leftadjust);
-	}
+    int client_width = GetClientRect().GetWidth();
+    int range_begin = AbsoluteXFromTime(range.begin());
+    int range_end = AbsoluteXFromTime(range.end());
+    int range_len = range_end - range_begin;
+
+    // Remove 5 % from each side of the client area.
+    int leftadjust = client_width / 20;
+    int client_left = scroll_left + leftadjust;
+    client_width = client_width * 9 / 10;
+
+    // Is everything already in view?
+    if (range_begin >= client_left && range_end <= client_left + client_width)
+        return;
+
+    // The entire range can fit inside the view, center it
+    if (range_len < client_width) {
+        ScrollPixelToLeft(range_begin - (client_width - range_len) / 2 - leftadjust);
+    }
+
+    // Range doesn't fit in view and we're viewing a middle part of it, just leave it alone
+    else if (range_begin < client_left && range_end > client_left + client_width) {
+        // nothing
+    }
+
+    // Right edge is in view, scroll it as far to the right as possible
+    else if (range_end >= client_left && range_end < client_left + client_width) {
+        ScrollPixelToLeft(range_end - client_width - leftadjust);
+    }
+
+    // Nothing is in view or the left edge is in view, scroll left edge as far to the left as possible
+    else {
+        ScrollPixelToLeft(range_begin - leftadjust);
+    }
 }
 
 void AudioDisplay::SetZoomLevel(int new_zoom_level)
 {
-	zoom_level = new_zoom_level;
+    zoom_level = new_zoom_level;
 
-	const int factor = GetZoomLevelFactor(zoom_level);
-	const int base_pixels_per_second = 50; /// @todo Make this customisable
-	const double base_ms_per_pixel = 1000.0 / base_pixels_per_second;
-	const double new_ms_per_pixel = 100.0 * base_ms_per_pixel / factor;
+    const int factor = GetZoomLevelFactor(zoom_level);
+    const int base_pixels_per_second = 50; /// @todo Make this customisable
+    const double base_ms_per_pixel = 1000.0 / base_pixels_per_second;
+    const double new_ms_per_pixel = 100.0 * base_ms_per_pixel / factor;
 
-	if (ms_per_pixel == new_ms_per_pixel) return;
+    if (ms_per_pixel == new_ms_per_pixel) return;
 
-	int client_width = GetClientSize().GetWidth();
-	double cursor_pos = track_cursor_pos >= 0 ? track_cursor_pos - scroll_left : client_width / 2.0;
-	double cursor_time = (scroll_left + cursor_pos) * ms_per_pixel;
+    int client_width = GetClientSize().GetWidth();
+    double cursor_pos = track_cursor_pos >= 0 ? track_cursor_pos - scroll_left : client_width / 2.0;
+    double cursor_time = (scroll_left + cursor_pos) * ms_per_pixel;
 
-	ms_per_pixel = new_ms_per_pixel;
-	pixel_audio_width = std::max(1, int(GetDuration() / ms_per_pixel));
+    ms_per_pixel = new_ms_per_pixel;
+    pixel_audio_width = std::max(1, int(GetDuration() / ms_per_pixel));
 
-	audio_renderer->SetMillisecondsPerPixel(ms_per_pixel);
-	scrollbar->ChangeLengths(pixel_audio_width, client_width);
-	timeline->ChangeZoom(ms_per_pixel);
+    audio_renderer->SetMillisecondsPerPixel(ms_per_pixel);
+    scrollbar->ChangeLengths(pixel_audio_width, client_width);
+    timeline->ChangeZoom(ms_per_pixel);
 
-	ScrollPixelToLeft(AbsoluteXFromTime(cursor_time) - cursor_pos);
-	if (track_cursor_pos >= 0)
-		track_cursor_pos = AbsoluteXFromTime(cursor_time);
-	Refresh();
+    ScrollPixelToLeft(AbsoluteXFromTime(cursor_time) - cursor_pos);
+    if (track_cursor_pos >= 0)
+        track_cursor_pos = AbsoluteXFromTime(cursor_time);
+    Refresh();
 }
 
 wxString AudioDisplay::GetZoomLevelDescription(int level) const
 {
-	const int factor = GetZoomLevelFactor(level);
-	const int base_pixels_per_second = 50; /// @todo Make this customisable along with the above
-	const int second_pixels = 100 * base_pixels_per_second / factor;
+    const int factor = GetZoomLevelFactor(level);
+    const int base_pixels_per_second = 50; /// @todo Make this customisable along with the above
+    const int second_pixels = 100 * base_pixels_per_second / factor;
 
-	return fmt_tl("%d%%, %d pixel/second", factor, second_pixels);
+    return fmt_tl("%d%%, %d pixel/second", factor, second_pixels);
 }
 
 int AudioDisplay::GetZoomLevelFactor(int level)
 {
-	int factor = 100;
-
-	if (level > 0)
-	{
-		factor += 25 * level;
-	}
-	else if (level < 0)
-	{
-		if (level >= -5)
-			factor += 10 * level;
-		else if (level >= -11)
-			factor = 50 + (level+5) * 5;
-		else
-			factor = 20 + level + 11;
-		if (factor <= 0)
-			factor = 1;
-	}
-
-	return factor;
+    int factor = 100;
+
+    if (level > 0) {
+        factor += 25 * level;
+    }
+    else if (level < 0) {
+        if (level >= -5)
+            factor += 10 * level;
+        else if (level >= -11)
+            factor = 50 + (level + 5) * 5;
+        else
+            factor = 20 + level + 11;
+        if (factor <= 0)
+            factor = 1;
+    }
+
+    return factor;
 }
 
 void AudioDisplay::SetAmplitudeScale(float scale)
 {
-	audio_renderer->SetAmplitudeScale(scale);
-	Refresh();
+    audio_renderer->SetAmplitudeScale(scale);
+    Refresh();
 }
 
 void AudioDisplay::ReloadRenderingSettings()
 {
-	std::string colour_scheme_name;
+    std::string colour_scheme_name;
 
-	if (OPT_GET("Audio/Spectrum")->GetBool())
-	{
-		colour_scheme_name = OPT_GET("Colour/Audio Display/Spectrum")->GetString();
-		auto audio_spectrum_renderer = agi::make_unique<AudioSpectrumRenderer>(colour_scheme_name);
+    if (OPT_GET("Audio/Spectrum")->GetBool()) {
+        colour_scheme_name = OPT_GET("Colour/Audio Display/Spectrum")->GetString();
+        auto audio_spectrum_renderer = agi::make_unique<AudioSpectrumRenderer>(colour_scheme_name);
 
-		int64_t spectrum_quality = OPT_GET("Audio/Renderer/Spectrum/Quality")->GetInt();
+        int64_t spectrum_quality = OPT_GET("Audio/Renderer/Spectrum/Quality")->GetInt();
 #ifdef WITH_FFTW3
-		// FFTW is so fast we can afford to upgrade quality by two levels
-		spectrum_quality += 2;
+        // FFTW is so fast we can afford to upgrade quality by two levels
+        spectrum_quality += 2;
 #endif
-		spectrum_quality = mid<int64_t>(0, spectrum_quality, 5);
+        spectrum_quality = mid<int64_t>(0, spectrum_quality, 5);
 
-		// Quality indexes:        0  1  2  3   4   5
-		int spectrum_width[]    = {8, 9, 9, 9, 10, 11};
-		int spectrum_distance[] = {8, 8, 7, 6,  6,  5};
+        // Quality indexes:        0  1  2  3   4   5
+        int spectrum_width[]    = {8, 9, 9, 9, 10, 11};
+        int spectrum_distance[] = {8, 8, 7, 6,  6,  5};
 
-		audio_spectrum_renderer->SetResolution(
-			spectrum_width[spectrum_quality],
-			spectrum_distance[spectrum_quality]);
+        audio_spectrum_renderer->SetResolution(
+            spectrum_width[spectrum_quality],
+            spectrum_distance[spectrum_quality]);
 
-		audio_renderer_provider = std::move(audio_spectrum_renderer);
-	}
-	else
-	{
-		colour_scheme_name = OPT_GET("Colour/Audio Display/Waveform")->GetString();
-		audio_renderer_provider = agi::make_unique<AudioWaveformRenderer>(colour_scheme_name);
-	}
+        audio_renderer_provider = std::move(audio_spectrum_renderer);
+    }
+    else {
+        colour_scheme_name = OPT_GET("Colour/Audio Display/Waveform")->GetString();
+        audio_renderer_provider = agi::make_unique<AudioWaveformRenderer>(colour_scheme_name);
+    }
 
-	audio_renderer->SetRenderer(audio_renderer_provider.get());
-	scrollbar->SetColourScheme(colour_scheme_name);
-	timeline->SetColourScheme(colour_scheme_name);
+    audio_renderer->SetRenderer(audio_renderer_provider.get());
+    scrollbar->SetColourScheme(colour_scheme_name);
+    timeline->SetColourScheme(colour_scheme_name);
 
-	Refresh();
+    Refresh();
 }
 
-void AudioDisplay::OnLoadTimer(wxTimerEvent&)
+void AudioDisplay::OnLoadTimer(wxTimerEvent &)
 {
-	using namespace std::chrono;
-	if (provider)
-	{
-		const auto now = steady_clock::now();
-		const auto elapsed = duration_cast<milliseconds>(now - audio_load_start_time).count();
-		if (elapsed == 0) return;
-
-		const int64_t new_decoded_count = provider->GetDecodedSamples();
-		if (new_decoded_count != last_sample_decoded)
-			audio_load_speed = (audio_load_speed + (double)new_decoded_count / elapsed) / 2;
-		if (audio_load_speed == 0) return;
-
-		int new_pos = AbsoluteXFromTime(elapsed * audio_load_speed * 1000.0 / provider->GetSampleRate());
-		if (new_pos > audio_load_position)
-			audio_load_position = new_pos;
-
-		const double left = last_sample_decoded * 1000.0 / provider->GetSampleRate() / ms_per_pixel;
-		const double right = new_decoded_count * 1000.0 / provider->GetSampleRate() / ms_per_pixel;
-
-		if (left < scroll_left + pixel_audio_width && right >= scroll_left)
-			Refresh();
-		else
-			RefreshRect(scrollbar->GetBounds());
-		last_sample_decoded = new_decoded_count;
-	}
-
-	if (!provider || last_sample_decoded == provider->GetNumSamples()) {
-		load_timer.Stop();
-		audio_load_position = -1;
-	}
+    using namespace std::chrono;
+    if (provider) {
+        const auto now = steady_clock::now();
+        const auto elapsed = duration_cast<milliseconds>(now - audio_load_start_time).count();
+        if (elapsed == 0) return;
+
+        const int64_t new_decoded_count = provider->GetDecodedSamples();
+        if (new_decoded_count != last_sample_decoded)
+            audio_load_speed = (audio_load_speed + (double)new_decoded_count / elapsed) / 2;
+        if (audio_load_speed == 0) return;
+
+        int new_pos = AbsoluteXFromTime(elapsed * audio_load_speed * 1000.0 / provider->GetSampleRate());
+        if (new_pos > audio_load_position)
+            audio_load_position = new_pos;
+
+        const double left = last_sample_decoded * 1000.0 / provider->GetSampleRate() / ms_per_pixel;
+        const double right = new_decoded_count * 1000.0 / provider->GetSampleRate() / ms_per_pixel;
+
+        if (left < scroll_left + pixel_audio_width && right >= scroll_left)
+            Refresh();
+        else
+            RefreshRect(scrollbar->GetBounds());
+        last_sample_decoded = new_decoded_count;
+    }
+
+    if (!provider || last_sample_decoded == provider->GetNumSamples()) {
+        load_timer.Stop();
+        audio_load_position = -1;
+    }
 }
 
-void AudioDisplay::OnPaint(wxPaintEvent&)
+void AudioDisplay::OnPaint(wxPaintEvent &)
 {
-	if (!audio_renderer_provider || !provider) return;
+    if (!audio_renderer_provider || !provider) return;
 
-	wxAutoBufferedPaintDC dc(this);
+    wxAutoBufferedPaintDC dc(this);
 
-	wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height);
-	bool redraw_scrollbar = false;
-	bool redraw_timeline = false;
+    wxRect audio_bounds(0, audio_top, GetClientSize().GetWidth(), audio_height);
+    bool redraw_scrollbar = false;
+    bool redraw_timeline = false;
 
-	for (wxRegionIterator region(GetUpdateRegion()); region; ++region)
-	{
-		wxRect updrect = region.GetRect();
+    for (wxRegionIterator region(GetUpdateRegion()); region; ++region) {
+        wxRect updrect = region.GetRect();
 
-		redraw_scrollbar |= scrollbar->GetBounds().Intersects(updrect);
-		redraw_timeline |= timeline->GetBounds().Intersects(updrect);
+        redraw_scrollbar |= scrollbar->GetBounds().Intersects(updrect);
+        redraw_timeline |= timeline->GetBounds().Intersects(updrect);
 
-		if (audio_bounds.Intersects(updrect))
-		{
-			TimeRange updtime(
-				std::max(0, TimeFromRelativeX(updrect.x - foot_size)),
-				std::max(0, TimeFromRelativeX(updrect.x + updrect.width + foot_size)));
+        if (audio_bounds.Intersects(updrect)) {
+            TimeRange updtime(
+                std::max(0, TimeFromRelativeX(updrect.x - foot_size)),
+                std::max(0, TimeFromRelativeX(updrect.x + updrect.width + foot_size)));
 
-			PaintAudio(dc, updtime, updrect);
-			PaintMarkers(dc, updtime);
-			PaintLabels(dc, updtime);
-		}
-	}
+            PaintAudio(dc, updtime, updrect);
+            PaintMarkers(dc, updtime);
+            PaintLabels(dc, updtime);
+        }
+    }
 
-	if (track_cursor_pos >= 0)
-		PaintTrackCursor(dc);
+    if (track_cursor_pos >= 0)
+        PaintTrackCursor(dc);
 
-	if (redraw_scrollbar)
-		scrollbar->Paint(dc, HasFocus(), audio_load_position);
-	if (redraw_timeline)
-		timeline->Paint(dc);
+    if (redraw_scrollbar)
+        scrollbar->Paint(dc, HasFocus(), audio_load_position);
+    if (redraw_timeline)
+        timeline->Paint(dc);
 }
 
 void AudioDisplay::PaintAudio(wxDC &dc, const TimeRange updtime, const wxRect updrect)
 {
-	auto pt = begin(style_ranges), pe = end(style_ranges);
-	while (pt != pe && pt + 1 != pe && (pt + 1)->first < updtime.begin()) ++pt;
-
-	while (pt != pe && pt->first < updtime.end())
-	{
-		const auto range_style = static_cast<AudioRenderingStyle>(pt->second);
-		const int range_x1 = std::max(updrect.x, RelativeXFromTime(pt->first));
-		int range_x2 = updrect.x + updrect.width;
-		if (++pt != pe)
-			range_x2 = std::min(range_x2, RelativeXFromTime(pt->first));
-
-		if (range_x2 > range_x1)
-			audio_renderer->Render(dc, wxPoint(range_x1, audio_top),
-				range_x1 + scroll_left, range_x2 - range_x1, range_style);
-	}
+    auto pt = begin(style_ranges), pe = end(style_ranges);
+    while (pt != pe && pt + 1 != pe && (pt + 1)->first < updtime.begin()) ++pt;
+
+    while (pt != pe && pt->first < updtime.end()) {
+        const auto range_style = static_cast<AudioRenderingStyle>(pt->second);
+        const int range_x1 = std::max(updrect.x, RelativeXFromTime(pt->first));
+        int range_x2 = updrect.x + updrect.width;
+        if (++pt != pe)
+            range_x2 = std::min(range_x2, RelativeXFromTime(pt->first));
+
+        if (range_x2 > range_x1)
+            audio_renderer->Render(dc, wxPoint(range_x1, audio_top),
+                                   range_x1 + scroll_left, range_x2 - range_x1, range_style);
+    }
 }
 
 void AudioDisplay::PaintMarkers(wxDC &dc, TimeRange updtime)
 {
-	AudioMarkerVector markers;
-	controller->GetTimingController()->GetMarkers(updtime, markers);
-	if (markers.empty()) return;
+    AudioMarkerVector markers;
+    controller->GetTimingController()->GetMarkers(updtime, markers);
+    if (markers.empty()) return;
 
-	wxDCPenChanger pen_retainer(dc, wxPen());
-	wxDCBrushChanger brush_retainer(dc, wxBrush());
-	for (const auto marker : markers)
-	{
-		int marker_x = RelativeXFromTime(marker->GetPosition());
+    wxDCPenChanger pen_retainer(dc, wxPen());
+    wxDCBrushChanger brush_retainer(dc, wxBrush());
+    for (const auto marker : markers) {
+        int marker_x = RelativeXFromTime(marker->GetPosition());
 
-		dc.SetPen(marker->GetStyle());
-		dc.DrawLine(marker_x, audio_top, marker_x, audio_top+audio_height);
+        dc.SetPen(marker->GetStyle());
+        dc.DrawLine(marker_x, audio_top, marker_x, audio_top + audio_height);
 
-		if (marker->GetFeet() == AudioMarker::Feet_None) continue;
+        if (marker->GetFeet() == AudioMarker::Feet_None) continue;
 
-		dc.SetBrush(wxBrush(marker->GetStyle().GetColour()));
-		dc.SetPen(*wxTRANSPARENT_PEN);
+        dc.SetBrush(wxBrush(marker->GetStyle().GetColour()));
+        dc.SetPen(*wxTRANSPARENT_PEN);
 
-		if (marker->GetFeet() & AudioMarker::Feet_Left)
-			PaintFoot(dc, marker_x, -1);
-		if (marker->GetFeet() & AudioMarker::Feet_Right)
-			PaintFoot(dc, marker_x, 1);
-	}
+        if (marker->GetFeet() & AudioMarker::Feet_Left)
+            PaintFoot(dc, marker_x, -1);
+        if (marker->GetFeet() & AudioMarker::Feet_Right)
+            PaintFoot(dc, marker_x, 1);
+    }
 
 
-	if (OPT_GET("Timing/Tap To Time")->GetBool()) {
-		dc.SetBrush(wxBrush(*wxGREEN));
-		dc.SetPen(*wxTRANSPARENT_PEN);
-		int marker_x = RelativeXFromTime(controller->GetTimingController()->GetTapMarkerPosition());
-		PaintTapMarker(dc, marker_x);
-	}
+    if (OPT_GET("Timing/Tap To Time")->GetBool()) {
+        dc.SetBrush(wxBrush(*wxGREEN));
+        dc.SetPen(*wxTRANSPARENT_PEN);
+        int marker_x = RelativeXFromTime(controller->GetTimingController()->GetTapMarkerPosition());
+        PaintTapMarker(dc, marker_x);
+    }
 }
 
 void AudioDisplay::PaintFoot(wxDC &dc, int marker_x, int dir)
 {
-	wxPoint foot_top[3] = { wxPoint(foot_size * dir, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
-	wxPoint foot_bot[3] = { wxPoint(foot_size * dir, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
-	dc.DrawPolygon(3, foot_top, marker_x, audio_top);
-	dc.DrawPolygon(3, foot_bot, marker_x, audio_top+audio_height);
+    wxPoint foot_top[3] = { wxPoint(foot_size * dir, 0), wxPoint(0, 0), wxPoint(0, foot_size) };
+    wxPoint foot_bot[3] = { wxPoint(foot_size * dir, 0), wxPoint(0, -foot_size), wxPoint(0, 0) };
+    dc.DrawPolygon(3, foot_top, marker_x, audio_top);
+    dc.DrawPolygon(3, foot_bot, marker_x, audio_top + audio_height);
 }
 
 void AudioDisplay::PaintTapMarker(wxDC &dc, int marker_x)
 {
-	wxPoint arrow[3] = { wxPoint(-foot_size * 2, 0), wxPoint(0, -foot_size * 2), wxPoint(foot_size * 2, 0) };
-	dc.DrawPolygon(3, arrow, marker_x, audio_top+audio_height);
+    wxPoint arrow[3] = { wxPoint(-foot_size * 2, 0), wxPoint(0, -foot_size * 2), wxPoint(foot_size * 2, 0) };
+    dc.DrawPolygon(3, arrow, marker_x, audio_top + audio_height);
 }
 
 void AudioDisplay::PaintLabels(wxDC &dc, TimeRange updtime)
 {
-	std::vector<AudioLabelProvider::AudioLabel> labels;
-	controller->GetTimingController()->GetLabels(updtime, labels);
-	if (labels.empty()) return;
-
-	wxDCFontChanger fc(dc);
-	wxFont font = dc.GetFont();
-	font.SetWeight(wxFONTWEIGHT_BOLD);
-	fc.Set(font);
-	dc.SetTextForeground(*wxWHITE);
-	for (auto const& label : labels)
-	{
-		wxSize extent = dc.GetTextExtent(label.text);
-		int left = RelativeXFromTime(label.range.begin());
-		int width = AbsoluteXFromTime(label.range.length());
-
-		// If it doesn't fit, truncate
-		if (width < extent.GetWidth())
-		{
-			dc.SetClippingRegion(left, audio_top + 4, width, extent.GetHeight());
-			dc.DrawText(label.text, left, audio_top + 4);
-			dc.DestroyClippingRegion();
-		}
-		// Otherwise center in the range
-		else
-		{
-			dc.DrawText(label.text, left + (width - extent.GetWidth()) / 2, audio_top + 4);
-		}
-	}
+    std::vector<AudioLabelProvider::AudioLabel> labels;
+    controller->GetTimingController()->GetLabels(updtime, labels);
+    if (labels.empty()) return;
+
+    wxDCFontChanger fc(dc);
+    wxFont font = dc.GetFont();
+    font.SetWeight(wxFONTWEIGHT_BOLD);
+    fc.Set(font);
+    dc.SetTextForeground(*wxWHITE);
+    for (auto const &label : labels) {
+        wxSize extent = dc.GetTextExtent(label.text);
+        int left = RelativeXFromTime(label.range.begin());
+        int width = AbsoluteXFromTime(label.range.length());
+
+        // If it doesn't fit, truncate
+        if (width < extent.GetWidth()) {
+            dc.SetClippingRegion(left, audio_top + 4, width, extent.GetHeight());
+            dc.DrawText(label.text, left, audio_top + 4);
+            dc.DestroyClippingRegion();
+        }
+        // Otherwise center in the range
+        else {
+            dc.DrawText(label.text, left + (width - extent.GetWidth()) / 2, audio_top + 4);
+        }
+    }
 }
 
-void AudioDisplay::PaintTrackCursor(wxDC &dc) {
-	wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
-	dc.DrawLine(track_cursor_pos-scroll_left, audio_top, track_cursor_pos-scroll_left, audio_top+audio_height);
-
-	if (track_cursor_label.empty()) return;
-
-	wxDCFontChanger fc(dc);
-	wxFont font = dc.GetFont();
-	wxString face_name = FontFace("Audio/Track Cursor");
-	if (!face_name.empty())
-		font.SetFaceName(face_name);
-	font.SetWeight(wxFONTWEIGHT_BOLD);
-	fc.Set(font);
-
-	wxSize label_size(dc.GetTextExtent(track_cursor_label));
-	wxPoint label_pos(track_cursor_pos - scroll_left - label_size.x/2, audio_top + 2);
-	label_pos.x = mid(2, label_pos.x, GetClientSize().GetWidth() - label_size.x - 2);
-
-	int old_bg_mode = dc.GetBackgroundMode();
-	dc.SetBackgroundMode(wxTRANSPARENT);
-
-	// Draw border
-	dc.SetTextForeground(wxColour(64, 64, 64));
-	dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y+1);
-	dc.DrawText(track_cursor_label, label_pos.x+1, label_pos.y-1);
-	dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y+1);
-	dc.DrawText(track_cursor_label, label_pos.x-1, label_pos.y-1);
-
-	// Draw fill
-	dc.SetTextForeground(*wxWHITE);
-	dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
-	dc.SetBackgroundMode(old_bg_mode);
-
-	label_pos.x -= 2;
-	label_pos.y -= 2;
-	label_size.IncBy(4, 4);
-	// If the rendered text changes size we have to draw it an extra time to make sure the entire thing was drawn
-	bool need_extra_redraw = track_cursor_label_rect.GetSize() != label_size;
-	track_cursor_label_rect.SetPosition(label_pos);
-	track_cursor_label_rect.SetSize(label_size);
-	if (need_extra_redraw)
-		RefreshRect(track_cursor_label_rect, false);
+void AudioDisplay::PaintTrackCursor(wxDC &dc)
+{
+    wxDCPenChanger penchanger(dc, wxPen(*wxWHITE));
+    dc.DrawLine(track_cursor_pos - scroll_left, audio_top, track_cursor_pos - scroll_left, audio_top + audio_height);
+
+    if (track_cursor_label.empty()) return;
+
+    wxDCFontChanger fc(dc);
+    wxFont font = dc.GetFont();
+    wxString face_name = FontFace("Audio/Track Cursor");
+    if (!face_name.empty())
+        font.SetFaceName(face_name);
+    font.SetWeight(wxFONTWEIGHT_BOLD);
+    fc.Set(font);
+
+    wxSize label_size(dc.GetTextExtent(track_cursor_label));
+    wxPoint label_pos(track_cursor_pos - scroll_left - label_size.x / 2, audio_top + 2);
+    label_pos.x = mid(2, label_pos.x, GetClientSize().GetWidth() - label_size.x - 2);
+
+    int old_bg_mode = dc.GetBackgroundMode();
+    dc.SetBackgroundMode(wxTRANSPARENT);
+
+    // Draw border
+    dc.SetTextForeground(wxColour(64, 64, 64));
+    dc.DrawText(track_cursor_label, label_pos.x + 1, label_pos.y + 1);
+    dc.DrawText(track_cursor_label, label_pos.x + 1, label_pos.y - 1);
+    dc.DrawText(track_cursor_label, label_pos.x - 1, label_pos.y + 1);
+    dc.DrawText(track_cursor_label, label_pos.x - 1, label_pos.y - 1);
+
+    // Draw fill
+    dc.SetTextForeground(*wxWHITE);
+    dc.DrawText(track_cursor_label, label_pos.x, label_pos.y);
+    dc.SetBackgroundMode(old_bg_mode);
+
+    label_pos.x -= 2;
+    label_pos.y -= 2;
+    label_size.IncBy(4, 4);
+    // If the rendered text changes size we have to draw it an extra time to make sure the entire thing was drawn
+    bool need_extra_redraw = track_cursor_label_rect.GetSize() != label_size;
+    track_cursor_label_rect.SetPosition(label_pos);
+    track_cursor_label_rect.SetSize(label_size);
+    if (need_extra_redraw)
+        RefreshRect(track_cursor_label_rect, false);
 }
 
 void AudioDisplay::SetDraggedObject(AudioDisplayInteractionObject *new_obj)
 {
-	dragged_object = new_obj;
+    dragged_object = new_obj;
 
-	if (dragged_object && !HasCapture())
-		CaptureMouse();
-	else if (!dragged_object && HasCapture())
-		ReleaseMouse();
+    if (dragged_object && !HasCapture())
+        CaptureMouse();
+    else if (!dragged_object && HasCapture())
+        ReleaseMouse();
 
-	if (!dragged_object)
-		audio_marker.reset();
+    if (!dragged_object)
+        audio_marker.reset();
 }
 
 void AudioDisplay::SetTrackCursor(int new_pos, bool show_time)
 {
-	if (new_pos == track_cursor_pos) return;
-
-	int old_pos = track_cursor_pos;
-	track_cursor_pos = new_pos;
-
-	RefreshRect(wxRect(old_pos - scroll_left - 1, audio_top, 2, audio_height - 1), false);
-	RefreshRect(wxRect(new_pos - scroll_left - 1, audio_top, 2, audio_height - 1), false);
-
-	// Make sure the old label gets cleared away
-	RefreshRect(track_cursor_label_rect, false);
-
-	if (show_time)
-	{
-		agi::Time new_label_time = TimeFromAbsoluteX(track_cursor_pos);
-		track_cursor_label = to_wx(new_label_time.GetAssFormatted());
-		track_cursor_label_rect.x += new_pos - old_pos;
-		RefreshRect(track_cursor_label_rect, false);
-	}
-	else
-	{
-		track_cursor_label_rect.SetSize(wxSize(0,0));
-		track_cursor_label.Clear();
-	}
+    if (new_pos == track_cursor_pos) return;
+
+    int old_pos = track_cursor_pos;
+    track_cursor_pos = new_pos;
+
+    RefreshRect(wxRect(old_pos - scroll_left - 1, audio_top, 2, audio_height - 1), false);
+    RefreshRect(wxRect(new_pos - scroll_left - 1, audio_top, 2, audio_height - 1), false);
+
+    // Make sure the old label gets cleared away
+    RefreshRect(track_cursor_label_rect, false);
+
+    if (show_time) {
+        agi::Time new_label_time = TimeFromAbsoluteX(track_cursor_pos);
+        track_cursor_label = to_wx(new_label_time.GetAssFormatted());
+        track_cursor_label_rect.x += new_pos - old_pos;
+        RefreshRect(track_cursor_label_rect, false);
+    }
+    else {
+        track_cursor_label_rect.SetSize(wxSize(0, 0));
+        track_cursor_label.Clear();
+    }
 }
 
 void AudioDisplay::RemoveTrackCursor()
 {
-	SetTrackCursor(-1, false);
+    SetTrackCursor(-1, false);
 }
 
-void AudioDisplay::OnMouseEnter(wxMouseEvent&)
+void AudioDisplay::OnMouseEnter(wxMouseEvent &)
 {
-	if (OPT_GET("Audio/Auto/Focus")->GetBool())
-		SetFocus();
+    if (OPT_GET("Audio/Auto/Focus")->GetBool())
+        SetFocus();
 }
 
-void AudioDisplay::OnMouseLeave(wxMouseEvent&)
+void AudioDisplay::OnMouseLeave(wxMouseEvent &)
 {
-	if (!controller->IsPlaying())
-		RemoveTrackCursor();
+    if (!controller->IsPlaying())
+        RemoveTrackCursor();
 }
 
-void AudioDisplay::OnMouseEvent(wxMouseEvent& event)
+void AudioDisplay::OnMouseEvent(wxMouseEvent &event)
 {
-	// If we have focus, we get mouse move events on Mac even when the mouse is
-	// outside our client rectangle, we don't want those.
-	if (event.Moving() && !GetClientRect().Contains(event.GetPosition()))
-	{
-		event.Skip();
-		return;
-	}
-
-	if (event.IsButton())
-		SetFocus();
-
-	const int mouse_x = event.GetPosition().x;
-
-	// Scroll the display after a mouse-up near one of the edges
-	if ((event.LeftUp() || event.RightUp()) && OPT_GET("Audio/Auto/Scroll")->GetBool())
-	{
-		const int width = GetClientSize().GetWidth();
-		if (mouse_x < width / 20) {
-			ScrollBy(-width / 3);
-		}
-		else if (width - mouse_x < width / 20) {
-			ScrollBy(width / 3);
-		}
-	}
-
-	if (ForwardMouseEvent(event))
-		return;
-
-	if (event.MiddleIsDown())
-	{
-		context->videoController->JumpToTime(TimeFromRelativeX(mouse_x), agi::vfr::EXACT);
-		return;
-	}
-
-	if (event.Moving() && !controller->IsPlaying())
-	{
-		SetTrackCursor(scroll_left + mouse_x, OPT_GET("Audio/Display/Draw/Cursor Time")->GetBool());
-	}
-
-	AudioTimingController *timing = controller->GetTimingController();
-	if (!timing) return;
-	const int drag_sensitivity = int(OPT_GET("Audio/Start Drag Sensitivity")->GetInt() * ms_per_pixel);
-	const int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? int(OPT_GET("Audio/Snap/Distance")->GetInt() * ms_per_pixel) : 0;
-
-	// Not scrollbar, not timeline, no button action
-	if (event.Moving())
-	{
-		const int timepos = TimeFromRelativeX(mouse_x);
-
-		if (timing->IsNearbyMarker(timepos, drag_sensitivity, event.AltDown()))
-			SetCursor(wxCursor(wxCURSOR_SIZEWE));
-		else
-			SetCursor(wxNullCursor);
-		return;
-	}
-
-	const int old_scroll_pos = scroll_left;
-	if (event.LeftDown() || event.RightDown())
-	{
-		const int timepos = TimeFromRelativeX(mouse_x);
-		std::vector<AudioMarker*> markers = event.LeftDown()
-			? timing->OnLeftClick(timepos, event.CmdDown(), event.AltDown(), drag_sensitivity, snap_sensitivity)
-			: timing->OnRightClick(timepos, event.CmdDown(), drag_sensitivity, snap_sensitivity);
-
-		// Clicking should never result in the audio display scrolling
-		ScrollPixelToLeft(old_scroll_pos);
-
-		if (markers.size())
-		{
-			RemoveTrackCursor();
-			audio_marker = agi::make_unique<AudioMarkerInteractionObject>(markers, timing, this, (wxMouseButton)event.GetButton());
-			SetDraggedObject(audio_marker.get());
-			return;
-		}
-	}
+    // If we have focus, we get mouse move events on Mac even when the mouse is
+    // outside our client rectangle, we don't want those.
+    if (event.Moving() && !GetClientRect().Contains(event.GetPosition())) {
+        event.Skip();
+        return;
+    }
+
+    if (event.IsButton())
+        SetFocus();
+
+    const int mouse_x = event.GetPosition().x;
+
+    // Scroll the display after a mouse-up near one of the edges
+    if ((event.LeftUp() || event.RightUp()) && OPT_GET("Audio/Auto/Scroll")->GetBool()) {
+        const int width = GetClientSize().GetWidth();
+        if (mouse_x < width / 20) {
+            ScrollBy(-width / 3);
+        }
+        else if (width - mouse_x < width / 20) {
+            ScrollBy(width / 3);
+        }
+    }
+
+    if (ForwardMouseEvent(event))
+        return;
+
+    if (event.MiddleIsDown()) {
+        context->videoController->JumpToTime(TimeFromRelativeX(mouse_x), agi::vfr::EXACT);
+        return;
+    }
+
+    if (event.Moving() && !controller->IsPlaying()) {
+        SetTrackCursor(scroll_left + mouse_x, OPT_GET("Audio/Display/Draw/Cursor Time")->GetBool());
+    }
+
+    AudioTimingController *timing = controller->GetTimingController();
+    if (!timing) return;
+    const int drag_sensitivity = int(OPT_GET("Audio/Start Drag Sensitivity")->GetInt() * ms_per_pixel);
+    const int snap_sensitivity = OPT_GET("Audio/Snap/Enable")->GetBool() != event.ShiftDown() ? int(OPT_GET("Audio/Snap/Distance")->GetInt() * ms_per_pixel) : 0;
+
+    // Not scrollbar, not timeline, no button action
+    if (event.Moving()) {
+        const int timepos = TimeFromRelativeX(mouse_x);
+
+        if (timing->IsNearbyMarker(timepos, drag_sensitivity, event.AltDown()))
+            SetCursor(wxCursor(wxCURSOR_SIZEWE));
+        else
+            SetCursor(wxNullCursor);
+        return;
+    }
+
+    const int old_scroll_pos = scroll_left;
+    if (event.LeftDown() || event.RightDown()) {
+        const int timepos = TimeFromRelativeX(mouse_x);
+        std::vector<AudioMarker *> markers = event.LeftDown()
+                                             ? timing->OnLeftClick(timepos, event.CmdDown(), event.AltDown(), drag_sensitivity, snap_sensitivity)
+                                             : timing->OnRightClick(timepos, event.CmdDown(), drag_sensitivity, snap_sensitivity);
+
+        // Clicking should never result in the audio display scrolling
+        ScrollPixelToLeft(old_scroll_pos);
+
+        if (markers.size()) {
+            RemoveTrackCursor();
+            audio_marker = agi::make_unique<AudioMarkerInteractionObject>(markers, timing, this, (wxMouseButton)event.GetButton());
+            SetDraggedObject(audio_marker.get());
+            return;
+        }
+    }
 }
 
-bool AudioDisplay::ForwardMouseEvent(wxMouseEvent &event) {
-	// Handle any ongoing drag
-	if (dragged_object && HasCapture())
-	{
-		if (!dragged_object->OnMouseEvent(event))
-		{
-			scroll_timer.Stop();
-			SetDraggedObject(nullptr);
-			SetCursor(wxNullCursor);
-		}
-		return true;
-	}
-	else
-	{
-		// Something is wrong, we might have lost capture somehow.
-		// Fix state and pretend it didn't happen.
-		SetDraggedObject(nullptr);
-		SetCursor(wxNullCursor);
-	}
-
-	const wxPoint mousepos = event.GetPosition();
-	AudioDisplayInteractionObject *new_obj = nullptr;
-	// Check for scrollbar action
-	if (scrollbar->GetBounds().Contains(mousepos))
-	{
-		new_obj = scrollbar.get();
-	}
-	// Check for timeline action
-	else if (timeline->GetBounds().Contains(mousepos))
-	{
-		SetCursor(wxCursor(wxCURSOR_SIZEWE));
-		new_obj = timeline.get();
-	}
-	else
-	{
-		return false;
-	}
-
-	if (!controller->IsPlaying())
-		RemoveTrackCursor();
-	if (new_obj->OnMouseEvent(event))
-		SetDraggedObject(new_obj);
-	return true;
+bool AudioDisplay::ForwardMouseEvent(wxMouseEvent &event)
+{
+    // Handle any ongoing drag
+    if (dragged_object && HasCapture()) {
+        if (!dragged_object->OnMouseEvent(event)) {
+            scroll_timer.Stop();
+            SetDraggedObject(nullptr);
+            SetCursor(wxNullCursor);
+        }
+        return true;
+    }
+    else {
+        // Something is wrong, we might have lost capture somehow.
+        // Fix state and pretend it didn't happen.
+        SetDraggedObject(nullptr);
+        SetCursor(wxNullCursor);
+    }
+
+    const wxPoint mousepos = event.GetPosition();
+    AudioDisplayInteractionObject *new_obj = nullptr;
+    // Check for scrollbar action
+    if (scrollbar->GetBounds().Contains(mousepos)) {
+        new_obj = scrollbar.get();
+    }
+    // Check for timeline action
+    else if (timeline->GetBounds().Contains(mousepos)) {
+        SetCursor(wxCursor(wxCURSOR_SIZEWE));
+        new_obj = timeline.get();
+    }
+    else {
+        return false;
+    }
+
+    if (!controller->IsPlaying())
+        RemoveTrackCursor();
+    if (new_obj->OnMouseEvent(event))
+        SetDraggedObject(new_obj);
+    return true;
 }
 
-void AudioDisplay::OnKeyDown(wxKeyEvent& event)
+void AudioDisplay::OnKeyDown(wxKeyEvent &event)
 {
-	hotkey::check("Audio", context, event);
+    hotkey::check("Audio", context, event);
 }
 
 void AudioDisplay::OnSize(wxSizeEvent &)
 {
-	// We changed size, update the sub-controls' internal data and redraw
-	wxSize size = GetClientSize();
+    // We changed size, update the sub-controls' internal data and redraw
+    wxSize size = GetClientSize();
 
-	timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
-	scrollbar->SetDisplaySize(size);
+    timeline->SetDisplaySize(wxSize(size.x, scrollbar->GetBounds().y));
+    scrollbar->SetDisplaySize(size);
 
-	if (controller->GetTimingController())
-	{
-		TimeRange sel(controller->GetTimingController()->GetPrimaryPlaybackRange());
-		scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
-	}
+    if (controller->GetTimingController()) {
+        TimeRange sel(controller->GetTimingController()->GetPrimaryPlaybackRange());
+        scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
+    }
 
-	audio_height = size.GetHeight();
-	audio_height -= scrollbar->GetBounds().GetHeight();
-	audio_height -= timeline->GetHeight();
-	audio_renderer->SetHeight(audio_height);
+    audio_height = size.GetHeight();
+    audio_height -= scrollbar->GetBounds().GetHeight();
+    audio_height -= timeline->GetHeight();
+    audio_renderer->SetHeight(audio_height);
 
-	audio_top = timeline->GetHeight();
+    audio_top = timeline->GetHeight();
 
-	Refresh();
+    Refresh();
 }
 
 void AudioDisplay::OnFocus(wxFocusEvent &)
 {
-	// The scrollbar indicates focus so repaint that
-	RefreshRect(scrollbar->GetBounds(), false);
+    // The scrollbar indicates focus so repaint that
+    RefreshRect(scrollbar->GetBounds(), false);
 }
 
 int AudioDisplay::GetDuration() const
 {
-	if (!provider) return 0;
-	return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
+    if (!provider) return 0;
+    return (provider->GetNumSamples() * 1000 + provider->GetSampleRate() - 1) / provider->GetSampleRate();
 }
 
 void AudioDisplay::OnAudioOpen(agi::AudioProvider *provider)
 {
-	this->provider = provider;
-
-	if (!audio_renderer_provider)
-		ReloadRenderingSettings();
-
-	audio_renderer->SetAudioProvider(provider);
-	audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
-
-	timeline->ChangeAudio(GetDuration());
-
-	ms_per_pixel = 0;
-	SetZoomLevel(zoom_level);
-
-	Refresh();
-
-	if (provider)
-	{
-		if (connections.empty())
-		{
-			connections = agi::signal::make_vector({
-				controller->AddPlaybackPositionListener(&AudioDisplay::OnPlaybackPosition, this),
-				controller->AddPlaybackStopListener(&AudioDisplay::RemoveTrackCursor, this),
-				controller->AddTimingControllerListener(&AudioDisplay::OnTimingController, this),
-				OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this),
-				OPT_SUB("Audio/Display/Waveform Style", &AudioDisplay::ReloadRenderingSettings, this),
-				OPT_SUB("Colour/Audio Display/Spectrum", &AudioDisplay::ReloadRenderingSettings, this),
-				OPT_SUB("Colour/Audio Display/Waveform", &AudioDisplay::ReloadRenderingSettings, this),
-				OPT_SUB("Audio/Renderer/Spectrum/Quality", &AudioDisplay::ReloadRenderingSettings, this),
-				OPT_SUB("Timing/Tap To Time", &AudioDisplay::OnTapMarkerChanged, this),
-			});
-			OnTimingController();
-		}
-
-		last_sample_decoded = provider->GetDecodedSamples();
-		audio_load_position = -1;
-		audio_load_speed = 0;
-		audio_load_start_time = std::chrono::steady_clock::now();
-		if (last_sample_decoded != provider->GetNumSamples())
-			load_timer.Start(100);
-	}
-	else
-	{
-		connections.clear();
-	}
+    this->provider = provider;
+
+    if (!audio_renderer_provider)
+        ReloadRenderingSettings();
+
+    audio_renderer->SetAudioProvider(provider);
+    audio_renderer->SetCacheMaxSize(OPT_GET("Audio/Renderer/Spectrum/Memory Max")->GetInt() * 1024 * 1024);
+
+    timeline->ChangeAudio(GetDuration());
+
+    ms_per_pixel = 0;
+    SetZoomLevel(zoom_level);
+
+    Refresh();
+
+    if (provider) {
+        if (connections.empty()) {
+            connections = agi::signal::make_vector({
+                controller->AddPlaybackPositionListener(&AudioDisplay::OnPlaybackPosition, this),
+                controller->AddPlaybackStopListener(&AudioDisplay::RemoveTrackCursor, this),
+                controller->AddTimingControllerListener(&AudioDisplay::OnTimingController, this),
+                OPT_SUB("Audio/Spectrum", &AudioDisplay::ReloadRenderingSettings, this),
+                OPT_SUB("Audio/Display/Waveform Style", &AudioDisplay::ReloadRenderingSettings, this),
+                OPT_SUB("Colour/Audio Display/Spectrum", &AudioDisplay::ReloadRenderingSettings, this),
+                OPT_SUB("Colour/Audio Display/Waveform", &AudioDisplay::ReloadRenderingSettings, this),
+                OPT_SUB("Audio/Renderer/Spectrum/Quality", &AudioDisplay::ReloadRenderingSettings, this),
+                OPT_SUB("Timing/Tap To Time", &AudioDisplay::OnTapMarkerChanged, this),
+            });
+            OnTimingController();
+        }
+
+        last_sample_decoded = provider->GetDecodedSamples();
+        audio_load_position = -1;
+        audio_load_speed = 0;
+        audio_load_start_time = std::chrono::steady_clock::now();
+        if (last_sample_decoded != provider->GetNumSamples())
+            load_timer.Start(100);
+    }
+    else {
+        connections.clear();
+    }
 }
 
 void AudioDisplay::OnTimingController()
 {
-	AudioTimingController *timing_controller = controller->GetTimingController();
-	if (timing_controller)
-	{
-		timing_controller->AddMarkerMovedListener(&AudioDisplay::OnMarkerMoved, this);
-		timing_controller->AddUpdatedPrimaryRangeListener(&AudioDisplay::OnSelectionChanged, this);
-		timing_controller->AddUpdatedStyleRangesListener(&AudioDisplay::OnStyleRangesChanged, this);
-		timing_controller->AddUpdatedTapMarkerListener(&AudioDisplay::OnTapMarkerChanged, this);
-
-		OnStyleRangesChanged();
-		OnMarkerMoved();
-		OnSelectionChanged();
-		OnTapMarkerChanged();
-	}
+    AudioTimingController *timing_controller = controller->GetTimingController();
+    if (timing_controller) {
+        timing_controller->AddMarkerMovedListener(&AudioDisplay::OnMarkerMoved, this);
+        timing_controller->AddUpdatedPrimaryRangeListener(&AudioDisplay::OnSelectionChanged, this);
+        timing_controller->AddUpdatedStyleRangesListener(&AudioDisplay::OnStyleRangesChanged, this);
+        timing_controller->AddUpdatedTapMarkerListener(&AudioDisplay::OnTapMarkerChanged, this);
+
+        OnStyleRangesChanged();
+        OnMarkerMoved();
+        OnSelectionChanged();
+        OnTapMarkerChanged();
+    }
 }
 
 void AudioDisplay::OnPlaybackPosition(int ms)
 {
-	int pixel_position = AbsoluteXFromTime(ms);
-	SetTrackCursor(pixel_position, false);
-
-	if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool())
-	{
-		int client_width = GetClientSize().GetWidth();
-		int edge_size = client_width / 20;
-		if (scroll_left > 0 && pixel_position < scroll_left + edge_size)
-		{
-			ScrollPixelToLeft(std::max(pixel_position - edge_size, 0));
-		}
-		else if (scroll_left + client_width < std::min(pixel_audio_width - 1, pixel_position + edge_size))
-		{
-			ScrollPixelToLeft(std::min(pixel_position - client_width + edge_size, pixel_audio_width - client_width - 1));
-		}
-	}
+    int pixel_position = AbsoluteXFromTime(ms);
+    SetTrackCursor(pixel_position, false);
+
+    if (OPT_GET("Audio/Lock Scroll on Cursor")->GetBool()) {
+        int client_width = GetClientSize().GetWidth();
+        int edge_size = client_width / 20;
+        if (scroll_left > 0 && pixel_position < scroll_left + edge_size) {
+            ScrollPixelToLeft(std::max(pixel_position - edge_size, 0));
+        }
+        else if (scroll_left + client_width < std::min(pixel_audio_width - 1, pixel_position + edge_size)) {
+            ScrollPixelToLeft(std::min(pixel_position - client_width + edge_size, pixel_audio_width - client_width - 1));
+        }
+    }
 }
 
 void AudioDisplay::OnSelectionChanged()
 {
-	TimeRange sel(controller->GetPrimaryPlaybackRange());
-	scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
-
-	if (audio_marker)
-	{
-		if (!scroll_timer.IsRunning())
-		{
-			// If the dragged object is outside the visible area, start the
-			// scroll timer to shift it back into view
-			int rel_x = RelativeXFromTime(audio_marker->GetPosition());
-			if (rel_x < 0 || rel_x >= GetClientSize().GetWidth())
-			{
-				// 50ms is the default for this on Windows (hardcoded since
-				// wxSystemSettings doesn't expose DragScrollDelay etc.)
-				scroll_timer.Start(50, true);
-			}
-		}
-	}
-	else if (OPT_GET("Audio/Auto/Scroll")->GetBool() && sel.end() != 0)
-	{
-		ScrollTimeRangeInView(sel);
-	}
-
-	RefreshRect(scrollbar->GetBounds(), false);
+    TimeRange sel(controller->GetPrimaryPlaybackRange());
+    scrollbar->SetSelection(AbsoluteXFromTime(sel.begin()), AbsoluteXFromTime(sel.length()));
+
+    if (audio_marker) {
+        if (!scroll_timer.IsRunning()) {
+            // If the dragged object is outside the visible area, start the
+            // scroll timer to shift it back into view
+            int rel_x = RelativeXFromTime(audio_marker->GetPosition());
+            if (rel_x < 0 || rel_x >= GetClientSize().GetWidth()) {
+                // 50ms is the default for this on Windows (hardcoded since
+                // wxSystemSettings doesn't expose DragScrollDelay etc.)
+                scroll_timer.Start(50, true);
+            }
+        }
+    }
+    else if (OPT_GET("Audio/Auto/Scroll")->GetBool() && sel.end() != 0) {
+        ScrollTimeRangeInView(sel);
+    }
+
+    RefreshRect(scrollbar->GetBounds(), false);
 }
 
 void AudioDisplay::OnScrollTimer(wxTimerEvent &event)
 {
-	if (!audio_marker) return;
-
-	int rel_x = RelativeXFromTime(audio_marker->GetPosition());
-	int width = GetClientSize().GetWidth();
-
-	// If the dragged object is outside the visible area, scroll it into
-	// view with a 5% margin
-	if (rel_x < 0)
-	{
-		ScrollBy(rel_x - width / 20);
-	}
-	else if (rel_x >= width)
-	{
-		ScrollBy(rel_x - width + width / 20);
-	}
+    if (!audio_marker) return;
+
+    int rel_x = RelativeXFromTime(audio_marker->GetPosition());
+    int width = GetClientSize().GetWidth();
+
+    // If the dragged object is outside the visible area, scroll it into
+    // view with a 5% margin
+    if (rel_x < 0) {
+        ScrollBy(rel_x - width / 20);
+    }
+    else if (rel_x >= width) {
+        ScrollBy(rel_x - width + width / 20);
+    }
 }
 
 void AudioDisplay::OnStyleRangesChanged()
 {
-	if (!controller->GetTimingController()) return;
+    if (!controller->GetTimingController()) return;
 
-	AudioStyleRangeMerger asrm;
-	controller->GetTimingController()->GetRenderingStyles(asrm);
+    AudioStyleRangeMerger asrm;
+    controller->GetTimingController()->GetRenderingStyles(asrm);
 
-	style_ranges.clear();
-	for (auto pair : asrm) style_ranges.push_back(pair);
+    style_ranges.clear();
+    for (auto pair : asrm) style_ranges.push_back(pair);
 
-	RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
+    RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
 }
 
 void AudioDisplay::OnTapMarkerChanged()
 {
-	RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
+    RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
 }
 
 
 void AudioDisplay::OnMarkerMoved()
 {
-	RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
+    RefreshRect(wxRect(0, audio_top, GetClientSize().GetWidth(), audio_height), false);
 }
diff --git a/src/audio_display.h b/src/audio_display.h
index 272472d86c814c1a6aadf3a4eb1c52525419b04e..5ded7a55000722b3734a2a7e55041926077f8b61 100644
--- a/src/audio_display.h
+++ b/src/audio_display.h
@@ -49,8 +49,8 @@ class TimeRange;
 
 // Helper classes used in implementation of the audio display
 namespace {
-	class AudioDisplayScrollbar;
-	class AudioDisplayTimeline;
+class AudioDisplayScrollbar;
+class AudioDisplayTimeline;
 }
 class AudioDisplayInteractionObject;
 class AudioMarkerInteractionObject;
@@ -62,240 +62,240 @@ class AudioMarkerInteractionObject;
 /// timing controller. The audio display also renders audio according to the audio controller
 /// and the timing controller, using an audio renderer instance.
 class AudioDisplay: public wxWindow {
-	agi::signal::Connection audio_open_connection;
+    agi::signal::Connection audio_open_connection;
 
-	std::vector<agi::signal::Connection> connections;
-	agi::Context *context;
+    std::vector<agi::signal::Connection> connections;
+    agi::Context *context;
 
-	/// The audio renderer manager
-	std::unique_ptr<AudioRenderer> audio_renderer;
+    /// The audio renderer manager
+    std::unique_ptr<AudioRenderer> audio_renderer;
 
-	/// The current audio renderer
-	std::unique_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
+    /// The current audio renderer
+    std::unique_ptr<AudioRendererBitmapProvider> audio_renderer_provider;
 
-	/// The controller managing us
-	AudioController *controller = nullptr;
+    /// The controller managing us
+    AudioController *controller = nullptr;
 
-	agi::AudioProvider *provider = nullptr;
+    agi::AudioProvider *provider = nullptr;
 
-	/// Scrollbar helper object
-	std::unique_ptr<AudioDisplayScrollbar> scrollbar;
+    /// Scrollbar helper object
+    std::unique_ptr<AudioDisplayScrollbar> scrollbar;
 
-	/// Timeline helper object
-	std::unique_ptr<AudioDisplayTimeline> timeline;
+    /// Timeline helper object
+    std::unique_ptr<AudioDisplayTimeline> timeline;
 
-	/// The interaction object for the last-dragged audio marker
-	std::unique_ptr<AudioMarkerInteractionObject> audio_marker;
+    /// The interaction object for the last-dragged audio marker
+    std::unique_ptr<AudioMarkerInteractionObject> audio_marker;
 
 
-	/// Current object on display being dragged, if any
-	AudioDisplayInteractionObject *dragged_object = nullptr;
-	/// Change the dragged object and update mouse capture
-	void SetDraggedObject(AudioDisplayInteractionObject *new_obj);
+    /// Current object on display being dragged, if any
+    AudioDisplayInteractionObject *dragged_object = nullptr;
+    /// Change the dragged object and update mouse capture
+    void SetDraggedObject(AudioDisplayInteractionObject *new_obj);
 
-
-	/// Timer for scrolling when markers are dragged out of the displayed area
-	wxTimer scroll_timer;
-
-	wxTimer load_timer;
-	int64_t last_sample_decoded = 0;
-	/// Time at which audio loading began, for calculating loading speed
-	std::chrono::steady_clock::time_point audio_load_start_time;
-	/// Estimated speed of audio decoding in samples per ms
-	double audio_load_speed = 0.0;
-	/// Current position of the audio loading progress in absolute pixels
-	int audio_load_position = 0;
-
-	/// Leftmost pixel in the virtual audio image being displayed
-	int scroll_left = 0;
-
-	/// Total width of the audio in pixels
-	int pixel_audio_width = 0;
-
-	/// Horizontal zoom measured in millisecond per pixels
-	double ms_per_pixel = 0.;
-
-	/// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
-	float scale_amplitude = 1.f;
-
-	/// Top of the main audio area in pixels
-	int audio_top = 0;
-
-	/// Height of main audio area in pixels
-	int audio_height = 0;
-
-	/// Width of the audio marker feet in pixels
-	static const int foot_size = 6;
-
-	/// Zoom level given as a number, see SetZoomLevel for details
-	int zoom_level;
-
-	/// Absolute pixel position of the tracking cursor (mouse or playback)
-	int track_cursor_pos = -1;
-	/// Label to show by track cursor
-	wxString track_cursor_label;
-	/// Bounding rectangle last drawn track cursor label
-	wxRect track_cursor_label_rect;
-	/// @brief Move the tracking cursor
-	/// @param new_pos   New absolute pixel position of the tracking cursor
-	/// @param show_time Display timestamp by the tracking cursor?
-	void SetTrackCursor(int new_pos, bool show_time);
-	/// @brief Remove the tracking cursor from the display
-	void RemoveTrackCursor();
-
-	/// Previous style ranges for optimizing redraw when ranges change
-	std::vector<std::pair<int, int>> style_ranges;
-
-	/// @brief Reload all rendering settings from Options and reset caches
-	///
-	/// This can be called if some rendering quality settings have been changed
-	/// in Options and need to be reloaded to take effect.
-	void ReloadRenderingSettings();
-
-	/// Paint the audio data for a time range
-	/// @param dc DC to paint to
-	/// @param updtime Time range to repaint
-	/// @param updrect Pixel range to repaint
-	void PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect);
-
-	/// Paint the markers in a time range
-	/// @param dc DC to paint to
-	/// @param updtime Time range to repaint
-	void PaintMarkers(wxDC &dc, TimeRange updtime);
-
-	/// Draw a single foot for a marker
-	/// @param dc DC to paint to
-	/// @param marker_x Position of the marker whose foot is being painted in pixels
-	/// @param dir -1 for left, 1 for right
-	void PaintFoot(wxDC &dc, int marker_x, int dir);
-
-	/// Draw an indicator for the tap marker
-	/// @param dc DC to paint to
-	/// @param marker_x Position of the tap marker
-	void PaintTapMarker(wxDC &dc, int marker_x);
-
-	/// Paint the labels in a time range
-	/// @param dc DC to paint to
-	/// @param updtime Time range to repaint
-	void PaintLabels(wxDC &dc, TimeRange updtime);
-
-	/// Paint the track cursor
-	/// @param dc DC to paint to
-	void PaintTrackCursor(wxDC &dc);
-
-	/// Forward the mouse event to the appropriate child control, if any
-	/// @return Was the mouse event forwarded somewhere?
-	bool ForwardMouseEvent(wxMouseEvent &event);
-
-	/// wxWidgets paint event
-	void OnPaint(wxPaintEvent &event);
-	/// wxWidgets mouse input event
-	void OnMouseEvent(wxMouseEvent &event);
-	/// wxWidgets control size changed event
-	void OnSize(wxSizeEvent &event);
-	/// wxWidgets input focus changed event
-	void OnFocus(wxFocusEvent &event);
-	/// wxWidgets keypress event
-	void OnKeyDown(wxKeyEvent& event);
-	void OnScrollTimer(wxTimerEvent &event);
-	void OnLoadTimer(wxTimerEvent &);
-	void OnMouseEnter(wxMouseEvent&);
-	void OnMouseLeave(wxMouseEvent&);
-
-	int GetDuration() const;
-
-	void OnAudioOpen(agi::AudioProvider *provider);
-	void OnPlaybackPosition(int ms_position);
-	void OnSelectionChanged();
-	void OnStyleRangesChanged();
-	void OnTimingController();
-	void OnMarkerMoved();
-	void OnTapMarkerChanged();
+
+    /// Timer for scrolling when markers are dragged out of the displayed area
+    wxTimer scroll_timer;
+
+    wxTimer load_timer;
+    int64_t last_sample_decoded = 0;
+    /// Time at which audio loading began, for calculating loading speed
+    std::chrono::steady_clock::time_point audio_load_start_time;
+    /// Estimated speed of audio decoding in samples per ms
+    double audio_load_speed = 0.0;
+    /// Current position of the audio loading progress in absolute pixels
+    int audio_load_position = 0;
+
+    /// Leftmost pixel in the virtual audio image being displayed
+    int scroll_left = 0;
+
+    /// Total width of the audio in pixels
+    int pixel_audio_width = 0;
+
+    /// Horizontal zoom measured in millisecond per pixels
+    double ms_per_pixel = 0.;
+
+    /// Amplitude scaling ("vertical zoom") as a factor, 1.0 is neutral
+    float scale_amplitude = 1.f;
+
+    /// Top of the main audio area in pixels
+    int audio_top = 0;
+
+    /// Height of main audio area in pixels
+    int audio_height = 0;
+
+    /// Width of the audio marker feet in pixels
+    static const int foot_size = 6;
+
+    /// Zoom level given as a number, see SetZoomLevel for details
+    int zoom_level;
+
+    /// Absolute pixel position of the tracking cursor (mouse or playback)
+    int track_cursor_pos = -1;
+    /// Label to show by track cursor
+    wxString track_cursor_label;
+    /// Bounding rectangle last drawn track cursor label
+    wxRect track_cursor_label_rect;
+    /// @brief Move the tracking cursor
+    /// @param new_pos   New absolute pixel position of the tracking cursor
+    /// @param show_time Display timestamp by the tracking cursor?
+    void SetTrackCursor(int new_pos, bool show_time);
+    /// @brief Remove the tracking cursor from the display
+    void RemoveTrackCursor();
+
+    /// Previous style ranges for optimizing redraw when ranges change
+    std::vector<std::pair<int, int>> style_ranges;
+
+    /// @brief Reload all rendering settings from Options and reset caches
+    ///
+    /// This can be called if some rendering quality settings have been changed
+    /// in Options and need to be reloaded to take effect.
+    void ReloadRenderingSettings();
+
+    /// Paint the audio data for a time range
+    /// @param dc DC to paint to
+    /// @param updtime Time range to repaint
+    /// @param updrect Pixel range to repaint
+    void PaintAudio(wxDC &dc, TimeRange updtime, wxRect updrect);
+
+    /// Paint the markers in a time range
+    /// @param dc DC to paint to
+    /// @param updtime Time range to repaint
+    void PaintMarkers(wxDC &dc, TimeRange updtime);
+
+    /// Draw a single foot for a marker
+    /// @param dc DC to paint to
+    /// @param marker_x Position of the marker whose foot is being painted in pixels
+    /// @param dir -1 for left, 1 for right
+    void PaintFoot(wxDC &dc, int marker_x, int dir);
+
+    /// Draw an indicator for the tap marker
+    /// @param dc DC to paint to
+    /// @param marker_x Position of the tap marker
+    void PaintTapMarker(wxDC &dc, int marker_x);
+
+    /// Paint the labels in a time range
+    /// @param dc DC to paint to
+    /// @param updtime Time range to repaint
+    void PaintLabels(wxDC &dc, TimeRange updtime);
+
+    /// Paint the track cursor
+    /// @param dc DC to paint to
+    void PaintTrackCursor(wxDC &dc);
+
+    /// Forward the mouse event to the appropriate child control, if any
+    /// @return Was the mouse event forwarded somewhere?
+    bool ForwardMouseEvent(wxMouseEvent &event);
+
+    /// wxWidgets paint event
+    void OnPaint(wxPaintEvent &event);
+    /// wxWidgets mouse input event
+    void OnMouseEvent(wxMouseEvent &event);
+    /// wxWidgets control size changed event
+    void OnSize(wxSizeEvent &event);
+    /// wxWidgets input focus changed event
+    void OnFocus(wxFocusEvent &event);
+    /// wxWidgets keypress event
+    void OnKeyDown(wxKeyEvent &event);
+    void OnScrollTimer(wxTimerEvent &event);
+    void OnLoadTimer(wxTimerEvent &);
+    void OnMouseEnter(wxMouseEvent &);
+    void OnMouseLeave(wxMouseEvent &);
+
+    int GetDuration() const;
+
+    void OnAudioOpen(agi::AudioProvider *provider);
+    void OnPlaybackPosition(int ms_position);
+    void OnSelectionChanged();
+    void OnStyleRangesChanged();
+    void OnTimingController();
+    void OnMarkerMoved();
+    void OnTapMarkerChanged();
 
 public:
-	AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context);
-	~AudioDisplay();
-
-	/// @brief Scroll the audio display
-	/// @param pixel_amount Number of pixels to scroll the view
-	///
-	/// A positive amount moves the display to the right, making later parts of the audio visible.
-	void ScrollBy(int pixel_amount);
-
-	/// @brief Scroll the audio display
-	/// @param pixel_position Absolute pixel to put at left edge of the audio display
-	void ScrollPixelToLeft(int pixel_position);
-
-	/// @brief Scroll the audio display
-	/// @param range Time range to ensure is in view
-	///
-	/// If the entire range is already visible inside the display, nothing is
-	/// scrolled. If just one of the two endpoints is visible, the display is
-	/// scrolled such that the visible endpoint stays in view but more of the
-	/// rest of the range becomes visible.
-	///
-	/// If the entire range fits inside the display, the display is centered
-	/// over the range.  For this calculation, the display is considered
-	/// smaller by some margins, see below.
-	///
-	/// If the range does not fit within the display with margins subtracted,
-	/// the start of the range is ensured visible and as much of the rest of
-	/// the range is brought into view.
-	///
-	/// For the purpose of this function, a 5 percent margin is assumed at each
-	/// end of the audio display such that a range endpoint that is ensured to
-	/// be in view never gets closer to the edge of the display than the
-	/// margin. The edge that is not ensured to be in view might be outside of
-	/// view or might be closer to the display edge than the
-	/// margin.
-	void ScrollTimeRangeInView(const TimeRange &range);
-
-
-	/// @brief Change the zoom level
-	/// @param new_zoom_level The new zoom level to use
-	///
-	/// A zoom level of 0 is the default zoom level, all other levels are based
-	/// on this. Negative zoom levels zoom out, positive zoom in.
-	///
-	/// The zoom levels generally go from +30 to -30. It is possible to zoom in
-	/// more than +30.
-	void SetZoomLevel(int new_zoom_level);
-
-	/// @brief Get the zoom level
-	/// @return The zoom level
-	///
-	/// See SetZoomLevel for a description of zoom levels.
-	int GetZoomLevel() const { return zoom_level; }
-
-	/// @brief Get a textual description of a zoom level
-	/// @param level The zoom level to describe
-	/// @return A translated string describing a zoom level
-	///
-	/// The zoom level description can tell the user details about how much audio is
-	/// actually displayed.
-	wxString GetZoomLevelDescription(int level) const;
-
-	/// @brief Get the zoom factor in percent for a zoom level
-	/// @param level The zoom level to get the factor of
-	/// @return The zoom factor in percent
-	///
-	/// Positive: 125, 150, 175, 200, 225, ...
-	///
-	/// Negative: 90, 80, 70, 60, 50, 45, 40, 35, 30, 25, 20, 19, 18, 17, ..., 1
-	///
-	/// Too negative numbers get clamped.
-	static int GetZoomLevelFactor(int level);
-
-	/// @brief Set amplitude scale factor
-	/// @param scale New amplitude scale factor, 1.0 is no scaling
-	void SetAmplitudeScale(float scale);
-
-	/// Get a time in milliseconds from an X coordinate relative to current scroll
-	int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
-	/// Get a time in milliseconds from an absolute X coordinate
-	int TimeFromAbsoluteX(int x) const { return int(x * ms_per_pixel); }
-	/// Get an X coordinate relative to the current scroll from a time in milliseconds
-	int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
-	/// Get an absolute X coordinate from a time in milliseconds
-	int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
+    AudioDisplay(wxWindow *parent, AudioController *controller, agi::Context *context);
+    ~AudioDisplay();
+
+    /// @brief Scroll the audio display
+    /// @param pixel_amount Number of pixels to scroll the view
+    ///
+    /// A positive amount moves the display to the right, making later parts of the audio visible.
+    void ScrollBy(int pixel_amount);
+
+    /// @brief Scroll the audio display
+    /// @param pixel_position Absolute pixel to put at left edge of the audio display
+    void ScrollPixelToLeft(int pixel_position);
+
+    /// @brief Scroll the audio display
+    /// @param range Time range to ensure is in view
+    ///
+    /// If the entire range is already visible inside the display, nothing is
+    /// scrolled. If just one of the two endpoints is visible, the display is
+    /// scrolled such that the visible endpoint stays in view but more of the
+    /// rest of the range becomes visible.
+    ///
+    /// If the entire range fits inside the display, the display is centered
+    /// over the range.  For this calculation, the display is considered
+    /// smaller by some margins, see below.
+    ///
+    /// If the range does not fit within the display with margins subtracted,
+    /// the start of the range is ensured visible and as much of the rest of
+    /// the range is brought into view.
+    ///
+    /// For the purpose of this function, a 5 percent margin is assumed at each
+    /// end of the audio display such that a range endpoint that is ensured to
+    /// be in view never gets closer to the edge of the display than the
+    /// margin. The edge that is not ensured to be in view might be outside of
+    /// view or might be closer to the display edge than the
+    /// margin.
+    void ScrollTimeRangeInView(const TimeRange &range);
+
+
+    /// @brief Change the zoom level
+    /// @param new_zoom_level The new zoom level to use
+    ///
+    /// A zoom level of 0 is the default zoom level, all other levels are based
+    /// on this. Negative zoom levels zoom out, positive zoom in.
+    ///
+    /// The zoom levels generally go from +30 to -30. It is possible to zoom in
+    /// more than +30.
+    void SetZoomLevel(int new_zoom_level);
+
+    /// @brief Get the zoom level
+    /// @return The zoom level
+    ///
+    /// See SetZoomLevel for a description of zoom levels.
+    int GetZoomLevel() const { return zoom_level; }
+
+    /// @brief Get a textual description of a zoom level
+    /// @param level The zoom level to describe
+    /// @return A translated string describing a zoom level
+    ///
+    /// The zoom level description can tell the user details about how much audio is
+    /// actually displayed.
+    wxString GetZoomLevelDescription(int level) const;
+
+    /// @brief Get the zoom factor in percent for a zoom level
+    /// @param level The zoom level to get the factor of
+    /// @return The zoom factor in percent
+    ///
+    /// Positive: 125, 150, 175, 200, 225, ...
+    ///
+    /// Negative: 90, 80, 70, 60, 50, 45, 40, 35, 30, 25, 20, 19, 18, 17, ..., 1
+    ///
+    /// Too negative numbers get clamped.
+    static int GetZoomLevelFactor(int level);
+
+    /// @brief Set amplitude scale factor
+    /// @param scale New amplitude scale factor, 1.0 is no scaling
+    void SetAmplitudeScale(float scale);
+
+    /// Get a time in milliseconds from an X coordinate relative to current scroll
+    int TimeFromRelativeX(int x) const { return int((scroll_left + x) * ms_per_pixel); }
+    /// Get a time in milliseconds from an absolute X coordinate
+    int TimeFromAbsoluteX(int x) const { return int(x * ms_per_pixel); }
+    /// Get an X coordinate relative to the current scroll from a time in milliseconds
+    int RelativeXFromTime(int ms) const { return int(ms / ms_per_pixel) - scroll_left; }
+    /// Get an absolute X coordinate from a time in milliseconds
+    int AbsoluteXFromTime(int ms) const { return int(ms / ms_per_pixel); }
 };
diff --git a/src/audio_karaoke.cpp b/src/audio_karaoke.cpp
index 7df1b0dded1857a43d56bd04f2f82f27add80409..c446df5cf5cb858d9e8cf8d484731d411f52dac9 100644
--- a/src/audio_karaoke.cpp
+++ b/src/audio_karaoke.cpp
@@ -50,410 +50,429 @@
 #include <wx/sizer.h>
 
 template<class Container, class Value>
-static inline size_t last_lt_or_eq(Container const& c, Value const& v) {
-	auto it = lower_bound(c.begin(), c.end(), v);
-	// lower_bound gives first >=
-	if (it == c.end() || *it > v)
-		--it;
-	return distance(c.begin(), it);
+static inline size_t last_lt_or_eq(Container const &c, Value const &v)
+{
+    auto it = lower_bound(c.begin(), c.end(), v);
+    // lower_bound gives first >=
+    if (it == c.end() || *it > v)
+        --it;
+    return distance(c.begin(), it);
 }
 
 AudioKaraoke::AudioKaraoke(wxWindow *parent, agi::Context *c)
-: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_SUNKEN)
-, c(c)
-, file_changed(c->ass->AddCommitListener(&AudioKaraoke::OnFileChanged, this))
-, audio_opened(c->project->AddAudioProviderListener(&AudioKaraoke::OnAudioOpened, this))
-, active_line_changed(c->selectionController->AddActiveLineListener(&AudioKaraoke::OnActiveLineChanged, this))
-, tap_to_time_toggled(OPT_SUB("Timing/Tap To Time", &AudioKaraoke::OnTapMarkerChanged, this))
-, kara(agi::make_unique<AssKaraoke>())
+    : wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_SUNKEN)
+    , c(c)
+    , file_changed(c->ass->AddCommitListener(&AudioKaraoke::OnFileChanged, this))
+    , audio_opened(c->project->AddAudioProviderListener(&AudioKaraoke::OnAudioOpened, this))
+    , active_line_changed(c->selectionController->AddActiveLineListener(&AudioKaraoke::OnActiveLineChanged, this))
+    , tap_to_time_toggled(OPT_SUB("Timing/Tap To Time", &AudioKaraoke::OnTapMarkerChanged, this))
+    , kara(agi::make_unique<AssKaraoke>())
 {
-	using std::bind;
-
-	cancel_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_cancel_16));
-	cancel_button->SetToolTip(_("Discard all uncommitted splits"));
-	cancel_button->Bind(wxEVT_BUTTON, bind(&AudioKaraoke::CancelSplit, this));
-
-	accept_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_accept_16));
-	accept_button->SetToolTip(_("Commit splits"));
-	accept_button->Bind(wxEVT_BUTTON, bind(&AudioKaraoke::AcceptSplit, this));
-
-	split_area = new wxPanel(this);
-
-	wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
-	main_sizer->Add(cancel_button);
-	main_sizer->Add(accept_button);
-	main_sizer->Add(split_area, wxSizerFlags(1).Expand());
-	SetSizerAndFit(main_sizer);
-
-	/// @todo subscribe
-	split_font.SetFaceName(FontFace("Audio/Karaoke"));
-	split_font.SetPointSize(OPT_GET("Audio/Karaoke/Font Size")->GetInt());
-
-	split_area->Bind(wxEVT_SIZE, &AudioKaraoke::OnSize, this);
-	split_area->Bind(wxEVT_PAINT, &AudioKaraoke::OnPaint, this);
-	split_area->Bind(wxEVT_LEFT_DOWN, &AudioKaraoke::OnMouse, this);
-	split_area->Bind(wxEVT_LEFT_UP, &AudioKaraoke::OnMouse, this);
-	split_area->Bind(wxEVT_MOTION, &AudioKaraoke::OnMouse, this);
-	split_area->Bind(wxEVT_LEAVE_WINDOW, &AudioKaraoke::OnMouse, this);
-	split_area->Bind(wxEVT_CONTEXT_MENU, &AudioKaraoke::OnContextMenu, this);
-	scroll_timer.Bind(wxEVT_TIMER, &AudioKaraoke::OnScrollTimer, this);
-
-	accept_button->Enable(false);
-	cancel_button->Enable(false);
-	enabled = false;
+    using std::bind;
+
+    cancel_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_cancel_16));
+    cancel_button->SetToolTip(_("Discard all uncommitted splits"));
+    cancel_button->Bind(wxEVT_BUTTON, bind(&AudioKaraoke::CancelSplit, this));
+
+    accept_button = new wxBitmapButton(this, -1, GETIMAGE(kara_split_accept_16));
+    accept_button->SetToolTip(_("Commit splits"));
+    accept_button->Bind(wxEVT_BUTTON, bind(&AudioKaraoke::AcceptSplit, this));
+
+    split_area = new wxPanel(this);
+
+    wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
+    main_sizer->Add(cancel_button);
+    main_sizer->Add(accept_button);
+    main_sizer->Add(split_area, wxSizerFlags(1).Expand());
+    SetSizerAndFit(main_sizer);
+
+    /// @todo subscribe
+    split_font.SetFaceName(FontFace("Audio/Karaoke"));
+    split_font.SetPointSize(OPT_GET("Audio/Karaoke/Font Size")->GetInt());
+
+    split_area->Bind(wxEVT_SIZE, &AudioKaraoke::OnSize, this);
+    split_area->Bind(wxEVT_PAINT, &AudioKaraoke::OnPaint, this);
+    split_area->Bind(wxEVT_LEFT_DOWN, &AudioKaraoke::OnMouse, this);
+    split_area->Bind(wxEVT_LEFT_UP, &AudioKaraoke::OnMouse, this);
+    split_area->Bind(wxEVT_MOTION, &AudioKaraoke::OnMouse, this);
+    split_area->Bind(wxEVT_LEAVE_WINDOW, &AudioKaraoke::OnMouse, this);
+    split_area->Bind(wxEVT_CONTEXT_MENU, &AudioKaraoke::OnContextMenu, this);
+    scroll_timer.Bind(wxEVT_TIMER, &AudioKaraoke::OnScrollTimer, this);
+
+    accept_button->Enable(false);
+    cancel_button->Enable(false);
+    enabled = false;
 }
 
-AudioKaraoke::~AudioKaraoke() {
+AudioKaraoke::~AudioKaraoke()
+{
 }
 
-void AudioKaraoke::OnActiveLineChanged(AssDialogue *new_line) {
-	active_line = new_line;
-	if (enabled) {
-		LoadFromLine();
-		split_area->Refresh(false);
-	}
+void AudioKaraoke::OnActiveLineChanged(AssDialogue *new_line)
+{
+    active_line = new_line;
+    if (enabled) {
+        LoadFromLine();
+        split_area->Refresh(false);
+    }
 }
 
-void AudioKaraoke::OnFileChanged(int type, const AssDialogue *changed) {
-	if (enabled && (type & AssFile::COMMIT_DIAG_FULL) && (!changed || changed == active_line)) {
-		LoadFromLine();
-		split_area->Refresh(false);
-	}
+void AudioKaraoke::OnFileChanged(int type, const AssDialogue *changed)
+{
+    if (enabled && (type & AssFile::COMMIT_DIAG_FULL) && (!changed || changed == active_line)) {
+        LoadFromLine();
+        split_area->Refresh(false);
+    }
 }
 
-void AudioKaraoke::OnAudioOpened(agi::AudioProvider *provider) {
-	if (provider)
-		SetEnabled(enabled);
-	else
-		c->audioController->SetTimingController(nullptr);
+void AudioKaraoke::OnAudioOpened(agi::AudioProvider *provider)
+{
+    if (provider)
+        SetEnabled(enabled);
+    else
+        c->audioController->SetTimingController(nullptr);
 }
 
-void AudioKaraoke::SetEnabled(bool en) {
-	enabled = en;
-
-	c->audioBox->ShowKaraokeBar(enabled);
-	if (enabled) {
-		LoadFromLine();
-		c->audioController->SetTimingController(CreateKaraokeTimingController(c, kara.get(), file_changed));
-		c->audioController->GetTimingController()->AddUpdatedTapMarkerListener(&AudioKaraoke::OnTapMarkerChanged, this);
-		Refresh(false);
-	}
-	else {
-		c->audioController->SetTimingController(CreateDialogueTimingController(c));
-	}
+void AudioKaraoke::SetEnabled(bool en)
+{
+    enabled = en;
+
+    c->audioBox->ShowKaraokeBar(enabled);
+    if (enabled) {
+        LoadFromLine();
+        c->audioController->SetTimingController(CreateKaraokeTimingController(c, kara.get(), file_changed));
+        c->audioController->GetTimingController()->AddUpdatedTapMarkerListener(&AudioKaraoke::OnTapMarkerChanged, this);
+        Refresh(false);
+    }
+    else {
+        c->audioController->SetTimingController(CreateDialogueTimingController(c));
+    }
 }
 
-void AudioKaraoke::OnSize(wxSizeEvent &evt) {
-	RenderText();
-	Refresh(false);
+void AudioKaraoke::OnSize(wxSizeEvent &evt)
+{
+    RenderText();
+    Refresh(false);
 }
 
-void AudioKaraoke::OnPaint(wxPaintEvent &) {
-	int w, h;
-	split_area->GetClientSize(&w, &h);
-
-	wxPaintDC dc(split_area);
-	wxMemoryDC bmp_dc(rendered_line);
-
-	// Draw the text and split lines
-	dc.Blit(-scroll_x, 0, rendered_line.GetWidth(), h, &bmp_dc, 0, 0);
-
-	// Draw the split line under the mouse
-	if (click_will_remove_split)
-		dc.SetPen(*wxRED);
-	else
-		dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
-	dc.DrawLine(mouse_pos, 0, mouse_pos, h);
-
-	dc.SetPen(*wxTRANSPARENT_PEN);
-
-	int width_past_bmp = w + scroll_x - rendered_line.GetWidth();
-	dc.SetBrush(*wxWHITE_BRUSH);
-	if (width_past_bmp > 0)
-		dc.DrawRectangle(w - width_past_bmp, 0, width_past_bmp, h);
-
-	// Draw scroll arrows if needed
-	if (scroll_x > 0) {
-		dc.DrawRectangle(0, 0, 20, h);
-
-		wxPoint triangle[] = {
-			wxPoint(10, h / 2 - 6),
-			wxPoint(4, h / 2),
-			wxPoint(10, h / 2 + 6)
-		};
-		dc.SetBrush(*wxBLACK_BRUSH);
-		dc.DrawPolygon(3, triangle);
-	}
-
-	if (rendered_line.GetWidth() - scroll_x > w) {
-		dc.SetBrush(*wxWHITE_BRUSH);
-		dc.DrawRectangle(w - 20, 0, 20, h);
-
-		wxPoint triangle[] = {
-			wxPoint(w - 10, h / 2 - 6),
-			wxPoint(w - 4, h / 2),
-			wxPoint(w - 10, h / 2 + 6)
-		};
-		dc.SetBrush(*wxBLACK_BRUSH);
-		dc.DrawPolygon(3, triangle);
-	}
+void AudioKaraoke::OnPaint(wxPaintEvent &)
+{
+    int w, h;
+    split_area->GetClientSize(&w, &h);
+
+    wxPaintDC dc(split_area);
+    wxMemoryDC bmp_dc(rendered_line);
+
+    // Draw the text and split lines
+    dc.Blit(-scroll_x, 0, rendered_line.GetWidth(), h, &bmp_dc, 0, 0);
+
+    // Draw the split line under the mouse
+    if (click_will_remove_split)
+        dc.SetPen(*wxRED);
+    else
+        dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+    dc.DrawLine(mouse_pos, 0, mouse_pos, h);
+
+    dc.SetPen(*wxTRANSPARENT_PEN);
+
+    int width_past_bmp = w + scroll_x - rendered_line.GetWidth();
+    dc.SetBrush(*wxWHITE_BRUSH);
+    if (width_past_bmp > 0)
+        dc.DrawRectangle(w - width_past_bmp, 0, width_past_bmp, h);
+
+    // Draw scroll arrows if needed
+    if (scroll_x > 0) {
+        dc.DrawRectangle(0, 0, 20, h);
+
+        wxPoint triangle[] = {
+            wxPoint(10, h / 2 - 6),
+            wxPoint(4, h / 2),
+            wxPoint(10, h / 2 + 6)
+        };
+        dc.SetBrush(*wxBLACK_BRUSH);
+        dc.DrawPolygon(3, triangle);
+    }
+
+    if (rendered_line.GetWidth() - scroll_x > w) {
+        dc.SetBrush(*wxWHITE_BRUSH);
+        dc.DrawRectangle(w - 20, 0, 20, h);
+
+        wxPoint triangle[] = {
+            wxPoint(w - 10, h / 2 - 6),
+            wxPoint(w - 4, h / 2),
+            wxPoint(w - 10, h / 2 + 6)
+        };
+        dc.SetBrush(*wxBLACK_BRUSH);
+        dc.DrawPolygon(3, triangle);
+    }
 }
 
-void AudioKaraoke::RenderText() {
-	wxSize bmp_size = split_area->GetClientSize();
-	int line_width = spaced_text.size() * char_width + 5;
-	if (line_width > bmp_size.GetWidth())
-		bmp_size.SetWidth(line_width);
-
-	if (!rendered_line.IsOk() || bmp_size != rendered_line.GetSize())
-		rendered_line = wxBitmap(bmp_size);
-
-	wxMemoryDC dc(rendered_line);
-
-	// Draw background
-	dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.DrawRectangle(wxPoint(), bmp_size);
-
-	dc.SetFont(split_font);
-	dc.SetTextForeground(wxColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)));
-
-	// Draw each character in the line
-	int y = (bmp_size.GetHeight() - char_height) / 2;
-	for (size_t i = 0; i < spaced_text.size(); ++i) {
-		if (!(tap_syl_start <= i && i < tap_syl_end)) {
-			// Only draw with normal color if _not_ the tap syllable
-			dc.DrawText(spaced_text[i], char_x[i], y);
-		}
-	}
-
-	// Draw marked syllable
-	dc.SetTextForeground(*wxGREEN);
-	for (size_t i = tap_syl_start; i < tap_syl_end; ++i)
-		dc.DrawText(spaced_text[i], char_x[i], y);
-
-	// Draw the lines between each syllable
-	dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
-	for (auto syl_line : syl_lines)
-		dc.DrawLine(syl_line, 0, syl_line, bmp_size.GetHeight());
+void AudioKaraoke::RenderText()
+{
+    wxSize bmp_size = split_area->GetClientSize();
+    int line_width = spaced_text.size() * char_width + 5;
+    if (line_width > bmp_size.GetWidth())
+        bmp_size.SetWidth(line_width);
+
+    if (!rendered_line.IsOk() || bmp_size != rendered_line.GetSize())
+        rendered_line = wxBitmap(bmp_size);
+
+    wxMemoryDC dc(rendered_line);
+
+    // Draw background
+    dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.DrawRectangle(wxPoint(), bmp_size);
+
+    dc.SetFont(split_font);
+    dc.SetTextForeground(wxColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)));
+
+    // Draw each character in the line
+    int y = (bmp_size.GetHeight() - char_height) / 2;
+    for (size_t i = 0; i < spaced_text.size(); ++i) {
+        if (!(tap_syl_start <= i && i < tap_syl_end)) {
+            // Only draw with normal color if _not_ the tap syllable
+            dc.DrawText(spaced_text[i], char_x[i], y);
+        }
+    }
+
+    // Draw marked syllable
+    dc.SetTextForeground(*wxGREEN);
+    for (size_t i = tap_syl_start; i < tap_syl_end; ++i)
+        dc.DrawText(spaced_text[i], char_x[i], y);
+
+    // Draw the lines between each syllable
+    dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+    for (auto syl_line : syl_lines)
+        dc.DrawLine(syl_line, 0, syl_line, bmp_size.GetHeight());
 }
 
-void AudioKaraoke::AddMenuItem(wxMenu &menu, std::string const& tag, wxString const& help, std::string const& selected) {
-	wxMenuItem *item = menu.AppendCheckItem(-1, to_wx(tag), help);
-	menu.Bind(wxEVT_MENU, std::bind(&AudioKaraoke::SetTagType, this, tag), item->GetId());
-	item->Check(tag == selected);
+void AudioKaraoke::AddMenuItem(wxMenu &menu, std::string const &tag, wxString const &help, std::string const &selected)
+{
+    wxMenuItem *item = menu.AppendCheckItem(-1, to_wx(tag), help);
+    menu.Bind(wxEVT_MENU, std::bind(&AudioKaraoke::SetTagType, this, tag), item->GetId());
+    item->Check(tag == selected);
 }
 
-void AudioKaraoke::OnContextMenu(wxContextMenuEvent&) {
-	if (!enabled) return;
+void AudioKaraoke::OnContextMenu(wxContextMenuEvent &)
+{
+    if (!enabled) return;
 
-	wxMenu context_menu(_("Karaoke tag"));
-	std::string type = kara->GetTagType();
+    wxMenu context_menu(_("Karaoke tag"));
+    std::string type = kara->GetTagType();
 
-	AddMenuItem(context_menu, "\\k", _("Change karaoke tag to \\k"), type);
-	AddMenuItem(context_menu, "\\kf", _("Change karaoke tag to \\kf"), type);
-	AddMenuItem(context_menu, "\\ko", _("Change karaoke tag to \\ko"), type);
+    AddMenuItem(context_menu, "\\k", _("Change karaoke tag to \\k"), type);
+    AddMenuItem(context_menu, "\\kf", _("Change karaoke tag to \\kf"), type);
+    AddMenuItem(context_menu, "\\ko", _("Change karaoke tag to \\ko"), type);
 
-	PopupMenu(&context_menu);
+    PopupMenu(&context_menu);
 }
 
-void AudioKaraoke::OnMouse(wxMouseEvent &event) {
-	if (!enabled) return;
-
-	mouse_pos = event.GetX();
-
-	if (event.Leaving()) {
-		mouse_pos = -1;
-		split_area->Refresh(false);
-		return;
-	}
-
-	if (scroll_timer.IsRunning() || split_area->HasCapture()) {
-		if (event.LeftUp()) {
-			scroll_timer.Stop();
-			split_area->ReleaseMouse();
-		}
-		return;
-	}
-
-	// Check if the mouse is over a scroll arrow
-	int client_width = split_area->GetClientSize().GetWidth();
-	if (scroll_x > 0 && mouse_pos < 20)
-		scroll_dir = -1;
-	else if (scroll_x + client_width < rendered_line.GetWidth() && mouse_pos > client_width - 20)
-		scroll_dir = 1;
-	else
-		scroll_dir = 0;
-
-	if (scroll_dir) {
-		mouse_pos = -1;
-		if (event.LeftDown()) {
-			split_area->Refresh(false);
-			scroll_timer.Start(50);
-			split_area->CaptureMouse();
-			wxTimerEvent evt;
-			OnScrollTimer(evt);
-		}
-		return;
-	}
-
-	int shifted_pos = mouse_pos + scroll_x;
-
-	// Character to insert the new split point before
-	int split_pos = std::min<int>((shifted_pos + char_width / 2) / char_width, spaced_text.size());
-
-	// Syllable this character is in
-	int syl = last_lt_or_eq(syl_start_points, split_pos);
-
-	// If the click is sufficiently close to a line of a syllable split,
-	// remove that split rather than adding a new one
-	bool click_right = (syl > 0 && shifted_pos <= syl_lines[syl - 1] + 3);
-	bool click_left = (syl < (int)syl_lines.size() && shifted_pos >= syl_lines[syl] - 3);
-	click_will_remove_split = click_left || click_right;
-
-	if (!event.LeftDown()) {
-		// Erase the old line and draw the new one
-		split_area->Refresh(false);
-		return;
-	}
-
-	if (click_will_remove_split)
-		kara->RemoveSplit(syl + (click_left && !click_right));
-	else
-		kara->AddSplit(syl, char_to_byte[split_pos] - 1);
-
-	SetDisplayText();
-	accept_button->Enable(true);
-	cancel_button->Enable(true);
-	split_area->Refresh(false);
+void AudioKaraoke::OnMouse(wxMouseEvent &event)
+{
+    if (!enabled) return;
+
+    mouse_pos = event.GetX();
+
+    if (event.Leaving()) {
+        mouse_pos = -1;
+        split_area->Refresh(false);
+        return;
+    }
+
+    if (scroll_timer.IsRunning() || split_area->HasCapture()) {
+        if (event.LeftUp()) {
+            scroll_timer.Stop();
+            split_area->ReleaseMouse();
+        }
+        return;
+    }
+
+    // Check if the mouse is over a scroll arrow
+    int client_width = split_area->GetClientSize().GetWidth();
+    if (scroll_x > 0 && mouse_pos < 20)
+        scroll_dir = -1;
+    else if (scroll_x + client_width < rendered_line.GetWidth() && mouse_pos > client_width - 20)
+        scroll_dir = 1;
+    else
+        scroll_dir = 0;
+
+    if (scroll_dir) {
+        mouse_pos = -1;
+        if (event.LeftDown()) {
+            split_area->Refresh(false);
+            scroll_timer.Start(50);
+            split_area->CaptureMouse();
+            wxTimerEvent evt;
+            OnScrollTimer(evt);
+        }
+        return;
+    }
+
+    int shifted_pos = mouse_pos + scroll_x;
+
+    // Character to insert the new split point before
+    int split_pos = std::min<int>((shifted_pos + char_width / 2) / char_width, spaced_text.size());
+
+    // Syllable this character is in
+    int syl = last_lt_or_eq(syl_start_points, split_pos);
+
+    // If the click is sufficiently close to a line of a syllable split,
+    // remove that split rather than adding a new one
+    bool click_right = (syl > 0 && shifted_pos <= syl_lines[syl - 1] + 3);
+    bool click_left = (syl < (int)syl_lines.size() && shifted_pos >= syl_lines[syl] - 3);
+    click_will_remove_split = click_left || click_right;
+
+    if (!event.LeftDown()) {
+        // Erase the old line and draw the new one
+        split_area->Refresh(false);
+        return;
+    }
+
+    if (click_will_remove_split)
+        kara->RemoveSplit(syl + (click_left && !click_right));
+    else
+        kara->AddSplit(syl, char_to_byte[split_pos] - 1);
+
+    SetDisplayText();
+    accept_button->Enable(true);
+    cancel_button->Enable(true);
+    split_area->Refresh(false);
 }
 
-void AudioKaraoke::OnScrollTimer(wxTimerEvent &) {
-	scroll_x += scroll_dir * char_width * 3;
+void AudioKaraoke::OnScrollTimer(wxTimerEvent &)
+{
+    scroll_x += scroll_dir * char_width * 3;
 
-	int max_scroll = rendered_line.GetWidth() + 20 - split_area->GetClientSize().GetWidth();
-	if (scroll_x < 0 || scroll_x > max_scroll) {
-		scroll_x = mid(0, scroll_x, max_scroll);
-		scroll_timer.Stop();
-	}
+    int max_scroll = rendered_line.GetWidth() + 20 - split_area->GetClientSize().GetWidth();
+    if (scroll_x < 0 || scroll_x > max_scroll) {
+        scroll_x = mid(0, scroll_x, max_scroll);
+        scroll_timer.Stop();
+    }
 
-	split_area->Refresh(false);
+    split_area->Refresh(false);
 }
 
-void AudioKaraoke::OnTapMarkerChanged() {
-	tap_syl_start = 0;
-	tap_syl_end = 0;
-
-	if (OPT_GET("Timing/Tap To Time")->GetBool() && kara->size() > 0) {
-		const AudioTimingController *tc = c->audioController->GetTimingController();
-		const size_t marker_idx = tc->GetTapMarkerIndex();
-		if (marker_idx > 0) {
-			tap_syl_start = syl_start_points[marker_idx - 1];
-			tap_syl_end =
-				(marker_idx < syl_start_points.size() ?
-				syl_start_points[marker_idx] :
-				spaced_text.size());
-		}
-	}
-
-	RenderText();
-	Refresh(false);
+void AudioKaraoke::OnTapMarkerChanged()
+{
+    tap_syl_start = 0;
+    tap_syl_end = 0;
+
+    if (OPT_GET("Timing/Tap To Time")->GetBool() && kara->size() > 0) {
+        const AudioTimingController *tc = c->audioController->GetTimingController();
+        const size_t marker_idx = tc->GetTapMarkerIndex();
+        if (marker_idx > 0) {
+            tap_syl_start = syl_start_points[marker_idx - 1];
+            tap_syl_end =
+                (marker_idx < syl_start_points.size() ?
+                 syl_start_points[marker_idx] :
+                 spaced_text.size());
+        }
+    }
+
+    RenderText();
+    Refresh(false);
 }
 
-void AudioKaraoke::LoadFromLine() {
-	scroll_x = 0;
-	scroll_timer.Stop();
-	kara->SetLine(active_line, true);
-	SetDisplayText();
-	accept_button->Enable(kara->GetText() != active_line->Text);
-	cancel_button->Enable(false);
+void AudioKaraoke::LoadFromLine()
+{
+    scroll_x = 0;
+    scroll_timer.Stop();
+    kara->SetLine(active_line, true);
+    SetDisplayText();
+    accept_button->Enable(kara->GetText() != active_line->Text);
+    cancel_button->Enable(false);
 }
 
-void AudioKaraoke::SetDisplayText() {
-	using namespace boost::locale::boundary;
-
-	wxMemoryDC dc;
-	dc.SetFont(split_font);
-
-	auto get_char_width = [&](std::string const& character) -> int {
-		const auto it = char_widths.find(character);
-		if (it != end(char_widths))
-			return it->second;
-
-		const auto size = dc.GetTextExtent(to_wx(character));
-		char_height = std::max(char_height, size.GetHeight());
-		char_widths[character] = size.GetWidth();
-		return size.GetWidth();
-	};
-
-	char_width = get_char_width(" ");
-
-	// Width in pixels of each character in this string
-	std::vector<int> str_char_widths;
-
-	spaced_text.clear();
-	char_to_byte.clear();
-	syl_start_points.clear();
-	for (auto const& syl : *kara) {
-		// The last (and only the last) syllable needs the width of the final
-		// character in the syllable, so we unconditionally add it at the end
-		// of this loop, then remove the extra ones here
-		if (!char_to_byte.empty())
-			char_to_byte.pop_back();
-
-		syl_start_points.push_back(spaced_text.size());
-
-		// Add a space between each syllable to avoid crowding
-		spaced_text.emplace_back(wxS(" "));
-		str_char_widths.push_back(char_width);
-		char_to_byte.push_back(1);
-
-		size_t syl_idx = 1;
-		const ssegment_index characters(character, begin(syl.text), end(syl.text));
-		for (auto chr : characters) {
-			// Calculate the width in pixels of this character
-			const std::string character = chr.str();
-			const int width = get_char_width(character);
-			char_width = std::max(char_width, width);
-			str_char_widths.push_back(width);
-
-			spaced_text.emplace_back(to_wx(character));
-			char_to_byte.push_back(syl_idx);
-			syl_idx += character.size();
-		}
-
-		char_to_byte.push_back(syl_idx);
-	}
-
-	// Center each character within the space available to it
-	char_x.resize(str_char_widths.size());
-	for (size_t i = 0; i < str_char_widths.size(); ++i)
-		char_x[i] =  i * char_width + (char_width - str_char_widths[i]) / 2;
-
-	// Calculate the positions of the syllable divider lines
-	syl_lines.resize(syl_start_points.size() - 1);
-	for (size_t i = 1; i < syl_start_points.size(); ++i)
-		syl_lines[i - 1] = syl_start_points[i] * char_width + char_width / 2;
-
-	RenderText();
+void AudioKaraoke::SetDisplayText()
+{
+    using namespace boost::locale::boundary;
+
+    wxMemoryDC dc;
+    dc.SetFont(split_font);
+
+    auto get_char_width = [&](std::string const & character) -> int {
+        const auto it = char_widths.find(character);
+        if (it != end(char_widths))
+            return it->second;
+
+        const auto size = dc.GetTextExtent(to_wx(character));
+        char_height = std::max(char_height, size.GetHeight());
+        char_widths[character] = size.GetWidth();
+        return size.GetWidth();
+    };
+
+    char_width = get_char_width(" ");
+
+    // Width in pixels of each character in this string
+    std::vector<int> str_char_widths;
+
+    spaced_text.clear();
+    char_to_byte.clear();
+    syl_start_points.clear();
+    for (auto const &syl : *kara) {
+        // The last (and only the last) syllable needs the width of the final
+        // character in the syllable, so we unconditionally add it at the end
+        // of this loop, then remove the extra ones here
+        if (!char_to_byte.empty())
+            char_to_byte.pop_back();
+
+        syl_start_points.push_back(spaced_text.size());
+
+        // Add a space between each syllable to avoid crowding
+        spaced_text.emplace_back(wxS(" "));
+        str_char_widths.push_back(char_width);
+        char_to_byte.push_back(1);
+
+        size_t syl_idx = 1;
+        const ssegment_index characters(character, begin(syl.text), end(syl.text));
+        for (auto chr : characters) {
+            // Calculate the width in pixels of this character
+            const std::string character = chr.str();
+            const int width = get_char_width(character);
+            char_width = std::max(char_width, width);
+            str_char_widths.push_back(width);
+
+            spaced_text.emplace_back(to_wx(character));
+            char_to_byte.push_back(syl_idx);
+            syl_idx += character.size();
+        }
+
+        char_to_byte.push_back(syl_idx);
+    }
+
+    // Center each character within the space available to it
+    char_x.resize(str_char_widths.size());
+    for (size_t i = 0; i < str_char_widths.size(); ++i)
+        char_x[i] =  i * char_width + (char_width - str_char_widths[i]) / 2;
+
+    // Calculate the positions of the syllable divider lines
+    syl_lines.resize(syl_start_points.size() - 1);
+    for (size_t i = 1; i < syl_start_points.size(); ++i)
+        syl_lines[i - 1] = syl_start_points[i] * char_width + char_width / 2;
+
+    RenderText();
 }
 
-void AudioKaraoke::CancelSplit() {
-	LoadFromLine();
-	split_area->Refresh(false);
+void AudioKaraoke::CancelSplit()
+{
+    LoadFromLine();
+    split_area->Refresh(false);
 }
 
-void AudioKaraoke::AcceptSplit() {
-	active_line->Text = kara->GetText();
-	file_changed.Block();
-	c->ass->Commit(_("karaoke split"), AssFile::COMMIT_DIAG_TEXT);
-	file_changed.Unblock();
+void AudioKaraoke::AcceptSplit()
+{
+    active_line->Text = kara->GetText();
+    file_changed.Block();
+    c->ass->Commit(_("karaoke split"), AssFile::COMMIT_DIAG_TEXT);
+    file_changed.Unblock();
 
-	accept_button->Enable(false);
-	cancel_button->Enable(false);
+    accept_button->Enable(false);
+    cancel_button->Enable(false);
 }
 
-void AudioKaraoke::SetTagType(std::string const& new_tag) {
-	kara->SetTagType(new_tag);
-	AcceptSplit();
+void AudioKaraoke::SetTagType(std::string const &new_tag)
+{
+    kara->SetTagType(new_tag);
+    AcceptSplit();
 }
diff --git a/src/audio_karaoke.h b/src/audio_karaoke.h
index 9cc4cb517aee934d1f1bf4b3ca9352834d92e3b7..60fc5b8653b29ac55cb11db62c53f400e1acc24b 100644
--- a/src/audio_karaoke.h
+++ b/src/audio_karaoke.h
@@ -62,104 +62,104 @@ namespace agi { struct Context; }
 /// changes is on, will happen as soon as the user adjusts the timing of the
 /// new syllable).
 class AudioKaraoke final : public wxWindow {
-	agi::Context *c; ///< Project context
-	agi::signal::Connection file_changed; ///< File changed slot
-	agi::signal::Connection audio_opened; ///< Audio opened connection
-	agi::signal::Connection audio_closed; ///< Audio closed connection
-	agi::signal::Connection active_line_changed;
-	agi::signal::Connection tap_to_time_toggled;
+    agi::Context *c; ///< Project context
+    agi::signal::Connection file_changed; ///< File changed slot
+    agi::signal::Connection audio_opened; ///< Audio opened connection
+    agi::signal::Connection audio_closed; ///< Audio closed connection
+    agi::signal::Connection active_line_changed;
+    agi::signal::Connection tap_to_time_toggled;
 
-	/// Currently active dialogue line
-	AssDialogue *active_line = nullptr;
-	/// Karaoke data
-	std::unique_ptr<AssKaraoke> kara;
+    /// Currently active dialogue line
+    AssDialogue *active_line = nullptr;
+    /// Karaoke data
+    std::unique_ptr<AssKaraoke> kara;
 
-	/// Current line's stripped text with spaces added between each syllable
-	std::vector<wxString> spaced_text;
+    /// Current line's stripped text with spaces added between each syllable
+    std::vector<wxString> spaced_text;
 
-	/// spaced_text + syl_lines rendered to a bitmap
-	wxBitmap rendered_line;
+    /// spaced_text + syl_lines rendered to a bitmap
+    wxBitmap rendered_line;
 
-	/// Indexes in spaced_text which are the beginning of syllables
-	std::vector<int> syl_start_points;
+    /// Indexes in spaced_text which are the beginning of syllables
+    std::vector<int> syl_start_points;
 
-	/// x coordinate in pixels of the separator lines of each syllable
-	std::vector<int> syl_lines;
+    /// x coordinate in pixels of the separator lines of each syllable
+    std::vector<int> syl_lines;
 
-	/// Left x coordinate of each character in spaced_text in pixels
-	std::vector<int> char_x;
+    /// Left x coordinate of each character in spaced_text in pixels
+    std::vector<int> char_x;
 
-	/// Mapping from character index to byte position in the relevant syllable's text
-	std::vector<size_t> char_to_byte;
+    /// Mapping from character index to byte position in the relevant syllable's text
+    std::vector<size_t> char_to_byte;
 
-	/// Cached width of characters from GetTextExtent
-	std::unordered_map<std::string, int> char_widths;
+    /// Cached width of characters from GetTextExtent
+    std::unordered_map<std::string, int> char_widths;
 
-	int scroll_x = 0; ///< Distance the display has been shifted to the left in pixels
-	int scroll_dir = 0; ///< Direction the display will be scrolled on scroll_timer ticks (+/- 1)
-	wxTimer scroll_timer; ///< Timer to scroll every 50ms when user holds down scroll button
+    int scroll_x = 0; ///< Distance the display has been shifted to the left in pixels
+    int scroll_dir = 0; ///< Direction the display will be scrolled on scroll_timer ticks (+/- 1)
+    wxTimer scroll_timer; ///< Timer to scroll every 50ms when user holds down scroll button
 
-	int char_height = 0; ///< Maximum character height in pixels
-	int char_width = 0; ///< Maximum character width in pixels
-	int mouse_pos = 0; ///< Last x coordinate of the mouse
-	bool click_will_remove_split = false; ///< If true a click at mouse_pos will remove a split rather than adding one
+    int char_height = 0; ///< Maximum character height in pixels
+    int char_width = 0; ///< Maximum character width in pixels
+    int mouse_pos = 0; ///< Last x coordinate of the mouse
+    bool click_will_remove_split = false; ///< If true a click at mouse_pos will remove a split rather than adding one
 
-	wxFont split_font; ///< Font used in the split/join interface
+    wxFont split_font; ///< Font used in the split/join interface
 
-	size_t tap_syl_start = 0; ///< Tap-to-time syllable start character index
-	size_t tap_syl_end = 0;		///< Tap-to-time syllable end character index
+    size_t tap_syl_start = 0; ///< Tap-to-time syllable start character index
+    size_t tap_syl_end = 0;		///< Tap-to-time syllable end character index
 
-	bool enabled = false; ///< Is karaoke mode enabled?
+    bool enabled = false; ///< Is karaoke mode enabled?
 
-	wxButton *accept_button; ///< Accept pending splits button
-	wxButton *cancel_button; ///< Revert pending changes
+    wxButton *accept_button; ///< Accept pending splits button
+    wxButton *cancel_button; ///< Revert pending changes
 
-	wxWindow *split_area; ///< The split/join window
+    wxWindow *split_area; ///< The split/join window
 
-	/// Load syllable data from the currently active line
-	void LoadFromLine();
-	/// Cache presentational data from the loaded syllable data
-	void SetDisplayText();
+    /// Load syllable data from the currently active line
+    void LoadFromLine();
+    /// Cache presentational data from the loaded syllable data
+    void SetDisplayText();
 
-	/// Helper function for context menu creation
-	void AddMenuItem(wxMenu &menu, std::string const& tag, wxString const& help, std::string const& selected);
-	/// Set the karaoke tags for the selected syllables to the indicated one
-	void SetTagType(std::string const& new_type);
+    /// Helper function for context menu creation
+    void AddMenuItem(wxMenu &menu, std::string const &tag, wxString const &help, std::string const &selected);
+    /// Set the karaoke tags for the selected syllables to the indicated one
+    void SetTagType(std::string const &new_type);
 
-	/// Prerender the current line along with syllable split lines
-	void RenderText();
+    /// Prerender the current line along with syllable split lines
+    void RenderText();
 
-	/// Refresh the area of the display around a single character
-	/// @param pos Index in spaced_text
-	void LimitedRefresh(int pos);
+    /// Refresh the area of the display around a single character
+    /// @param pos Index in spaced_text
+    void LimitedRefresh(int pos);
 
-	/// Reset all pending split information and return to normal mode
-	void CancelSplit();
-	/// Apply any pending split information to the syllable data and return to normal mode
-	void AcceptSplit();
+    /// Reset all pending split information and return to normal mode
+    void CancelSplit();
+    /// Apply any pending split information to the syllable data and return to normal mode
+    void AcceptSplit();
 
-	void OnActiveLineChanged(AssDialogue *new_line);
-	void OnContextMenu(wxContextMenuEvent&);
-	void OnEnableButton(wxCommandEvent &evt);
-	void OnFileChanged(int type, const AssDialogue *changed);
-	void OnMouse(wxMouseEvent &event);
-	void OnPaint(wxPaintEvent &event);
-	void OnSize(wxSizeEvent &event);
-	void OnAudioOpened(agi::AudioProvider *provider);
-	void OnScrollTimer(wxTimerEvent &event);
-	void OnTapMarkerChanged();
+    void OnActiveLineChanged(AssDialogue *new_line);
+    void OnContextMenu(wxContextMenuEvent &);
+    void OnEnableButton(wxCommandEvent &evt);
+    void OnFileChanged(int type, const AssDialogue *changed);
+    void OnMouse(wxMouseEvent &event);
+    void OnPaint(wxPaintEvent &event);
+    void OnSize(wxSizeEvent &event);
+    void OnAudioOpened(agi::AudioProvider *provider);
+    void OnScrollTimer(wxTimerEvent &event);
+    void OnTapMarkerChanged();
 
 public:
-	/// Constructor
-	/// @param parent Parent window
-	/// @param c Project context
-	AudioKaraoke(wxWindow *parent, agi::Context *c);
-	/// Destructor
-	~AudioKaraoke();
-
-	/// Is karaoke mode currently enabled?
-	bool IsEnabled() const { return enabled; }
-
-	/// Enable or disable karaoke mode
-	void SetEnabled(bool enable);
+    /// Constructor
+    /// @param parent Parent window
+    /// @param c Project context
+    AudioKaraoke(wxWindow *parent, agi::Context *c);
+    /// Destructor
+    ~AudioKaraoke();
+
+    /// Is karaoke mode currently enabled?
+    bool IsEnabled() const { return enabled; }
+
+    /// Enable or disable karaoke mode
+    void SetEnabled(bool enable);
 };
diff --git a/src/audio_marker.cpp b/src/audio_marker.cpp
index c627d5b9641f8eb611950cfdefe66f80edb32fba..765433b936980d8a09d4b0cc85dd2d1bc87d5ccf 100644
--- a/src/audio_marker.cpp
+++ b/src/audio_marker.cpp
@@ -32,127 +32,135 @@
 #include <algorithm>
 
 class AudioMarkerKeyframe final : public AudioMarker {
-	Pen *style;
-	int position;
+    Pen *style;
+    int position;
 public:
-	AudioMarkerKeyframe(Pen *style, int position) : style(style), position(position) { }
-	int GetPosition() const override { return position; }
-	FeetStyle GetFeet() const override { return Feet_None; }
-	wxPen GetStyle() const override { return *style; }
-	operator int() const { return position; }
+    AudioMarkerKeyframe(Pen *style, int position) : style(style), position(position) { }
+    int GetPosition() const override { return position; }
+    FeetStyle GetFeet() const override { return Feet_None; }
+    wxPen GetStyle() const override { return *style; }
+    operator int() const { return position; }
 };
 
 AudioMarkerProviderKeyframes::AudioMarkerProviderKeyframes(agi::Context *c, const char *opt_name)
-: p(c->project.get())
-, keyframe_slot(p->AddKeyframesListener(&AudioMarkerProviderKeyframes::Update, this))
-, timecode_slot(p->AddTimecodesListener(&AudioMarkerProviderKeyframes::Update, this))
-, enabled_slot(OPT_SUB(opt_name, &AudioMarkerProviderKeyframes::Update, this))
-, enabled_opt(OPT_GET(opt_name))
-, style(agi::make_unique<Pen>("Colour/Audio Display/Keyframe"))
+    : p(c->project.get())
+    , keyframe_slot(p->AddKeyframesListener(&AudioMarkerProviderKeyframes::Update, this))
+    , timecode_slot(p->AddTimecodesListener(&AudioMarkerProviderKeyframes::Update, this))
+    , enabled_slot(OPT_SUB(opt_name, &AudioMarkerProviderKeyframes::Update, this))
+    , enabled_opt(OPT_GET(opt_name))
+    , style(agi::make_unique<Pen>("Colour/Audio Display/Keyframe"))
 {
-	Update();
+    Update();
 }
 
 AudioMarkerProviderKeyframes::~AudioMarkerProviderKeyframes() { }
 
-void AudioMarkerProviderKeyframes::Update() {
-	auto const& keyframes = p->Keyframes();
-	auto const& timecodes = p->Timecodes();
-
-	if (keyframes.empty() || !timecodes.IsLoaded() || !enabled_opt->GetBool()) {
-		if (!markers.empty()) {
-			markers.clear();
-			AnnounceMarkerMoved();
-		}
-		return;
-	}
-
-	markers.clear();
-	markers.reserve(keyframes.size());
-	for (int frame : keyframes)
-		markers.emplace_back(style.get(), timecodes.TimeAtFrame(frame, agi::vfr::START));
-	AnnounceMarkerMoved();
+void AudioMarkerProviderKeyframes::Update()
+{
+    auto const &keyframes = p->Keyframes();
+    auto const &timecodes = p->Timecodes();
+
+    if (keyframes.empty() || !timecodes.IsLoaded() || !enabled_opt->GetBool()) {
+        if (!markers.empty()) {
+            markers.clear();
+            AnnounceMarkerMoved();
+        }
+        return;
+    }
+
+    markers.clear();
+    markers.reserve(keyframes.size());
+    for (int frame : keyframes)
+        markers.emplace_back(style.get(), timecodes.TimeAtFrame(frame, agi::vfr::START));
+    AnnounceMarkerMoved();
 }
 
-void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
-	// Find first and last keyframes inside the range
-	auto a = lower_bound(markers.begin(), markers.end(), range.begin());
-	auto b = upper_bound(markers.begin(), markers.end(), range.end());
+void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const &range, AudioMarkerVector &out) const
+{
+    // Find first and last keyframes inside the range
+    auto a = lower_bound(markers.begin(), markers.end(), range.begin());
+    auto b = upper_bound(markers.begin(), markers.end(), range.end());
 
-	// Place pointers to the markers in the output vector
-	for (; a != b; ++a)
-		out.push_back(&*a);
+    // Place pointers to the markers in the output vector
+    for (; a != b; ++a)
+        out.push_back(&*a);
 }
 
 class VideoPositionMarker final : public AudioMarker {
-	Pen style{"Colour/Audio Display/Play Cursor"};
-	int position = -1;
+    Pen style{"Colour/Audio Display/Play Cursor"};
+    int position = -1;
 
 public:
-	void SetPosition(int new_pos) { position = new_pos; }
+    void SetPosition(int new_pos) { position = new_pos; }
 
-	int GetPosition() const override { return position; }
-	FeetStyle GetFeet() const override { return Feet_None; }
-	wxPen GetStyle() const override { return style; }
-	operator int() const { return position; }
+    int GetPosition() const override { return position; }
+    FeetStyle GetFeet() const override { return Feet_None; }
+    wxPen GetStyle() const override { return style; }
+    operator int() const { return position; }
 };
 
 VideoPositionMarkerProvider::VideoPositionMarkerProvider(agi::Context *c)
-: vc(c->videoController.get())
-, video_seek_slot(vc->AddSeekListener(&VideoPositionMarkerProvider::Update, this))
-, enable_opt_changed_slot(OPT_SUB("Audio/Display/Draw/Video Position", &VideoPositionMarkerProvider::OptChanged, this))
+    : vc(c->videoController.get())
+    , video_seek_slot(vc->AddSeekListener(&VideoPositionMarkerProvider::Update, this))
+    , enable_opt_changed_slot(OPT_SUB("Audio/Display/Draw/Video Position", &VideoPositionMarkerProvider::OptChanged, this))
 {
-	OptChanged(*OPT_GET("Audio/Display/Draw/Video Position"));
+    OptChanged(*OPT_GET("Audio/Display/Draw/Video Position"));
 }
 
 VideoPositionMarkerProvider::~VideoPositionMarkerProvider() { }
 
-void VideoPositionMarkerProvider::Update(int frame_number) {
-	marker->SetPosition(vc->TimeAtFrame(frame_number));
-	AnnounceMarkerMoved();
+void VideoPositionMarkerProvider::Update(int frame_number)
+{
+    marker->SetPosition(vc->TimeAtFrame(frame_number));
+    AnnounceMarkerMoved();
 }
 
-void VideoPositionMarkerProvider::OptChanged(agi::OptionValue const& opt) {
-	if (opt.GetBool()) {
-		video_seek_slot.Unblock();
-		marker = agi::make_unique<VideoPositionMarker>();
-		marker->SetPosition(vc->GetFrameN());
-	}
-	else {
-		video_seek_slot.Block();
-		marker.reset();
-	}
+void VideoPositionMarkerProvider::OptChanged(agi::OptionValue const &opt)
+{
+    if (opt.GetBool()) {
+        video_seek_slot.Unblock();
+        marker = agi::make_unique<VideoPositionMarker>();
+        marker->SetPosition(vc->GetFrameN());
+    }
+    else {
+        video_seek_slot.Block();
+        marker.reset();
+    }
 }
 
-void VideoPositionMarkerProvider::GetMarkers(const TimeRange &range, AudioMarkerVector &out) const {
-	if (marker && range.contains(*marker))
-		out.push_back(marker.get());
+void VideoPositionMarkerProvider::GetMarkers(const TimeRange &range, AudioMarkerVector &out) const
+{
+    if (marker && range.contains(*marker))
+        out.push_back(marker.get());
 }
 
 SecondsMarkerProvider::SecondsMarkerProvider()
-: pen(agi::make_unique<Pen>("Colour/Audio Display/Seconds Line", 1, wxPENSTYLE_DOT))
-, enabled(OPT_GET("Audio/Display/Draw/Seconds"))
-, enabled_opt_changed(OPT_SUB("Audio/Display/Draw/Seconds", &SecondsMarkerProvider::EnabledOptChanged, this))
+    : pen(agi::make_unique<Pen>("Colour/Audio Display/Seconds Line", 1, wxPENSTYLE_DOT))
+    , enabled(OPT_GET("Audio/Display/Draw/Seconds"))
+    , enabled_opt_changed(OPT_SUB("Audio/Display/Draw/Seconds", &SecondsMarkerProvider::EnabledOptChanged, this))
 {
 }
 
-void SecondsMarkerProvider::EnabledOptChanged() {
-	AnnounceMarkerMoved();
+void SecondsMarkerProvider::EnabledOptChanged()
+{
+    AnnounceMarkerMoved();
 }
 
-void SecondsMarkerProvider::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
-	if (!enabled->GetBool()) return;
+void SecondsMarkerProvider::GetMarkers(TimeRange const &range, AudioMarkerVector &out) const
+{
+    if (!enabled->GetBool()) return;
 
-	if ((range.length() + 999) / 1000 > (int)markers.size())
-		markers.resize((range.length() + 999) / 1000, Marker(pen.get()));
+    if ((range.length() + 999) / 1000 > (int)markers.size())
+        markers.resize((range.length() + 999) / 1000, Marker(pen.get()));
 
-	size_t i = 0;
-	for (int time = ((range.begin() + 999) / 1000) * 1000; time < range.end(); time += 1000) {
-		markers[i].position = time;
-		out.push_back(&markers[i++]);
-	}
+    size_t i = 0;
+    for (int time = ((range.begin() + 999) / 1000) * 1000; time < range.end(); time += 1000) {
+        markers[i].position = time;
+        out.push_back(&markers[i++]);
+    }
 }
 
-wxPen SecondsMarkerProvider::Marker::GetStyle() const {
-	return *style;
+wxPen SecondsMarkerProvider::Marker::GetStyle() const
+{
+    return *style;
 }
diff --git a/src/audio_marker.h b/src/audio_marker.h
index ba725ed4cd54415fdbef2cafcbf88e478825cfa3..faf668a1ce79ad671157e2c40897987bed12db10 100644
--- a/src/audio_marker.h
+++ b/src/audio_marker.h
@@ -32,159 +32,159 @@ class VideoPositionMarker;
 class wxPen;
 
 namespace agi {
-	class OptionValue;
-	struct Context;
+class OptionValue;
+struct Context;
 }
 
 /// @class AudioMarker
 /// @brief A marker on the audio display
 class AudioMarker {
 protected:
-	~AudioMarker()=default;
+    ~AudioMarker() = default;
 public:
-	/// Describe which directions a marker has feet in
-	enum FeetStyle {
-		Feet_None = 0,
-		Feet_Left,
-		Feet_Right,
-		Feet_Both // Conveniently Feet_Left|Feet_Right
-	};
-
-	/// @brief Get the marker's position
-	/// @return The marker's position in milliseconds
-	virtual int GetPosition() const = 0;
-
-	/// @brief Get the marker's drawing style
-	/// @return A pen object describing the marker's drawing style
-	virtual wxPen GetStyle() const = 0;
-
-	/// @brief Get the marker's feet style
-	/// @return The marker's feet style
-	virtual FeetStyle GetFeet() const = 0;
+    /// Describe which directions a marker has feet in
+    enum FeetStyle {
+        Feet_None = 0,
+        Feet_Left,
+        Feet_Right,
+        Feet_Both // Conveniently Feet_Left|Feet_Right
+    };
+
+    /// @brief Get the marker's position
+    /// @return The marker's position in milliseconds
+    virtual int GetPosition() const = 0;
+
+    /// @brief Get the marker's drawing style
+    /// @return A pen object describing the marker's drawing style
+    virtual wxPen GetStyle() const = 0;
+
+    /// @brief Get the marker's feet style
+    /// @return The marker's feet style
+    virtual FeetStyle GetFeet() const = 0;
 };
 
-typedef std::vector<const AudioMarker*> AudioMarkerVector;
+typedef std::vector<const AudioMarker *> AudioMarkerVector;
 
 /// Abstract interface for audio marker providers
 class AudioMarkerProvider {
 protected:
-	/// One or more of the markers provided by this object have changed
-	agi::signal::Signal<> AnnounceMarkerMoved;
+    /// One or more of the markers provided by this object have changed
+    agi::signal::Signal<> AnnounceMarkerMoved;
 
-	~AudioMarkerProvider() = default;
+    ~AudioMarkerProvider() = default;
 public:
-	/// @brief Return markers in a time range
-	virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0;
+    /// @brief Return markers in a time range
+    virtual void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const = 0;
 
-	DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceMarkerMoved, AddMarkerMovedListener)
 };
 
 /// @class AudioLabelProvider
 /// @brief Abstract interface for audio label providers
 class AudioLabelProvider {
 protected:
-	/// One or more of the labels provided by this object have changed
-	agi::signal::Signal<> AnnounceLabelChanged;
+    /// One or more of the labels provided by this object have changed
+    agi::signal::Signal<> AnnounceLabelChanged;
 
-	~AudioLabelProvider() = default;
+    ~AudioLabelProvider() = default;
 public:
-	/// A label for a range of time on the audio display
-	struct AudioLabel {
-		/// Text of the label
-		wxString text;
-		/// Range which this label applies to
-		TimeRange range;
-	};
-
-	/// @brief Get labels in a time range
-	/// @param range Range of times to get labels for
-	/// @param[out] out Vector which should be filled with the labels
-	virtual void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const = 0;
-
-	DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
+    /// A label for a range of time on the audio display
+    struct AudioLabel {
+        /// Text of the label
+        wxString text;
+        /// Range which this label applies to
+        TimeRange range;
+    };
+
+    /// @brief Get labels in a time range
+    /// @param range Range of times to get labels for
+    /// @param[out] out Vector which should be filled with the labels
+    virtual void GetLabels(TimeRange const &range, std::vector<AudioLabel> &out) const = 0;
+
+    DEFINE_SIGNAL_ADDERS(AnnounceLabelChanged, AddLabelChangedListener)
 };
 
 
 /// Marker provider for video keyframes
 class AudioMarkerProviderKeyframes final : public AudioMarkerProvider {
-	/// Project to get keyframes from
-	Project *p;
+    /// Project to get keyframes from
+    Project *p;
 
-	agi::signal::Connection keyframe_slot;
-	agi::signal::Connection timecode_slot;
-	agi::signal::Connection enabled_slot;
-	const agi::OptionValue *enabled_opt;
+    agi::signal::Connection keyframe_slot;
+    agi::signal::Connection timecode_slot;
+    agi::signal::Connection enabled_slot;
+    const agi::OptionValue *enabled_opt;
 
-	/// Current set of markers for the keyframes
-	std::vector<AudioMarkerKeyframe> markers;
+    /// Current set of markers for the keyframes
+    std::vector<AudioMarkerKeyframe> markers;
 
-	/// Pen used for all keyframe markers, stored here for performance reasons
-	std::unique_ptr<Pen> style;
+    /// Pen used for all keyframe markers, stored here for performance reasons
+    std::unique_ptr<Pen> style;
 
-	/// Regenerate the list of markers
-	void Update();
+    /// Regenerate the list of markers
+    void Update();
 
 public:
-	/// Constructor
-	/// @param c Project context; must have audio and video controllers initialized
-	/// @param opt_name Name of the option to use to decide whether or not this provider is enabled
-	AudioMarkerProviderKeyframes(agi::Context *c, const char *opt_name);
-	/// Explicit destructor needed due to members with incomplete types
-	~AudioMarkerProviderKeyframes();
-
-	/// Get all keyframe markers within a range
-	/// @param range Time range to get markers for
-	/// @param[out] out Vector to fill with markers in the range
-	void GetMarkers(TimeRange const& range, AudioMarkerVector &out) const override;
+    /// Constructor
+    /// @param c Project context; must have audio and video controllers initialized
+    /// @param opt_name Name of the option to use to decide whether or not this provider is enabled
+    AudioMarkerProviderKeyframes(agi::Context *c, const char *opt_name);
+    /// Explicit destructor needed due to members with incomplete types
+    ~AudioMarkerProviderKeyframes();
+
+    /// Get all keyframe markers within a range
+    /// @param range Time range to get markers for
+    /// @param[out] out Vector to fill with markers in the range
+    void GetMarkers(TimeRange const &range, AudioMarkerVector &out) const override;
 };
 
 /// Marker provider for the current video playback position
 class VideoPositionMarkerProvider final : public AudioMarkerProvider {
-	VideoController *vc;
+    VideoController *vc;
 
-	std::unique_ptr<VideoPositionMarker> marker;
+    std::unique_ptr<VideoPositionMarker> marker;
 
-	agi::signal::Connection video_seek_slot;
-	agi::signal::Connection enable_opt_changed_slot;
+    agi::signal::Connection video_seek_slot;
+    agi::signal::Connection enable_opt_changed_slot;
 
-	void Update(int frame_number);
-	void OptChanged(agi::OptionValue const& opt);
+    void Update(int frame_number);
+    void OptChanged(agi::OptionValue const &opt);
 
 public:
-	VideoPositionMarkerProvider(agi::Context *c);
-	~VideoPositionMarkerProvider();
+    VideoPositionMarkerProvider(agi::Context *c);
+    ~VideoPositionMarkerProvider();
 
-	void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const override;
+    void GetMarkers(const TimeRange &range, AudioMarkerVector &out) const override;
 };
 
 /// Marker provider for lines every second
 class SecondsMarkerProvider final : public AudioMarkerProvider {
-	struct Marker final : public AudioMarker {
-		Pen *style;
-		int position = 0;
+    struct Marker final : public AudioMarker {
+        Pen *style;
+        int position = 0;
 
-		Marker(Pen *style) : style(style) { }
-		int GetPosition() const override { return position; }
-		FeetStyle GetFeet() const override { return Feet_None; }
-		wxPen GetStyle() const override;
-		operator int() const { return position; }
-	};
+        Marker(Pen *style) : style(style) { }
+        int GetPosition() const override { return position; }
+        FeetStyle GetFeet() const override { return Feet_None; }
+        wxPen GetStyle() const override;
+        operator int() const { return position; }
+    };
 
-	/// Pen used by all seconds markers, here for performance
-	std::unique_ptr<Pen> pen;
+    /// Pen used by all seconds markers, here for performance
+    std::unique_ptr<Pen> pen;
 
-	/// Markers returned from last call to GetMarkers
-	mutable std::vector<Marker> markers;
+    /// Markers returned from last call to GetMarkers
+    mutable std::vector<Marker> markers;
 
-	/// Cached reference to the option to enable/disable seconds markers
-	const agi::OptionValue *enabled;
+    /// Cached reference to the option to enable/disable seconds markers
+    const agi::OptionValue *enabled;
 
-	/// Enabled option change connection
-	agi::signal::Connection enabled_opt_changed;
+    /// Enabled option change connection
+    agi::signal::Connection enabled_opt_changed;
 
-	void EnabledOptChanged();
+    void EnabledOptChanged();
 
 public:
-	SecondsMarkerProvider();
-	void GetMarkers(TimeRange const& range, AudioMarkerVector &out) const override;
+    SecondsMarkerProvider();
+    void GetMarkers(TimeRange const &range, AudioMarkerVector &out) const override;
 };
diff --git a/src/audio_player.cpp b/src/audio_player.cpp
index f5a8327ca08abdeab76bebbb326218e871dcfaca..4f680475b88f1e3b5f385543a541e920c1290ada 100644
--- a/src/audio_player.cpp
+++ b/src/audio_player.cpp
@@ -49,54 +49,56 @@ std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *provider
 std::unique_ptr<AudioPlayer> CreateOSSPlayer(agi::AudioProvider *providers, wxWindow *window);
 
 namespace {
-	struct factory {
-		const char *name;
-		std::unique_ptr<AudioPlayer> (*create)(agi::AudioProvider *, wxWindow *window);
-		bool hidden;
-	};
+struct factory {
+    const char *name;
+    std::unique_ptr<AudioPlayer> (*create)(agi::AudioProvider *, wxWindow *window);
+    bool hidden;
+};
 
-	const factory factories[] = {
+const factory factories[] = {
 #ifdef WITH_ALSA
-		{"ALSA", CreateAlsaPlayer, false},
+    {"ALSA", CreateAlsaPlayer, false},
 #endif
 #ifdef WITH_DIRECTSOUND
-		{"DirectSound-old", CreateDirectSoundPlayer, false},
-		{"DirectSound", CreateDirectSound2Player, false},
+    {"DirectSound-old", CreateDirectSoundPlayer, false},
+    {"DirectSound", CreateDirectSound2Player, false},
 #endif
 #ifdef WITH_OPENAL
-		{"OpenAL", CreateOpenALPlayer, false},
+    {"OpenAL", CreateOpenALPlayer, false},
 #endif
 #ifdef WITH_PORTAUDIO
-		{"PortAudio", CreatePortAudioPlayer, false},
+    {"PortAudio", CreatePortAudioPlayer, false},
 #endif
 #ifdef WITH_LIBPULSE
-		{"PulseAudio", CreatePulseAudioPlayer, false},
+    {"PulseAudio", CreatePulseAudioPlayer, false},
 #endif
 #ifdef WITH_OSS
-		{"OSS", CreateOSSPlayer, false},
+    {"OSS", CreateOSSPlayer, false},
 #endif
-	};
+};
 }
 
-std::vector<std::string> AudioPlayerFactory::GetClasses() {
-	return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories)));
+std::vector<std::string> AudioPlayerFactory::GetClasses()
+{
+    return ::GetClasses(boost::make_iterator_range(std::begin(factories), std::end(factories)));
 }
 
-std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(agi::AudioProvider *provider, wxWindow *window) {
-	if (std::begin(factories) == std::end(factories))
-		throw AudioPlayerOpenError("No audio players are available.");
+std::unique_ptr<AudioPlayer> AudioPlayerFactory::GetAudioPlayer(agi::AudioProvider *provider, wxWindow *window)
+{
+    if (std::begin(factories) == std::end(factories))
+        throw AudioPlayerOpenError("No audio players are available.");
 
-	auto preferred = OPT_GET("Audio/Player")->GetString();
-	auto sorted = GetSorted(boost::make_iterator_range(std::begin(factories), std::end(factories)), preferred);
+    auto preferred = OPT_GET("Audio/Player")->GetString();
+    auto sorted = GetSorted(boost::make_iterator_range(std::begin(factories), std::end(factories)), preferred);
 
-	std::string error;
-	for (auto factory : sorted) {
-		try {
-			return factory->create(provider, window);
-		}
-		catch (AudioPlayerOpenError const& err) {
-			error += std::string(factory->name) + " factory: " + err.GetMessage() + "\n";
-		}
-	}
-	throw AudioPlayerOpenError(error);
+    std::string error;
+    for (auto factory : sorted) {
+        try {
+            return factory->create(provider, window);
+        }
+        catch (AudioPlayerOpenError const &err) {
+            error += std::string(factory->name) + " factory: " + err.GetMessage() + "\n";
+        }
+    }
+    throw AudioPlayerOpenError(error);
 }
diff --git a/src/audio_player_alsa.cpp b/src/audio_player_alsa.cpp
index 5a1705622923cc78c6adb817a4c136da3c182781..8d3bda327661e38815b6a9ff5925d8f5442caee8 100644
--- a/src/audio_player_alsa.cpp
+++ b/src/audio_player_alsa.cpp
@@ -59,297 +59,282 @@
 
 namespace {
 enum class Message {
-	None,
-	Start,
-	Stop,
-	Close
+    None,
+    Start,
+    Stop,
+    Close
 };
 
 using clock = std::chrono::steady_clock;
 
 class AlsaPlayer final : public AudioPlayer {
-	std::mutex mutex;
-	std::condition_variable cond;
+    std::mutex mutex;
+    std::condition_variable cond;
 
-	std::string device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
+    std::string device_name = OPT_GET("Player/Audio/ALSA/Device")->GetString();
 
-	Message message = Message::None;
+    Message message = Message::None;
 
-	std::atomic<bool> playing{false};
-	std::atomic<double> volume{1.0};
-	int64_t start_position = 0;
-	std::atomic<int64_t> end_position{0};
+    std::atomic<bool> playing{false};
+    std::atomic<double> volume{1.0};
+    int64_t start_position = 0;
+    std::atomic<int64_t> end_position{0};
 
-	std::mutex position_mutex;
-	int64_t last_position = 0;
-	clock::time_point last_position_time;
+    std::mutex position_mutex;
+    int64_t last_position = 0;
+    clock::time_point last_position_time;
 
-	std::vector<char> decode_buffer;
+    std::vector<char> decode_buffer;
 
-	std::thread thread;
+    std::thread thread;
 
-	void PlaybackThread();
+    void PlaybackThread();
 
-	void UpdatePlaybackPosition(snd_pcm_t *pcm, int64_t position)
-	{
-		snd_pcm_sframes_t delay;
-		if (snd_pcm_delay(pcm, &delay) == 0)
-		{
-			std::unique_lock<std::mutex> playback_lock;
-			last_position = position - delay;
-			last_position_time = clock::now();
-		}
-	}
+    void UpdatePlaybackPosition(snd_pcm_t *pcm, int64_t position) {
+        snd_pcm_sframes_t delay;
+        if (snd_pcm_delay(pcm, &delay) == 0) {
+            std::unique_lock<std::mutex> playback_lock;
+            last_position = position - delay;
+            last_position_time = clock::now();
+        }
+    }
 
 public:
-	AlsaPlayer(agi::AudioProvider *provider);
-	~AlsaPlayer();
+    AlsaPlayer(agi::AudioProvider *provider);
+    ~AlsaPlayer();
 
-	void Play(int64_t start, int64_t count) override;
-	void Stop() override;
-	bool IsPlaying() override { return playing; }
+    void Play(int64_t start, int64_t count) override;
+    void Stop() override;
+    bool IsPlaying() override { return playing; }
 
-	void SetVolume(double vol) override { volume = vol; }
-	int64_t GetEndPosition() override { return end_position; }
-	int64_t GetCurrentPosition() override;
-	void SetEndPosition(int64_t pos) override;
+    void SetVolume(double vol) override { volume = vol; }
+    int64_t GetEndPosition() override { return end_position; }
+    int64_t GetCurrentPosition() override;
+    void SetEndPosition(int64_t pos) override;
 };
 
 void AlsaPlayer::PlaybackThread()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+    std::unique_lock<std::mutex> lock(mutex);
 
-	snd_pcm_t *pcm = nullptr;
-	if (snd_pcm_open(&pcm, device_name.c_str(), SND_PCM_STREAM_PLAYBACK, 0) != 0)
-		return;
-	LOG_D("audio/player/alsa") << "opened pcm";
-	BOOST_SCOPE_EXIT_ALL(&) { snd_pcm_close(pcm); };
+    snd_pcm_t *pcm = nullptr;
+    if (snd_pcm_open(&pcm, device_name.c_str(), SND_PCM_STREAM_PLAYBACK, 0) != 0)
+        return;
+    LOG_D("audio/player/alsa") << "opened pcm";
+    BOOST_SCOPE_EXIT_ALL(&) { snd_pcm_close(pcm); };
 
 do_setup:
-	snd_pcm_format_t pcm_format;
-	switch (provider->GetBytesPerSample())
-	{
-	case 1:
-		LOG_D("audio/player/alsa") << "format U8";
-		pcm_format = SND_PCM_FORMAT_U8;
-		break;
-	case 2:
-		LOG_D("audio/player/alsa") << "format S16_LE";
-		pcm_format = SND_PCM_FORMAT_S16_LE;
-		break;
-	default:
-		return;
-	}
-	if (snd_pcm_set_params(pcm,
-	                       pcm_format,
-	                       SND_PCM_ACCESS_RW_INTERLEAVED,
-	                       provider->GetChannels(),
-	                       provider->GetSampleRate(),
-	                       1, // allow resample
-	                       100*1000 // 100 milliseconds latency
-	                      ) != 0)
-		return;
-	LOG_D("audio/player/alsa") << "set pcm params";
-
-	size_t framesize = provider->GetChannels() * provider->GetBytesPerSample();
-
-	while (true)
-	{
-		// Wait for condition to trigger
-		while (message != Message::Start)
-		{
-			cond.wait(lock, [&] { return message != Message::None; });
-			if (message == Message::Close)
-				return;
-			if (message == Message::Start && end_position > start_position)
-				break;
-			// Not playing, so don't need to stop...
-			message = Message::None;
-		}
-		message = Message::None;
-
-		LOG_D("audio/player/alsa") << "starting playback";
-		int64_t position = start_position;
-
-		// Initial buffer-fill
-		{
-			auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position-position));
-			decode_buffer.resize(avail * framesize);
-			provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
-
-			snd_pcm_sframes_t written = 0;
-			while (written <= 0)
-			{
-				written = snd_pcm_writei(pcm, decode_buffer.data(), avail);
-				if (written == -ESTRPIPE)
-					snd_pcm_recover(pcm, written, 0);
-				else if (written <= 0)
-				{
-					LOG_D("audio/player/alsa") << "error filling buffer";
-					return;
-				}
-			}
-			position += written;
-		}
-
-		// Start playback
-		LOG_D("audio/player/alsa") << "initial buffer filled, hitting start";
-		snd_pcm_start(pcm);
-
-		UpdatePlaybackPosition(pcm, position);
-		playing = true;
-		BOOST_SCOPE_EXIT_ALL(&) { playing = false; };
-		while (true)
-		{
-			// Sleep a bit, or until an event
-			cond.wait_for(lock, std::chrono::milliseconds{25});
-
-			if (message == Message::Close)
-			{
-				snd_pcm_drop(pcm);
-				return;
-			}
-
-			// Check for stop signal
-			if (message == Message::Stop || message == Message::Start)
-			{
-				LOG_D("audio/player/alsa") << "playback loop, stop signal";
-				snd_pcm_drop(pcm);
-				break;
-			}
-
-			// Fill buffer
-			snd_pcm_sframes_t tmp_pcm_avail = snd_pcm_avail(pcm);
-			if (tmp_pcm_avail == -EPIPE)
-			{
-				if (snd_pcm_recover(pcm, -EPIPE, 1) < 0)
-				{
-					LOG_D("audio/player/alsa") << "failed to recover from underrun";
-					return;
-				}
-				tmp_pcm_avail = snd_pcm_avail(pcm);
-			}
-			auto avail = std::min(tmp_pcm_avail, (snd_pcm_sframes_t)(end_position-position));
-			if (avail < 0)
-				continue;
-
-			{
-				decode_buffer.resize(avail * framesize);
-				provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
-				snd_pcm_sframes_t written = 0;
-				while (written <= 0)
-				{
-					written = snd_pcm_writei(pcm, decode_buffer.data(), avail);
-					if (written == -ESTRPIPE || written == -EPIPE)
-						snd_pcm_recover(pcm, written, 0);
-					else if (written == 0)
-						break;
-					else if (written < 0)
-					{
-						LOG_D("audio/player/alsa") << "error filling buffer, written=" << written;
-						return;
-					}
-				}
-				position += written;
-			}
-
-			UpdatePlaybackPosition(pcm, position);
-
-			// Check for end of playback
-			if (position >= end_position)
-			{
-				LOG_D("audio/player/alsa") << "playback loop, past end, draining";
-				snd_pcm_drain(pcm);
-				break;
-			}
-		}
-
-		playing = false;
-		LOG_D("audio/player/alsa") << "out of playback loop";
-
-		switch (snd_pcm_state(pcm))
-		{
-		case SND_PCM_STATE_OPEN:
-			// no clue what could have happened here, but start over
-			goto do_setup;
-
-		case SND_PCM_STATE_SETUP:
-			// we lost the preparedness?
-			snd_pcm_prepare(pcm);
-			break;
-
-		case SND_PCM_STATE_DISCONNECTED:
-			// lost device, close the handle and return error
-			return;
-
-		default:
-			// everything else should either be fine or impossible (here)
-			break;
-		}
-	}
+    snd_pcm_format_t pcm_format;
+    switch (provider->GetBytesPerSample()) {
+    case 1:
+        LOG_D("audio/player/alsa") << "format U8";
+        pcm_format = SND_PCM_FORMAT_U8;
+        break;
+    case 2:
+        LOG_D("audio/player/alsa") << "format S16_LE";
+        pcm_format = SND_PCM_FORMAT_S16_LE;
+        break;
+    default:
+        return;
+    }
+    if (snd_pcm_set_params(pcm,
+                           pcm_format,
+                           SND_PCM_ACCESS_RW_INTERLEAVED,
+                           provider->GetChannels(),
+                           provider->GetSampleRate(),
+                           1, // allow resample
+                           100 * 1000 // 100 milliseconds latency
+                          ) != 0)
+        return;
+    LOG_D("audio/player/alsa") << "set pcm params";
+
+    size_t framesize = provider->GetChannels() * provider->GetBytesPerSample();
+
+    while (true) {
+        // Wait for condition to trigger
+        while (message != Message::Start) {
+            cond.wait(lock, [&] { return message != Message::None; });
+            if (message == Message::Close)
+                return;
+            if (message == Message::Start && end_position > start_position)
+                break;
+            // Not playing, so don't need to stop...
+            message = Message::None;
+        }
+        message = Message::None;
+
+        LOG_D("audio/player/alsa") << "starting playback";
+        int64_t position = start_position;
+
+        // Initial buffer-fill
+        {
+            auto avail = std::min(snd_pcm_avail(pcm), (snd_pcm_sframes_t)(end_position - position));
+            decode_buffer.resize(avail * framesize);
+            provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
+
+            snd_pcm_sframes_t written = 0;
+            while (written <= 0) {
+                written = snd_pcm_writei(pcm, decode_buffer.data(), avail);
+                if (written == -ESTRPIPE)
+                    snd_pcm_recover(pcm, written, 0);
+                else if (written <= 0) {
+                    LOG_D("audio/player/alsa") << "error filling buffer";
+                    return;
+                }
+            }
+            position += written;
+        }
+
+        // Start playback
+        LOG_D("audio/player/alsa") << "initial buffer filled, hitting start";
+        snd_pcm_start(pcm);
+
+        UpdatePlaybackPosition(pcm, position);
+        playing = true;
+        BOOST_SCOPE_EXIT_ALL(&) { playing = false; };
+        while (true) {
+            // Sleep a bit, or until an event
+            cond.wait_for(lock, std::chrono::milliseconds{25});
+
+            if (message == Message::Close) {
+                snd_pcm_drop(pcm);
+                return;
+            }
+
+            // Check for stop signal
+            if (message == Message::Stop || message == Message::Start) {
+                LOG_D("audio/player/alsa") << "playback loop, stop signal";
+                snd_pcm_drop(pcm);
+                break;
+            }
+
+            // Fill buffer
+            snd_pcm_sframes_t tmp_pcm_avail = snd_pcm_avail(pcm);
+            if (tmp_pcm_avail == -EPIPE) {
+                if (snd_pcm_recover(pcm, -EPIPE, 1) < 0) {
+                    LOG_D("audio/player/alsa") << "failed to recover from underrun";
+                    return;
+                }
+                tmp_pcm_avail = snd_pcm_avail(pcm);
+            }
+            auto avail = std::min(tmp_pcm_avail, (snd_pcm_sframes_t)(end_position - position));
+            if (avail < 0)
+                continue;
+
+            {
+                decode_buffer.resize(avail * framesize);
+                provider->GetAudioWithVolume(decode_buffer.data(), position, avail, volume);
+                snd_pcm_sframes_t written = 0;
+                while (written <= 0) {
+                    written = snd_pcm_writei(pcm, decode_buffer.data(), avail);
+                    if (written == -ESTRPIPE || written == -EPIPE)
+                        snd_pcm_recover(pcm, written, 0);
+                    else if (written == 0)
+                        break;
+                    else if (written < 0) {
+                        LOG_D("audio/player/alsa") << "error filling buffer, written=" << written;
+                        return;
+                    }
+                }
+                position += written;
+            }
+
+            UpdatePlaybackPosition(pcm, position);
+
+            // Check for end of playback
+            if (position >= end_position) {
+                LOG_D("audio/player/alsa") << "playback loop, past end, draining";
+                snd_pcm_drain(pcm);
+                break;
+            }
+        }
+
+        playing = false;
+        LOG_D("audio/player/alsa") << "out of playback loop";
+
+        switch (snd_pcm_state(pcm)) {
+        case SND_PCM_STATE_OPEN:
+            // no clue what could have happened here, but start over
+            goto do_setup;
+
+        case SND_PCM_STATE_SETUP:
+            // we lost the preparedness?
+            snd_pcm_prepare(pcm);
+            break;
+
+        case SND_PCM_STATE_DISCONNECTED:
+            // lost device, close the handle and return error
+            return;
+
+        default:
+            // everything else should either be fine or impossible (here)
+            break;
+        }
+    }
 }
 
 AlsaPlayer::AlsaPlayer(agi::AudioProvider *provider) try
 : AudioPlayer(provider)
-, thread(&AlsaPlayer::PlaybackThread, this)
+    , thread(&AlsaPlayer::PlaybackThread, this)
 {
 }
-catch (std::system_error const&) {
-	throw AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed");
+catch (std::system_error const &)
+{
+    throw AudioPlayerOpenError("AlsaPlayer: Creating the playback thread failed");
 }
 
 AlsaPlayer::~AlsaPlayer()
 {
-	{
-		std::unique_lock<std::mutex> lock(mutex);
-		message = Message::Close;
-		cond.notify_all();
-	}
+    {
+        std::unique_lock<std::mutex> lock(mutex);
+        message = Message::Close;
+        cond.notify_all();
+    }
 
-	thread.join();
+    thread.join();
 }
 
 void AlsaPlayer::Play(int64_t start, int64_t count)
 {
-	std::unique_lock<std::mutex> lock(mutex);
-	message = Message::Start;
-	start_position = start;
-	end_position = start + count;
-	cond.notify_all();
+    std::unique_lock<std::mutex> lock(mutex);
+    message = Message::Start;
+    start_position = start;
+    end_position = start + count;
+    cond.notify_all();
 }
 
 void AlsaPlayer::Stop()
 {
-	std::unique_lock<std::mutex> lock(mutex);
-	message = Message::Stop;
-	cond.notify_all();
+    std::unique_lock<std::mutex> lock(mutex);
+    message = Message::Stop;
+    cond.notify_all();
 }
 
 void AlsaPlayer::SetEndPosition(int64_t pos)
 {
-	std::unique_lock<std::mutex> lock(mutex);
-	end_position = pos;
+    std::unique_lock<std::mutex> lock(mutex);
+    end_position = pos;
 }
 
 int64_t AlsaPlayer::GetCurrentPosition()
 {
-	int64_t lastpos;
-	clock::time_point lasttime;
+    int64_t lastpos;
+    clock::time_point lasttime;
 
-	{
-		std::unique_lock<std::mutex> playback_lock;
-		lastpos = last_position;
-		lasttime = last_position_time;
-	}
+    {
+        std::unique_lock<std::mutex> playback_lock;
+        lastpos = last_position;
+        lasttime = last_position_time;
+    }
 
-	auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - lasttime).count();
-	return lastpos + ms * provider->GetSampleRate() / 1000;
+    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() - lasttime).count();
+    return lastpos + ms * provider->GetSampleRate() / 1000;
 }
 }
 
 std::unique_ptr<AudioPlayer> CreateAlsaPlayer(agi::AudioProvider *provider, wxWindow *)
 {
-	return agi::make_unique<AlsaPlayer>(provider);
+    return agi::make_unique<AlsaPlayer>(provider);
 }
 
 #endif // WITH_ALSA
diff --git a/src/audio_player_dsound.cpp b/src/audio_player_dsound.cpp
index 01b47b3544fc4b3d35881702e58a6eaeebf9de6c..a570bd7a7c63e79f3e4ec42724387a6f8229f36f 100644
--- a/src/audio_player_dsound.cpp
+++ b/src/audio_player_dsound.cpp
@@ -50,325 +50,336 @@ namespace {
 class DirectSoundPlayer;
 
 class DirectSoundPlayerThread final : public wxThread {
-	DirectSoundPlayer *parent;
-	HANDLE stopnotify;
+    DirectSoundPlayer *parent;
+    HANDLE stopnotify;
 
 public:
-	void Stop(); // Notify thread to stop audio playback. Thread safe.
-	DirectSoundPlayerThread(DirectSoundPlayer *parent);
-	~DirectSoundPlayerThread();
+    void Stop(); // Notify thread to stop audio playback. Thread safe.
+    DirectSoundPlayerThread(DirectSoundPlayer *parent);
+    ~DirectSoundPlayerThread();
 
-	wxThread::ExitCode Entry();
+    wxThread::ExitCode Entry();
 };
 
 class DirectSoundPlayer final : public AudioPlayer {
-	friend class DirectSoundPlayerThread;
+    friend class DirectSoundPlayerThread;
 
-	volatile bool playing = false;
-	float volume = 1.0f;
-	int offset = 0;
+    volatile bool playing = false;
+    float volume = 1.0f;
+    int offset = 0;
 
-	DWORD bufSize = 0;
-	volatile int64_t playPos = 0;
-	int64_t startPos = 0;
-	volatile int64_t endPos = 0;
-	DWORD startTime = 0;
+    DWORD bufSize = 0;
+    volatile int64_t playPos = 0;
+    int64_t startPos = 0;
+    volatile int64_t endPos = 0;
+    DWORD startTime = 0;
 
-	IDirectSound8 *directSound = nullptr;
-	IDirectSoundBuffer8 *buffer = nullptr;
+    IDirectSound8 *directSound = nullptr;
+    IDirectSoundBuffer8 *buffer = nullptr;
 
-	bool FillBuffer(bool fill);
-	DirectSoundPlayerThread *thread = nullptr;
+    bool FillBuffer(bool fill);
+    DirectSoundPlayerThread *thread = nullptr;
 
 public:
-	DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent);
-	~DirectSoundPlayer();
+    DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent);
+    ~DirectSoundPlayer();
 
-	void Play(int64_t start,int64_t count);
-	void Stop();
+    void Play(int64_t start, int64_t count);
+    void Stop();
 
-	bool IsPlaying() { return playing; }
+    bool IsPlaying() { return playing; }
 
-	int64_t GetEndPosition() { return endPos; }
-	int64_t GetCurrentPosition();
-	void SetEndPosition(int64_t pos);
+    int64_t GetEndPosition() { return endPos; }
+    int64_t GetCurrentPosition();
+    void SetEndPosition(int64_t pos);
 
-	void SetVolume(double vol) { volume = vol; }
+    void SetVolume(double vol) { volume = vol; }
 };
 
 DirectSoundPlayer::DirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent)
-: AudioPlayer(provider)
+    : AudioPlayer(provider)
 {
-	// Initialize the DirectSound object
-	HRESULT res;
-	res = DirectSoundCreate8(&DSDEVID_DefaultPlayback,&directSound,nullptr); // TODO: support selecting audio device
-	if (FAILED(res)) throw AudioPlayerOpenError("Failed initializing DirectSound");
-
-	// Set DirectSound parameters
-	directSound->SetCooperativeLevel((HWND)parent->GetHandle(),DSSCL_PRIORITY);
-
-	// Create the wave format structure
-	WAVEFORMATEX waveFormat;
-	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
-	waveFormat.nSamplesPerSec = provider->GetSampleRate();
-	waveFormat.nChannels = provider->GetChannels();
-	waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
-	waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
-	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
-	waveFormat.cbSize = sizeof(waveFormat);
-
-	// Create the buffer initializer
-	int aim = waveFormat.nAvgBytesPerSec * 15/100; // 150 ms buffer
-	int min = DSBSIZE_MIN;
-	int max = DSBSIZE_MAX;
-	bufSize = std::min(std::max(min,aim),max);
-	DSBUFFERDESC desc;
-	desc.dwSize = sizeof(DSBUFFERDESC);
-	desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
-	desc.dwBufferBytes = bufSize;
-	desc.dwReserved = 0;
-	desc.lpwfxFormat = &waveFormat;
-	desc.guid3DAlgorithm = GUID_NULL;
-
-	// Create the buffer
-	IDirectSoundBuffer *buf;
-	res = directSound->CreateSoundBuffer(&desc,&buf,nullptr);
-	if (res != DS_OK) throw AudioPlayerOpenError("Failed creating DirectSound buffer");
-
-	// Copy interface to buffer
-	res = buf->QueryInterface(IID_IDirectSoundBuffer8,(LPVOID*) &buffer);
-	if (res != S_OK) throw AudioPlayerOpenError("Failed casting interface to IDirectSoundBuffer8");
-
-	// Set data
-	offset = 0;
+    // Initialize the DirectSound object
+    HRESULT res;
+    res = DirectSoundCreate8(&DSDEVID_DefaultPlayback, &directSound, nullptr); // TODO: support selecting audio device
+    if (FAILED(res)) throw AudioPlayerOpenError("Failed initializing DirectSound");
+
+    // Set DirectSound parameters
+    directSound->SetCooperativeLevel((HWND)parent->GetHandle(), DSSCL_PRIORITY);
+
+    // Create the wave format structure
+    WAVEFORMATEX waveFormat;
+    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
+    waveFormat.nSamplesPerSec = provider->GetSampleRate();
+    waveFormat.nChannels = provider->GetChannels();
+    waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    waveFormat.cbSize = sizeof(waveFormat);
+
+    // Create the buffer initializer
+    int aim = waveFormat.nAvgBytesPerSec * 15 / 100; // 150 ms buffer
+    int min = DSBSIZE_MIN;
+    int max = DSBSIZE_MAX;
+    bufSize = std::min(std::max(min, aim), max);
+    DSBUFFERDESC desc;
+    desc.dwSize = sizeof(DSBUFFERDESC);
+    desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
+    desc.dwBufferBytes = bufSize;
+    desc.dwReserved = 0;
+    desc.lpwfxFormat = &waveFormat;
+    desc.guid3DAlgorithm = GUID_NULL;
+
+    // Create the buffer
+    IDirectSoundBuffer *buf;
+    res = directSound->CreateSoundBuffer(&desc, &buf, nullptr);
+    if (res != DS_OK) throw AudioPlayerOpenError("Failed creating DirectSound buffer");
+
+    // Copy interface to buffer
+    res = buf->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID *) &buffer);
+    if (res != S_OK) throw AudioPlayerOpenError("Failed casting interface to IDirectSoundBuffer8");
+
+    // Set data
+    offset = 0;
 }
 
-DirectSoundPlayer::~DirectSoundPlayer() {
-	Stop();
+DirectSoundPlayer::~DirectSoundPlayer()
+{
+    Stop();
 
-	if (buffer)
-		buffer->Release();
+    if (buffer)
+        buffer->Release();
 
-	if (directSound)
-		directSound->Release();
+    if (directSound)
+        directSound->Release();
 }
 
-bool DirectSoundPlayer::FillBuffer(bool fill) {
-	if (playPos >= endPos) return false;
-
-	// Variables
-	HRESULT res;
-	void *ptr1, *ptr2;
-	unsigned long int size1, size2;
-	int bytesps = provider->GetBytesPerSample();
-
-	// To write length
-	int toWrite = 0;
-	if (fill) {
-		toWrite = bufSize;
-	}
-	else {
-		DWORD bufplay;
-		res = buffer->GetCurrentPosition(&bufplay, nullptr);
-		if (FAILED(res)) return false;
-		toWrite = (int)bufplay - (int)offset;
-		if (toWrite < 0) toWrite += bufSize;
-	}
-	if (toWrite == 0) return true;
-
-	// Make sure we only get as many samples as are available
-	if (playPos + toWrite/bytesps > endPos) {
-		toWrite = (endPos - playPos) * bytesps;
-	}
-
-	// If we're going to fill the entire buffer (ie. at start of playback) start by zeroing it out
-	// If it's not zeroed out we might have a playback selection shorter than the buffer
-	// and then everything after the playback selection will be junk, which we don't want played.
-	if (fill) {
+bool DirectSoundPlayer::FillBuffer(bool fill)
+{
+    if (playPos >= endPos) return false;
+
+    // Variables
+    HRESULT res;
+    void *ptr1, *ptr2;
+    unsigned long int size1, size2;
+    int bytesps = provider->GetBytesPerSample();
+
+    // To write length
+    int toWrite = 0;
+    if (fill) {
+        toWrite = bufSize;
+    }
+    else {
+        DWORD bufplay;
+        res = buffer->GetCurrentPosition(&bufplay, nullptr);
+        if (FAILED(res)) return false;
+        toWrite = (int)bufplay - (int)offset;
+        if (toWrite < 0) toWrite += bufSize;
+    }
+    if (toWrite == 0) return true;
+
+    // Make sure we only get as many samples as are available
+    if (playPos + toWrite / bytesps > endPos) {
+        toWrite = (endPos - playPos) * bytesps;
+    }
+
+    // If we're going to fill the entire buffer (ie. at start of playback) start by zeroing it out
+    // If it's not zeroed out we might have a playback selection shorter than the buffer
+    // and then everything after the playback selection will be junk, which we don't want played.
+    if (fill) {
 RetryClear:
-		res = buffer->Lock(0, bufSize, &ptr1, &size1, &ptr2, &size2, 0);
-		if (res == DSERR_BUFFERLOST) {
-			buffer->Restore();
-			goto RetryClear;
-		}
-		memset(ptr1, 0, size1);
-		memset(ptr2, 0, size2);
-		buffer->Unlock(ptr1, size1, ptr2, size2);
-	}
-
-	// Lock buffer
+        res = buffer->Lock(0, bufSize, &ptr1, &size1, &ptr2, &size2, 0);
+        if (res == DSERR_BUFFERLOST) {
+            buffer->Restore();
+            goto RetryClear;
+        }
+        memset(ptr1, 0, size1);
+        memset(ptr2, 0, size2);
+        buffer->Unlock(ptr1, size1, ptr2, size2);
+    }
+
+    // Lock buffer
 RetryLock:
-	if (fill) {
-		res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);
-	}
-	else {
-		res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);//DSBLOCK_FROMWRITECURSOR);
-	}
+    if (fill) {
+        res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);
+    }
+    else {
+        res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);//DSBLOCK_FROMWRITECURSOR);
+    }
 
-	// Buffer lost?
-	if (res == DSERR_BUFFERLOST) {
-		LOG_D("audio/player/dsound1") << "lost dsound buffer";
-		buffer->Restore();
-		goto RetryLock;
-	}
+    // Buffer lost?
+    if (res == DSERR_BUFFERLOST) {
+        LOG_D("audio/player/dsound1") << "lost dsound buffer";
+        buffer->Restore();
+        goto RetryLock;
+    }
 
-	if (FAILED(res)) return false;
+    if (FAILED(res)) return false;
 
-	// Convert size to number of samples
-	unsigned long int count1 = size1 / bytesps;
-	unsigned long int count2 = size2 / bytesps;
+    // Convert size to number of samples
+    unsigned long int count1 = size1 / bytesps;
+    unsigned long int count2 = size2 / bytesps;
 
-	LOG_D_IF(count1, "audio/player/dsound1") << "DS fill: " << (unsigned long)playPos << " -> " << (unsigned long)playPos+count1;
-	LOG_D_IF(count2, "audio/player/dsound1") << "DS fill: " << (unsigned long)playPos+count1 << " -> " << (unsigned long)playPos+count1+count2;
-	LOG_D_IF(!count1 && !count2, "audio/player/dsound1") << "DS fill: nothing";
+    LOG_D_IF(count1, "audio/player/dsound1") << "DS fill: " << (unsigned long)playPos << " -> " << (unsigned long)playPos + count1;
+    LOG_D_IF(count2, "audio/player/dsound1") << "DS fill: " << (unsigned long)playPos + count1 << " -> " << (unsigned long)playPos + count1 + count2;
+    LOG_D_IF(!count1 && !count2, "audio/player/dsound1") << "DS fill: nothing";
 
-	// Get source wave
-	if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume);
-	if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
-	playPos += count1+count2;
+    // Get source wave
+    if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume);
+    if (count2) provider->GetAudioWithVolume(ptr2, playPos + count1, count2, volume);
+    playPos += count1 + count2;
 
-	buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
+    buffer->Unlock(ptr1, count1 * bytesps, ptr2, count2 * bytesps);
 
-	offset = (offset + count1*bytesps + count2*bytesps) % bufSize;
+    offset = (offset + count1 * bytesps + count2 * bytesps) % bufSize;
 
-	return playPos < endPos;
+    return playPos < endPos;
 }
 
-void DirectSoundPlayer::Play(int64_t start,int64_t count) {
-	// Make sure that it's stopped
-	Stop();
-	// The thread is now guaranteed dead
-
-	HRESULT res;
-
-	// We sure better have a buffer
-	assert(buffer);
-
-	// Set variables
-	startPos = start;
-	endPos = start+count;
-	playPos = start;
-	offset = 0;
-
-	// Fill whole buffer
-	FillBuffer(true);
-
-	DWORD play_flag = 0;
-	if (count*provider->GetBytesPerSample() > bufSize) {
-		// Start thread
-		thread = new DirectSoundPlayerThread(this);
-		thread->Create();
-		thread->Run();
-		play_flag = DSBPLAY_LOOPING;
-	}
-
-	// Play
-	buffer->SetCurrentPosition(0);
-	res = buffer->Play(0,0,play_flag);
-	if (SUCCEEDED(res)) playing = true;
-	startTime = GetTickCount();
+void DirectSoundPlayer::Play(int64_t start, int64_t count)
+{
+    // Make sure that it's stopped
+    Stop();
+    // The thread is now guaranteed dead
+
+    HRESULT res;
+
+    // We sure better have a buffer
+    assert(buffer);
+
+    // Set variables
+    startPos = start;
+    endPos = start + count;
+    playPos = start;
+    offset = 0;
+
+    // Fill whole buffer
+    FillBuffer(true);
+
+    DWORD play_flag = 0;
+    if (count * provider->GetBytesPerSample() > bufSize) {
+        // Start thread
+        thread = new DirectSoundPlayerThread(this);
+        thread->Create();
+        thread->Run();
+        play_flag = DSBPLAY_LOOPING;
+    }
+
+    // Play
+    buffer->SetCurrentPosition(0);
+    res = buffer->Play(0, 0, play_flag);
+    if (SUCCEEDED(res)) playing = true;
+    startTime = GetTickCount();
 }
 
-void DirectSoundPlayer::Stop() {
-	// Stop the thread
-	if (thread) {
-		if (thread->IsAlive()) {
-			thread->Stop();
-			thread->Wait();
-		}
-		thread = nullptr;
-	}
-	// The thread is now guaranteed dead and there are no concurrency problems to worry about
-
-	if (buffer) buffer->Stop(); // the thread should have done this already
-
-	// Reset variables
-	playing = false;
-	playPos = 0;
-	startPos = 0;
-	endPos = 0;
-	offset = 0;
+void DirectSoundPlayer::Stop()
+{
+    // Stop the thread
+    if (thread) {
+        if (thread->IsAlive()) {
+            thread->Stop();
+            thread->Wait();
+        }
+        thread = nullptr;
+    }
+    // The thread is now guaranteed dead and there are no concurrency problems to worry about
+
+    if (buffer) buffer->Stop(); // the thread should have done this already
+
+    // Reset variables
+    playing = false;
+    playPos = 0;
+    startPos = 0;
+    endPos = 0;
+    offset = 0;
 }
 
-void DirectSoundPlayer::SetEndPosition(int64_t pos) {
-	if (playing) endPos = pos;
+void DirectSoundPlayer::SetEndPosition(int64_t pos)
+{
+    if (playing) endPos = pos;
 }
 
-int64_t DirectSoundPlayer::GetCurrentPosition() {
-	// Check if buffer is loaded
-	if (!buffer || !playing) return 0;
-
-	// FIXME: this should be based on not duration played but actual sample being heard
-	// (during vidoeo playback, cur_frame might get changed to resync)
-	DWORD curtime = GetTickCount();
-	int64_t tdiff = curtime - startTime;
-	return startPos + tdiff * provider->GetSampleRate() / 1000;
+int64_t DirectSoundPlayer::GetCurrentPosition()
+{
+    // Check if buffer is loaded
+    if (!buffer || !playing) return 0;
+
+    // FIXME: this should be based on not duration played but actual sample being heard
+    // (during vidoeo playback, cur_frame might get changed to resync)
+    DWORD curtime = GetTickCount();
+    int64_t tdiff = curtime - startTime;
+    return startPos + tdiff * provider->GetSampleRate() / 1000;
 }
 
-DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
-	parent = par;
-	stopnotify = CreateEvent(nullptr, true, false, nullptr);
+DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE)
+{
+    parent = par;
+    stopnotify = CreateEvent(nullptr, true, false, nullptr);
 }
 
-DirectSoundPlayerThread::~DirectSoundPlayerThread() {
-	CloseHandle(stopnotify);
+DirectSoundPlayerThread::~DirectSoundPlayerThread()
+{
+    CloseHandle(stopnotify);
 }
 
-wxThread::ExitCode DirectSoundPlayerThread::Entry() {
-	CoInitialize(0);
-
-	// Wake up thread every half second to fill buffer as needed
-	// This more or less assumes the buffer is at least one second long
-	while (WaitForSingleObject(stopnotify, 50) == WAIT_TIMEOUT) {
-		if (!parent->FillBuffer(false)) {
-			// FillBuffer returns false when end of stream is reached
-			LOG_D("audio/player/dsound1") << "DS thread hit end of stream";
-			break;
-		}
-	}
-
-	// Now fill buffer with silence
-	DWORD bytesFilled = 0;
-	while (WaitForSingleObject(stopnotify, 50) == WAIT_TIMEOUT) {
-		void *buf1, *buf2;
-		DWORD size1, size2;
-		DWORD playpos;
-		HRESULT res;
-		res = parent->buffer->GetCurrentPosition(&playpos, nullptr);
-		if (FAILED(res)) break;
-		int toWrite = playpos - parent->offset;
-		while (toWrite < 0) toWrite += parent->bufSize;
-		res = parent->buffer->Lock(parent->offset, toWrite, &buf1, &size1, &buf2, &size2, 0);
-		if (FAILED(res)) break;
-		if (size1) memset(buf1, 0, size1);
-		if (size2) memset(buf2, 0, size2);
-		LOG_D_IF(size1, "audio/player/dsound1") << "DS blnk:" << (unsigned long)parent->playPos+bytesFilled << " -> " << (unsigned long)parent->playPos+bytesFilled+size1;
-		LOG_D_IF(size2, "audio/player/dsound1") << "DS blnk:" << (unsigned long)parent->playPos+bytesFilled+size1 << " -> " << (unsigned long)parent->playPos+bytesFilled+size1+size2;
-		bytesFilled += size1 + size2;
-		parent->buffer->Unlock(buf1, size1, buf2, size2);
-		if (bytesFilled > parent->bufSize) break;
-		parent->offset = (parent->offset + size1 + size2) % parent->bufSize;
-	}
-
-	WaitForSingleObject(stopnotify, 150);
-
-	LOG_D("audio/player/dsound1") << "DS thread dead";
-
-	parent->playing = false;
-	parent->buffer->Stop();
-
-	CoUninitialize();
-	return 0;
+wxThread::ExitCode DirectSoundPlayerThread::Entry()
+{
+    CoInitialize(0);
+
+    // Wake up thread every half second to fill buffer as needed
+    // This more or less assumes the buffer is at least one second long
+    while (WaitForSingleObject(stopnotify, 50) == WAIT_TIMEOUT) {
+        if (!parent->FillBuffer(false)) {
+            // FillBuffer returns false when end of stream is reached
+            LOG_D("audio/player/dsound1") << "DS thread hit end of stream";
+            break;
+        }
+    }
+
+    // Now fill buffer with silence
+    DWORD bytesFilled = 0;
+    while (WaitForSingleObject(stopnotify, 50) == WAIT_TIMEOUT) {
+        void *buf1, *buf2;
+        DWORD size1, size2;
+        DWORD playpos;
+        HRESULT res;
+        res = parent->buffer->GetCurrentPosition(&playpos, nullptr);
+        if (FAILED(res)) break;
+        int toWrite = playpos - parent->offset;
+        while (toWrite < 0) toWrite += parent->bufSize;
+        res = parent->buffer->Lock(parent->offset, toWrite, &buf1, &size1, &buf2, &size2, 0);
+        if (FAILED(res)) break;
+        if (size1) memset(buf1, 0, size1);
+        if (size2) memset(buf2, 0, size2);
+        LOG_D_IF(size1, "audio/player/dsound1") << "DS blnk:" << (unsigned long)parent->playPos + bytesFilled << " -> " << (unsigned long)parent->playPos + bytesFilled + size1;
+        LOG_D_IF(size2, "audio/player/dsound1") << "DS blnk:" << (unsigned long)parent->playPos + bytesFilled + size1 << " -> " << (unsigned long)parent->playPos + bytesFilled + size1 + size2;
+        bytesFilled += size1 + size2;
+        parent->buffer->Unlock(buf1, size1, buf2, size2);
+        if (bytesFilled > parent->bufSize) break;
+        parent->offset = (parent->offset + size1 + size2) % parent->bufSize;
+    }
+
+    WaitForSingleObject(stopnotify, 150);
+
+    LOG_D("audio/player/dsound1") << "DS thread dead";
+
+    parent->playing = false;
+    parent->buffer->Stop();
+
+    CoUninitialize();
+    return 0;
 }
 
-void DirectSoundPlayerThread::Stop() {
-	// Increase the stopnotify by one, causing a wait for it to succeed
-	SetEvent(stopnotify);
+void DirectSoundPlayerThread::Stop()
+{
+    // Increase the stopnotify by one, causing a wait for it to succeed
+    SetEvent(stopnotify);
 }
 }
 
-std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent) {
-	return agi::make_unique<DirectSoundPlayer>(provider, parent);
+std::unique_ptr<AudioPlayer> CreateDirectSoundPlayer(agi::AudioProvider *provider, wxWindow *parent)
+{
+    return agi::make_unique<DirectSoundPlayer>(provider, parent);
 }
 
 #endif // WITH_DIRECTSOUND
diff --git a/src/audio_player_dsound2.cpp b/src/audio_player_dsound2.cpp
index dd7bf86800e2e031dda474b4656d9542b33684ae..dc184f535c4cb1a9b08fc13e69cd6ffc1d7c2548 100644
--- a/src/audio_player_dsound2.cpp
+++ b/src/audio_player_dsound2.cpp
@@ -59,86 +59,83 @@ class DirectSoundPlayer2Thread;
 /// and performs all playback operations, and use the player object as a proxy to
 /// send commands to the playback thread.
 class DirectSoundPlayer2 final : public AudioPlayer {
-	/// The playback thread
-	std::unique_ptr<DirectSoundPlayer2Thread> thread;
+    /// The playback thread
+    std::unique_ptr<DirectSoundPlayer2Thread> thread;
 
-	/// Desired length in milliseconds to write ahead of the playback cursor
-	int WantedLatency;
+    /// Desired length in milliseconds to write ahead of the playback cursor
+    int WantedLatency;
 
-	/// Multiplier for WantedLatency to get total buffer length
-	int BufferLength;
+    /// Multiplier for WantedLatency to get total buffer length
+    int BufferLength;
 
-	/// @brief Tell whether playback thread is alive
-	/// @return True if there is a playback thread and it's ready
-	bool IsThreadAlive();
+    /// @brief Tell whether playback thread is alive
+    /// @return True if there is a playback thread and it's ready
+    bool IsThreadAlive();
 
 public:
-	/// @brief Constructor
-	DirectSoundPlayer2(agi::AudioProvider *provider, wxWindow *parent);
-	/// @brief Destructor
-	~DirectSoundPlayer2();
-
-	/// @brief Start playback
-	/// @param start First audio frame to play
-	/// @param count Number of audio frames to play
-	void Play(int64_t start,int64_t count);
-
-	/// @brief Stop audio playback
-	/// @param timerToo Whether to also stop the playback update timer
-	void Stop();
-
-	/// @brief Tell whether playback is active
-	/// @return True if audio is playing back
-	bool IsPlaying();
-
-	/// @brief Get playback end position
-	/// @return Audio frame index
-	///
-	/// Returns 0 if playback is stopped or there is no playback thread
-	int64_t GetEndPosition();
-	/// @brief Get approximate playback position
-	/// @return Index of audio frame user is currently hearing
-	///
-	/// Returns 0 if playback is stopped or there is no playback thread
-	int64_t GetCurrentPosition();
-
-	/// @brief Change playback end position
-	/// @param pos New end position
-	void SetEndPosition(int64_t pos);
-
-	/// @brief Change playback volume
-	/// @param vol Amplification factor
-	void SetVolume(double vol);
+    /// @brief Constructor
+    DirectSoundPlayer2(agi::AudioProvider *provider, wxWindow *parent);
+    /// @brief Destructor
+    ~DirectSoundPlayer2();
+
+    /// @brief Start playback
+    /// @param start First audio frame to play
+    /// @param count Number of audio frames to play
+    void Play(int64_t start, int64_t count);
+
+    /// @brief Stop audio playback
+    /// @param timerToo Whether to also stop the playback update timer
+    void Stop();
+
+    /// @brief Tell whether playback is active
+    /// @return True if audio is playing back
+    bool IsPlaying();
+
+    /// @brief Get playback end position
+    /// @return Audio frame index
+    ///
+    /// Returns 0 if playback is stopped or there is no playback thread
+    int64_t GetEndPosition();
+    /// @brief Get approximate playback position
+    /// @return Index of audio frame user is currently hearing
+    ///
+    /// Returns 0 if playback is stopped or there is no playback thread
+    int64_t GetCurrentPosition();
+
+    /// @brief Change playback end position
+    /// @param pos New end position
+    void SetEndPosition(int64_t pos);
+
+    /// @brief Change playback volume
+    /// @param vol Amplification factor
+    void SetVolume(double vol);
 };
 
 /// @brief RAII support class to init and de-init the COM library
 struct COMInitialization {
 
-	/// Flag set if an inited COM library is managed
-	bool inited = false;
-
-	/// @brief Destructor, de-inits COM if it is inited
-	~COMInitialization()
-	{
-		if (inited) CoUninitialize();
-	}
-
-	/// @brief Initialise the COM library as single-threaded apartment if isn't already inited by us
-	bool Init()
-	{
-		if (!inited)
-		{
-			if (SUCCEEDED(CoInitialize(nullptr)))
-				inited = true;
-		}
-		return inited;
-	}
+    /// Flag set if an inited COM library is managed
+    bool inited = false;
+
+    /// @brief Destructor, de-inits COM if it is inited
+    ~COMInitialization() {
+        if (inited) CoUninitialize();
+    }
+
+    /// @brief Initialise the COM library as single-threaded apartment if isn't already inited by us
+    bool Init() {
+        if (!inited) {
+            if (SUCCEEDED(CoInitialize(nullptr)))
+                inited = true;
+        }
+        return inited;
+    }
 };
 
 struct ReleaseCOMObject {
-	void operator()(IUnknown *obj) {
-		if (obj) obj->Release();
-	}
+    void operator()(IUnknown *obj) {
+        if (obj) obj->Release();
+    }
 };
 
 template<typename T>
@@ -146,18 +143,16 @@ using COMObjectRetainer = std::unique_ptr<T, ReleaseCOMObject>;
 
 /// @brief RAII wrapper around Win32 HANDLE type
 struct Win32KernelHandle final : public agi::scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)> {
-	/// @brief Create with a managed handle
-	/// @param handle Win32 handle to manage
-	Win32KernelHandle(HANDLE handle = 0)
-	: scoped_holder(handle, CloseHandle)
-	{
-	}
-
-	Win32KernelHandle& operator=(HANDLE new_handle)
-	{
-		scoped_holder::operator=(new_handle);
-		return *this;
-	}
+    /// @brief Create with a managed handle
+    /// @param handle Win32 handle to manage
+    Win32KernelHandle(HANDLE handle = 0)
+        : scoped_holder(handle, CloseHandle) {
+    }
+
+    Win32KernelHandle &operator=(HANDLE new_handle) {
+        scoped_holder::operator=(new_handle);
+        return *this;
+    }
 };
 
 /// @class DirectSoundPlayer2Thread
@@ -165,129 +160,129 @@ struct Win32KernelHandle final : public agi::scoped_holder<HANDLE, BOOL (__stdca
 ///
 /// Not based on wxThread, but uses Win32 threads directly
 class DirectSoundPlayer2Thread {
-	/// @brief Win32 thread entry point
-	/// @param parameter Pointer to our thread object
-	/// @return Thread return value, always 0 here
-	static unsigned int __stdcall ThreadProc(void *parameter);
-	/// @brief Thread entry point
-	void Run();
+    /// @brief Win32 thread entry point
+    /// @param parameter Pointer to our thread object
+    /// @return Thread return value, always 0 here
+    static unsigned int __stdcall ThreadProc(void *parameter);
+    /// @brief Thread entry point
+    void Run();
 
-	/// @brief Fill audio data into a locked buffer-pair and unlock the buffers
-	/// @param buf1        First buffer in pair
-	/// @param buf1sz      Byte-size of first buffer in pair
-	/// @param buf2        Second buffer in pair, or null
-	/// @param buf2sz      Byte-size of second buffer in pair
-	/// @param input_frame First audio frame to fill into buffers
-	/// @param bfr         DirectSound buffer object owning the buffer pair
-	/// @return Number of bytes written
-	DWORD FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr);
+    /// @brief Fill audio data into a locked buffer-pair and unlock the buffers
+    /// @param buf1        First buffer in pair
+    /// @param buf1sz      Byte-size of first buffer in pair
+    /// @param buf2        Second buffer in pair, or null
+    /// @param buf2sz      Byte-size of second buffer in pair
+    /// @param input_frame First audio frame to fill into buffers
+    /// @param bfr         DirectSound buffer object owning the buffer pair
+    /// @return Number of bytes written
+    DWORD FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr);
 
-	/// @brief Check for error state and throw exception if one occurred
-	void CheckError();
+    /// @brief Check for error state and throw exception if one occurred
+    void CheckError();
 
-	HWND parent;
+    HWND parent;
 
-	/// Win32 handle to the thread
-	Win32KernelHandle thread_handle;
+    /// Win32 handle to the thread
+    Win32KernelHandle thread_handle;
 
-	/// Event object, world to thread, set to start playback
-	Win32KernelHandle event_start_playback;
+    /// Event object, world to thread, set to start playback
+    Win32KernelHandle event_start_playback;
 
-	/// Event object, world to thread, set to stop playback
-	Win32KernelHandle event_stop_playback;
+    /// Event object, world to thread, set to stop playback
+    Win32KernelHandle event_stop_playback;
 
-	/// Event object, world to thread, set if playback end time was updated
-	Win32KernelHandle event_update_end_time;
+    /// Event object, world to thread, set if playback end time was updated
+    Win32KernelHandle event_update_end_time;
 
-	/// Event object, world to thread, set if the volume was changed
-	Win32KernelHandle event_set_volume;
+    /// Event object, world to thread, set if the volume was changed
+    Win32KernelHandle event_set_volume;
 
-	/// Event object, world to thread, set if the thread should end as soon as possible
-	Win32KernelHandle event_kill_self;
+    /// Event object, world to thread, set if the thread should end as soon as possible
+    Win32KernelHandle event_kill_self;
 
-	/// Event object, thread to world, set when the thread has entered its main loop
-	Win32KernelHandle thread_running;
+    /// Event object, thread to world, set when the thread has entered its main loop
+    Win32KernelHandle thread_running;
 
-	/// Event object, thread to world, set when playback is ongoing
-	Win32KernelHandle is_playing;
+    /// Event object, thread to world, set when playback is ongoing
+    Win32KernelHandle is_playing;
 
-	/// Event object, thread to world, set if an error state has occurred (implies thread is dying)
-	Win32KernelHandle error_happened;
+    /// Event object, thread to world, set if an error state has occurred (implies thread is dying)
+    Win32KernelHandle error_happened;
 
-	/// Statically allocated error message text describing reason for error_happened being set
-	const char *error_message = nullptr;
+    /// Statically allocated error message text describing reason for error_happened being set
+    const char *error_message = nullptr;
 
-	/// Playback volume, 1.0 is "unchanged"
-	double volume = 1.0;
+    /// Playback volume, 1.0 is "unchanged"
+    double volume = 1.0;
 
-	/// Audio frame to start playback at
-	int64_t start_frame = 0;
+    /// Audio frame to start playback at
+    int64_t start_frame = 0;
 
-	/// Audio frame to end playback at
-	int64_t end_frame = 0;
+    /// Audio frame to end playback at
+    int64_t end_frame = 0;
 
-	/// Desired length in milliseconds to write ahead of the playback cursor
-	int wanted_latency;
+    /// Desired length in milliseconds to write ahead of the playback cursor
+    int wanted_latency;
 
-	/// Multiplier for WantedLatency to get total buffer length
-	int buffer_length;
+    /// Multiplier for WantedLatency to get total buffer length
+    int buffer_length;
 
-	/// System millisecond timestamp of last playback start, used to calculate playback position
-	DWORD last_playback_restart;
+    /// System millisecond timestamp of last playback start, used to calculate playback position
+    DWORD last_playback_restart;
 
-	/// Audio provider to take sample data from
-	agi::AudioProvider *provider;
+    /// Audio provider to take sample data from
+    agi::AudioProvider *provider;
 
 public:
-	/// @brief Constructor, creates and starts playback thread
-	/// @param provider       Audio provider to take sample data from
-	/// @param WantedLatency Desired length in milliseconds to write ahead of the playback cursor
-	/// @param BufferLength  Multiplier for WantedLatency to get total buffer length
-	DirectSoundPlayer2Thread(agi::AudioProvider *provider, int WantedLatency, int BufferLength, wxWindow *parent);
-	/// @brief Destructor, waits for thread to have died
-	~DirectSoundPlayer2Thread();
-
-	/// @brief Start audio playback
-	/// @param start Audio frame to start playback at
-	/// @param count Number of audio frames to play
-	void Play(int64_t start, int64_t count);
-
-	/// @brief Stop audio playback
-	void Stop();
-
-	/// @brief Change audio playback end point
-	/// @param new_end_frame New last audio frame to play
-	///
-	/// Playback stops instantly if new_end_frame is before the current playback position
-	void SetEndFrame(int64_t new_end_frame);
-
-	/// @brief Change audio playback volume
-	/// @param new_volume New playback amplification factor, 1.0 is "unchanged"
-	void SetVolume(double new_volume);
-
-	/// @brief Tell whether audio playback is active
-	/// @return True if audio is being played back, false if it is not
-	bool IsPlaying();
-
-	/// @brief Get approximate current audio frame being heard by the user
-	/// @return Audio frame index
-	///
-	/// Returns 0 if not playing
-	int64_t GetCurrentFrame();
-
-	/// @brief Get audio playback end point
-	/// @return Audio frame index
-	int64_t GetEndFrame();
-
-	/// @brief Tell whether playback thread has died
-	/// @return True if thread is no longer running
-	bool IsDead();
+    /// @brief Constructor, creates and starts playback thread
+    /// @param provider       Audio provider to take sample data from
+    /// @param WantedLatency Desired length in milliseconds to write ahead of the playback cursor
+    /// @param BufferLength  Multiplier for WantedLatency to get total buffer length
+    DirectSoundPlayer2Thread(agi::AudioProvider *provider, int WantedLatency, int BufferLength, wxWindow *parent);
+    /// @brief Destructor, waits for thread to have died
+    ~DirectSoundPlayer2Thread();
+
+    /// @brief Start audio playback
+    /// @param start Audio frame to start playback at
+    /// @param count Number of audio frames to play
+    void Play(int64_t start, int64_t count);
+
+    /// @brief Stop audio playback
+    void Stop();
+
+    /// @brief Change audio playback end point
+    /// @param new_end_frame New last audio frame to play
+    ///
+    /// Playback stops instantly if new_end_frame is before the current playback position
+    void SetEndFrame(int64_t new_end_frame);
+
+    /// @brief Change audio playback volume
+    /// @param new_volume New playback amplification factor, 1.0 is "unchanged"
+    void SetVolume(double new_volume);
+
+    /// @brief Tell whether audio playback is active
+    /// @return True if audio is being played back, false if it is not
+    bool IsPlaying();
+
+    /// @brief Get approximate current audio frame being heard by the user
+    /// @return Audio frame index
+    ///
+    /// Returns 0 if not playing
+    int64_t GetCurrentFrame();
+
+    /// @brief Get audio playback end point
+    /// @return Audio frame index
+    int64_t GetEndFrame();
+
+    /// @brief Tell whether playback thread has died
+    /// @return True if thread is no longer running
+    bool IsDead();
 };
 
 unsigned int __stdcall DirectSoundPlayer2Thread::ThreadProc(void *parameter)
 {
-	static_cast<DirectSoundPlayer2Thread*>(parameter)->Run();
-	return 0;
+    static_cast<DirectSoundPlayer2Thread *>(parameter)->Run();
+    return 0;
 }
 
 /// Macro used to set error_message, error_happened and end the thread
@@ -301,526 +296,495 @@ unsigned int __stdcall DirectSoundPlayer2Thread::ThreadProc(void *parameter)
 
 void DirectSoundPlayer2Thread::Run()
 {
-	COMInitialization COM_library;
-	if (!COM_library.Init())
-		REPORT_ERROR("Could not initialise COM")
-
-	// Create DirectSound object
-	IDirectSound8 *ds_raw = nullptr;
-	if (FAILED(DirectSoundCreate8(&DSDEVID_DefaultPlayback, &ds_raw, nullptr)))
-		REPORT_ERROR("Cound not create DirectSound object")
-
-	COMObjectRetainer<IDirectSound8> ds(ds_raw);
-
-	// Ensure we can get interesting wave formats (unless we have PRIORITY we can only use a standard 8 bit format)
-	ds->SetCooperativeLevel(parent, DSSCL_PRIORITY);
-
-	// Describe the wave format
-	WAVEFORMATEX waveFormat;
-	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
-	waveFormat.nSamplesPerSec = provider->GetSampleRate();
-	waveFormat.nChannels = provider->GetChannels();
-	waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
-	waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
-	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
-	waveFormat.cbSize = sizeof(waveFormat);
-
-	// And the buffer itself
-	int aim = waveFormat.nAvgBytesPerSec * (wanted_latency*buffer_length)/1000;
-	int min = DSBSIZE_MIN;
-	int max = DSBSIZE_MAX;
-	DWORD bufSize = mid(min,aim,max); // size of entire playback buffer
-	DSBUFFERDESC desc;
-	desc.dwSize = sizeof(DSBUFFERDESC);
-	desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
-	desc.dwBufferBytes = bufSize;
-	desc.dwReserved = 0;
-	desc.lpwfxFormat = &waveFormat;
-	desc.guid3DAlgorithm = GUID_NULL;
-
-	// And then create the buffer
-	IDirectSoundBuffer *bfr7 = 0;
-	if FAILED(ds->CreateSoundBuffer(&desc, &bfr7, 0))
-		REPORT_ERROR("Could not create buffer")
-
-	// But it's an old version interface we get, query it for the DSound8 interface
-	IDirectSoundBuffer8 *bfr_raw = nullptr;
-	if (FAILED(bfr7->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&bfr_raw)))
-		REPORT_ERROR("Buffer doesn't support version 8 interface")
-	COMObjectRetainer<IDirectSoundBuffer8> bfr(bfr_raw);
-	bfr7->Release();
-	bfr7 = 0;
-
-	//wx Log Debug("DirectSoundPlayer2: Created buffer of %d bytes, supposed to be %d milliseconds or %d frames", bufSize, WANTED_LATENCY*BUFFER_LENGTH, bufSize/provider->GetBytesPerSample());
-
-	// Now we're ready to roll!
-	SetEvent(thread_running);
-	bool running = true;
-
-	HANDLE events_to_wait[] = {
-		event_start_playback,
-		event_stop_playback,
-		event_update_end_time,
-		event_set_volume,
-		event_kill_self
-	};
-
-	int64_t next_input_frame = 0;
-	DWORD buffer_offset = 0;
-	bool playback_should_be_running = false;
-	int current_latency = wanted_latency;
-	const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*provider->GetBytesPerSample()/1000;
-
-	while (running)
-	{
-		DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait)/sizeof(HANDLE), events_to_wait, FALSE, current_latency);
-
-		switch (wait_result)
-		{
-		case WAIT_OBJECT_0+0:
-			{
-				// Start or restart playback
-				bfr->Stop();
-
-				next_input_frame = start_frame;
-
-				DWORD buf_size; // size of buffer locked for filling
-				void *buf;
-				buffer_offset = 0;
-
-				if (FAILED(bfr->SetCurrentPosition(0)))
-					REPORT_ERROR("Could not reset playback buffer cursor before filling first buffer.")
-
-				HRESULT res = bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER);
-				if (FAILED(res))
-				{
-					if (res == DSERR_BUFFERLOST)
-					{
-						// Try to regain the buffer
-						if (FAILED(bfr->Restore()) ||
-							FAILED(bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER)))
-						{
-							REPORT_ERROR("Lost buffer and could not restore it.")
-						}
-					}
-					else
-					{
-						REPORT_ERROR("Could not lock buffer for playback.")
-					}
-				}
-
-				// Clear the buffer in case we can't fill it completely
-				memset(buf, 0, buf_size);
-
-				DWORD bytes_filled = FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.get());
-				buffer_offset += bytes_filled;
-				if (buffer_offset >= bufSize) buffer_offset -= bufSize;
-
-				if (FAILED(bfr->SetCurrentPosition(0)))
-					REPORT_ERROR("Could not reset playback buffer cursor before playback.")
-
-				if (bytes_filled < wanted_latency_bytes)
-				{
-					// Very short playback length, do without streaming playback
-					current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
-					if (FAILED(bfr->Play(0, 0, 0)))
-						REPORT_ERROR("Could not start single-buffer playback.")
-				}
-				else
-				{
-					// We filled the entire buffer so there's reason to do streaming playback
-					current_latency = wanted_latency;
-					if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING)))
-						REPORT_ERROR("Could not start looping playback.")
-				}
-
-				SetEvent(is_playing);
-				playback_should_be_running = true;
-
-				break;
-			}
-
-		case WAIT_OBJECT_0+1:
+    COMInitialization COM_library;
+    if (!COM_library.Init())
+        REPORT_ERROR("Could not initialise COM")
+
+        // Create DirectSound object
+        IDirectSound8 *ds_raw = nullptr;
+    if (FAILED(DirectSoundCreate8(&DSDEVID_DefaultPlayback, &ds_raw, nullptr)))
+        REPORT_ERROR("Cound not create DirectSound object")
+
+        COMObjectRetainer<IDirectSound8> ds(ds_raw);
+
+    // Ensure we can get interesting wave formats (unless we have PRIORITY we can only use a standard 8 bit format)
+    ds->SetCooperativeLevel(parent, DSSCL_PRIORITY);
+
+    // Describe the wave format
+    WAVEFORMATEX waveFormat;
+    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
+    waveFormat.nSamplesPerSec = provider->GetSampleRate();
+    waveFormat.nChannels = provider->GetChannels();
+    waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    waveFormat.cbSize = sizeof(waveFormat);
+
+    // And the buffer itself
+    int aim = waveFormat.nAvgBytesPerSec * (wanted_latency * buffer_length) / 1000;
+    int min = DSBSIZE_MIN;
+    int max = DSBSIZE_MAX;
+    DWORD bufSize = mid(min, aim, max); // size of entire playback buffer
+    DSBUFFERDESC desc;
+    desc.dwSize = sizeof(DSBUFFERDESC);
+    desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
+    desc.dwBufferBytes = bufSize;
+    desc.dwReserved = 0;
+    desc.lpwfxFormat = &waveFormat;
+    desc.guid3DAlgorithm = GUID_NULL;
+
+    // And then create the buffer
+    IDirectSoundBuffer *bfr7 = 0;
+    if FAILED(ds->CreateSoundBuffer(&desc, &bfr7, 0))
+        REPORT_ERROR("Could not create buffer")
+
+        // But it's an old version interface we get, query it for the DSound8 interface
+        IDirectSoundBuffer8 *bfr_raw = nullptr;
+    if (FAILED(bfr7->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID *)&bfr_raw)))
+        REPORT_ERROR("Buffer doesn't support version 8 interface")
+        COMObjectRetainer<IDirectSoundBuffer8> bfr(bfr_raw);
+    bfr7->Release();
+    bfr7 = 0;
+
+    //wx Log Debug("DirectSoundPlayer2: Created buffer of %d bytes, supposed to be %d milliseconds or %d frames", bufSize, WANTED_LATENCY*BUFFER_LENGTH, bufSize/provider->GetBytesPerSample());
+
+    // Now we're ready to roll!
+    SetEvent(thread_running);
+    bool running = true;
+
+    HANDLE events_to_wait[] = {
+        event_start_playback,
+        event_stop_playback,
+        event_update_end_time,
+        event_set_volume,
+        event_kill_self
+    };
+
+    int64_t next_input_frame = 0;
+    DWORD buffer_offset = 0;
+    bool playback_should_be_running = false;
+    int current_latency = wanted_latency;
+    const DWORD wanted_latency_bytes = wanted_latency * waveFormat.nSamplesPerSec * provider->GetBytesPerSample() / 1000;
+
+    while (running) {
+        DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait) / sizeof(HANDLE), events_to_wait, FALSE, current_latency);
+
+        switch (wait_result) {
+        case WAIT_OBJECT_0+0: {
+            // Start or restart playback
+            bfr->Stop();
+
+            next_input_frame = start_frame;
+
+            DWORD buf_size; // size of buffer locked for filling
+            void *buf;
+            buffer_offset = 0;
+
+            if (FAILED(bfr->SetCurrentPosition(0)))
+                REPORT_ERROR("Could not reset playback buffer cursor before filling first buffer.")
+
+                HRESULT res = bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER);
+            if (FAILED(res)) {
+                if (res == DSERR_BUFFERLOST) {
+                    // Try to regain the buffer
+                    if (FAILED(bfr->Restore()) ||
+                        FAILED(bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER))) {
+                        REPORT_ERROR("Lost buffer and could not restore it.")
+                    }
+                }
+                else {
+                    REPORT_ERROR("Could not lock buffer for playback.")
+                }
+            }
+
+            // Clear the buffer in case we can't fill it completely
+            memset(buf, 0, buf_size);
+
+            DWORD bytes_filled = FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.get());
+            buffer_offset += bytes_filled;
+            if (buffer_offset >= bufSize) buffer_offset -= bufSize;
+
+            if (FAILED(bfr->SetCurrentPosition(0)))
+                REPORT_ERROR("Could not reset playback buffer cursor before playback.")
+
+                if (bytes_filled < wanted_latency_bytes) {
+                    // Very short playback length, do without streaming playback
+                    current_latency = (bytes_filled * 1000) / (waveFormat.nSamplesPerSec * provider->GetBytesPerSample());
+                    if (FAILED(bfr->Play(0, 0, 0)))
+                        REPORT_ERROR("Could not start single-buffer playback.")
+                    }
+                else {
+                    // We filled the entire buffer so there's reason to do streaming playback
+                    current_latency = wanted_latency;
+                    if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING)))
+                        REPORT_ERROR("Could not start looping playback.")
+                    }
+
+            SetEvent(is_playing);
+            playback_should_be_running = true;
+
+            break;
+        }
+
+        case WAIT_OBJECT_0+1:
 stop_playback:
-			// Stop playing
-			bfr->Stop();
-			ResetEvent(is_playing);
-			playback_should_be_running = false;
-			break;
-
-		case WAIT_OBJECT_0+2:
-			// Set end frame
-			if (end_frame <= next_input_frame)
-			{
-				goto stop_playback;
-			}
-
-			// If the user is dragging the start or end point in the audio display
-			// the set end frame events might come in faster than the timeouts happen
-			// and then new data never get filled into the buffer. See bug #915.
-			goto do_fill_buffer;
-
-		case WAIT_OBJECT_0+3:
-			// Change volume
-			// We aren't thread safe right now, filling the buffers grabs volume directly
-			// from the field set by the controlling thread, but it shouldn't be a major
-			// problem if race conditions do occur, just some momentary distortion.
-			goto do_fill_buffer;
-
-		case WAIT_OBJECT_0+4:
-			// Perform suicide
-			running = false;
-			goto stop_playback;
-
-		case WAIT_TIMEOUT:
-do_fill_buffer:
-			{
-				// Time to fill more into buffer
-				if (!playback_should_be_running)
-					break;
-
-				DWORD status;
-				if (FAILED(bfr->GetStatus(&status)))
-					REPORT_ERROR("Could not get playback buffer status")
-
-				if (!(status & DSBSTATUS_LOOPING))
-				{
-					// Not looping playback...
-					// hopefully we only triggered timeout after being done with the buffer
-					goto stop_playback;
-				}
-
-				DWORD play_cursor;
-				if (FAILED(bfr->GetCurrentPosition(&play_cursor, 0)))
-					REPORT_ERROR("Could not get play cursor position for filling buffer.")
-
-				int bytes_needed = (int)play_cursor - (int)buffer_offset;
-				if (bytes_needed < 0) bytes_needed += (int)bufSize;
-
-				// Requesting zero buffer makes Windows cry, and zero buffer seemed to be
-				// a common request on Windows 7. (Maybe related to the new timer coalescing?)
-				// We'll probably get non-zero bytes requested on the next iteration.
-				if (bytes_needed == 0)
-					break;
-
-				DWORD buf1sz, buf2sz;
-				void *buf1, *buf2;
-
-				assert(bytes_needed > 0);
-				assert(buffer_offset < bufSize);
-				assert((DWORD)bytes_needed <= bufSize);
-
-				HRESULT res = bfr->Lock(buffer_offset, bytes_needed, &buf1, &buf1sz, &buf2, &buf2sz, 0);
-				switch (res)
-				{
-				case DSERR_BUFFERLOST:
-					// Try to regain the buffer
-					// When the buffer was lost the entire contents was lost too, so we have to start over
-					if (SUCCEEDED(bfr->Restore()) &&
-					    SUCCEEDED(bfr->Lock(0, bufSize, &buf1, &buf1sz, &buf2, &buf2sz, 0)) &&
-					    SUCCEEDED(bfr->Play(0, 0, DSBPLAY_LOOPING)))
-					{
-						LOG_D("audio/player/dsound") << "Lost and restored buffer";
-						break;
-					}
-					REPORT_ERROR("Lost buffer and could not restore it.")
-
-				case DSERR_INVALIDPARAM:
-					REPORT_ERROR("Invalid parameters to IDirectSoundBuffer8::Lock().")
-
-				case DSERR_INVALIDCALL:
-					REPORT_ERROR("Invalid call to IDirectSoundBuffer8::Lock().")
-
-				case DSERR_PRIOLEVELNEEDED:
-					REPORT_ERROR("Incorrect priority level set on DirectSoundBuffer8 object.")
-
-				default:
-					if (FAILED(res))
-						REPORT_ERROR("Could not lock audio buffer, unknown error.")
-					break;
-				}
-
-				DWORD bytes_filled = FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.get());
-				buffer_offset += bytes_filled;
-				if (buffer_offset >= bufSize) buffer_offset -= bufSize;
-
-				if (bytes_filled < 1024)
-				{
-					// Arbitrary low number, we filled in very little so better get back to filling in the rest with silence
-					// really fast... set latency to zero in this case.
-					current_latency = 0;
-				}
-				else if (bytes_filled < wanted_latency_bytes)
-				{
-					// Didn't fill as much as we wanted to, let's get back to filling sooner than normal
-					current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
-				}
-				else
-				{
-					// Plenty filled in, do regular latency
-					current_latency = wanted_latency;
-				}
-
-				break;
-			}
-
-		default:
-			REPORT_ERROR("Something bad happened while waiting on events in playback loop, either the wait failed or an event object was abandoned.")
-			break;
-		}
-	}
+            // Stop playing
+            bfr->Stop();
+            ResetEvent(is_playing);
+            playback_should_be_running = false;
+            break;
+
+        case WAIT_OBJECT_0+2:
+            // Set end frame
+            if (end_frame <= next_input_frame) {
+                goto stop_playback;
+            }
+
+            // If the user is dragging the start or end point in the audio display
+            // the set end frame events might come in faster than the timeouts happen
+            // and then new data never get filled into the buffer. See bug #915.
+            goto do_fill_buffer;
+
+        case WAIT_OBJECT_0+3:
+            // Change volume
+            // We aren't thread safe right now, filling the buffers grabs volume directly
+            // from the field set by the controlling thread, but it shouldn't be a major
+            // problem if race conditions do occur, just some momentary distortion.
+            goto do_fill_buffer;
+
+        case WAIT_OBJECT_0+4:
+            // Perform suicide
+            running = false;
+            goto stop_playback;
+
+        case WAIT_TIMEOUT:
+do_fill_buffer: {
+                // Time to fill more into buffer
+                if (!playback_should_be_running)
+                    break;
+
+                DWORD status;
+                if (FAILED(bfr->GetStatus(&status)))
+                    REPORT_ERROR("Could not get playback buffer status")
+
+                    if (!(status & DSBSTATUS_LOOPING)) {
+                        // Not looping playback...
+                        // hopefully we only triggered timeout after being done with the buffer
+                        goto stop_playback;
+                    }
+
+                DWORD play_cursor;
+                if (FAILED(bfr->GetCurrentPosition(&play_cursor, 0)))
+                    REPORT_ERROR("Could not get play cursor position for filling buffer.")
+
+                    int bytes_needed = (int)play_cursor - (int)buffer_offset;
+                if (bytes_needed < 0) bytes_needed += (int)bufSize;
+
+                // Requesting zero buffer makes Windows cry, and zero buffer seemed to be
+                // a common request on Windows 7. (Maybe related to the new timer coalescing?)
+                // We'll probably get non-zero bytes requested on the next iteration.
+                if (bytes_needed == 0)
+                    break;
+
+                DWORD buf1sz, buf2sz;
+                void *buf1, *buf2;
+
+                assert(bytes_needed > 0);
+                assert(buffer_offset < bufSize);
+                assert((DWORD)bytes_needed <= bufSize);
+
+                HRESULT res = bfr->Lock(buffer_offset, bytes_needed, &buf1, &buf1sz, &buf2, &buf2sz, 0);
+                switch (res) {
+                case DSERR_BUFFERLOST:
+                    // Try to regain the buffer
+                    // When the buffer was lost the entire contents was lost too, so we have to start over
+                    if (SUCCEEDED(bfr->Restore()) &&
+                        SUCCEEDED(bfr->Lock(0, bufSize, &buf1, &buf1sz, &buf2, &buf2sz, 0)) &&
+                        SUCCEEDED(bfr->Play(0, 0, DSBPLAY_LOOPING))) {
+                        LOG_D("audio/player/dsound") << "Lost and restored buffer";
+                        break;
+                    }
+                    REPORT_ERROR("Lost buffer and could not restore it.")
+
+                case DSERR_INVALIDPARAM:
+                    REPORT_ERROR("Invalid parameters to IDirectSoundBuffer8::Lock().")
+
+                case DSERR_INVALIDCALL:
+                    REPORT_ERROR("Invalid call to IDirectSoundBuffer8::Lock().")
+
+                case DSERR_PRIOLEVELNEEDED:
+                    REPORT_ERROR("Incorrect priority level set on DirectSoundBuffer8 object.")
+
+                default:
+                    if (FAILED(res))
+                        REPORT_ERROR("Could not lock audio buffer, unknown error.")
+                        break;
+                }
+
+                DWORD bytes_filled = FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.get());
+                buffer_offset += bytes_filled;
+                if (buffer_offset >= bufSize) buffer_offset -= bufSize;
+
+                if (bytes_filled < 1024) {
+                    // Arbitrary low number, we filled in very little so better get back to filling in the rest with silence
+                    // really fast... set latency to zero in this case.
+                    current_latency = 0;
+                }
+                else if (bytes_filled < wanted_latency_bytes) {
+                    // Didn't fill as much as we wanted to, let's get back to filling sooner than normal
+                    current_latency = (bytes_filled * 1000) / (waveFormat.nSamplesPerSec * provider->GetBytesPerSample());
+                }
+                else {
+                    // Plenty filled in, do regular latency
+                    current_latency = wanted_latency;
+                }
+
+                break;
+            }
+
+        default:
+            REPORT_ERROR("Something bad happened while waiting on events in playback loop, either the wait failed or an event object was abandoned.")
+            break;
+        }
+    }
 }
 
 #undef REPORT_ERROR
 
 DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr)
 {
-	// Assume buffers have been locked and are ready to be filled
+    // Assume buffers have been locked and are ready to be filled
 
-	DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample();
-	DWORD buf1szf = buf1sz / bytes_per_frame;
-	DWORD buf2szf = buf2sz / bytes_per_frame;
+    DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample();
+    DWORD buf1szf = buf1sz / bytes_per_frame;
+    DWORD buf2szf = buf2sz / bytes_per_frame;
 
-	if (input_frame >= end_frame)
-	{
-		// Silence
+    if (input_frame >= end_frame) {
+        // Silence
 
-		if (buf1)
-			memset(buf1, 0, buf1sz);
+        if (buf1)
+            memset(buf1, 0, buf1sz);
 
-		if (buf2)
-			memset(buf2, 0, buf2sz);
+        if (buf2)
+            memset(buf2, 0, buf2sz);
 
-		input_frame += buf1szf + buf2szf;
+        input_frame += buf1szf + buf2szf;
 
-		bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // should be checking for success
+        bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // should be checking for success
 
-		return buf1sz + buf2sz;
-	}
+        return buf1sz + buf2sz;
+    }
 
-	if (buf1 && buf1sz)
-	{
-		if (buf1szf + input_frame > end_frame)
-		{
-			buf1szf = end_frame - input_frame;
-			buf1sz = buf1szf * bytes_per_frame;
-			buf2szf = 0;
-			buf2sz = 0;
-		}
+    if (buf1 && buf1sz) {
+        if (buf1szf + input_frame > end_frame) {
+            buf1szf = end_frame - input_frame;
+            buf1sz = buf1szf * bytes_per_frame;
+            buf2szf = 0;
+            buf2sz = 0;
+        }
 
-		provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume);
+        provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume);
 
-		input_frame += buf1szf;
-	}
+        input_frame += buf1szf;
+    }
 
-	if (buf2 && buf2sz)
-	{
-		if (buf2szf + input_frame > end_frame)
-		{
-			buf2szf = end_frame - input_frame;
-			buf2sz = buf2szf * bytes_per_frame;
-		}
+    if (buf2 && buf2sz) {
+        if (buf2szf + input_frame > end_frame) {
+            buf2szf = end_frame - input_frame;
+            buf2sz = buf2szf * bytes_per_frame;
+        }
 
-		provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume);
+        provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume);
 
-		input_frame += buf2szf;
-	}
+        input_frame += buf2szf;
+    }
 
-	bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // bad? should check for success
+    bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // bad? should check for success
 
-	return buf1sz + buf2sz;
+    return buf1sz + buf2sz;
 }
 
 void DirectSoundPlayer2Thread::CheckError()
 {
-	try
-	{
-		switch (WaitForSingleObject(error_happened, 0))
-		{
-		case WAIT_OBJECT_0:
-			throw error_message;
-
-		case WAIT_ABANDONED:
-			throw "The DirectShowPlayer2Thread error signal event was abandoned, somehow. This should not happen.";
-
-		case WAIT_FAILED:
-			throw "Failed checking state of DirectShowPlayer2Thread error signal event.";
-
-		case WAIT_TIMEOUT:
-		default:
-			return;
-		}
-	}
-	catch (...)
-	{
-		ResetEvent(is_playing);
-		ResetEvent(thread_running);
-		throw;
-	}
+    try {
+        switch (WaitForSingleObject(error_happened, 0)) {
+        case WAIT_OBJECT_0:
+            throw error_message;
+
+        case WAIT_ABANDONED:
+            throw "The DirectShowPlayer2Thread error signal event was abandoned, somehow. This should not happen.";
+
+        case WAIT_FAILED:
+            throw "Failed checking state of DirectShowPlayer2Thread error signal event.";
+
+        case WAIT_TIMEOUT:
+        default:
+            return;
+        }
+    }
+    catch (...) {
+        ResetEvent(is_playing);
+        ResetEvent(thread_running);
+        throw;
+    }
 }
 
 DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(agi::AudioProvider *provider, int WantedLatency, int BufferLength, wxWindow *parent)
-: parent((HWND)parent->GetHandle())
-, event_start_playback  (CreateEvent(0, FALSE, FALSE, 0))
-, event_stop_playback   (CreateEvent(0, FALSE, FALSE, 0))
-, event_update_end_time (CreateEvent(0, FALSE, FALSE, 0))
-, event_set_volume      (CreateEvent(0, FALSE, FALSE, 0))
-, event_kill_self       (CreateEvent(0, FALSE, FALSE, 0))
-, thread_running        (CreateEvent(0,  TRUE, FALSE, 0))
-, is_playing            (CreateEvent(0,  TRUE, FALSE, 0))
-, error_happened        (CreateEvent(0, FALSE, FALSE, 0))
-, wanted_latency(WantedLatency)
-, buffer_length(BufferLength)
-, provider(provider)
+    : parent((HWND)parent->GetHandle())
+    , event_start_playback  (CreateEvent(0, FALSE, FALSE, 0))
+    , event_stop_playback   (CreateEvent(0, FALSE, FALSE, 0))
+    , event_update_end_time (CreateEvent(0, FALSE, FALSE, 0))
+    , event_set_volume      (CreateEvent(0, FALSE, FALSE, 0))
+    , event_kill_self       (CreateEvent(0, FALSE, FALSE, 0))
+    , thread_running        (CreateEvent(0,  TRUE, FALSE, 0))
+    , is_playing            (CreateEvent(0,  TRUE, FALSE, 0))
+    , error_happened        (CreateEvent(0, FALSE, FALSE, 0))
+    , wanted_latency(WantedLatency)
+    , buffer_length(BufferLength)
+    , provider(provider)
 {
-	thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
+    thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);
 
-	if (!thread_handle)
-		throw AudioPlayerOpenError("Failed creating playback thread in DirectSoundPlayer2. This is bad.");
+    if (!thread_handle)
+        throw AudioPlayerOpenError("Failed creating playback thread in DirectSoundPlayer2. This is bad.");
 
-	HANDLE running_or_error[] = { thread_running, error_happened };
-	switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE))
-	{
-	case WAIT_OBJECT_0:
-		// running, all good
-		return;
+    HANDLE running_or_error[] = { thread_running, error_happened };
+    switch (WaitForMultipleObjects(2, running_or_error, FALSE, INFINITE)) {
+    case WAIT_OBJECT_0:
+        // running, all good
+        return;
 
-	case WAIT_OBJECT_0 + 1:
-		// error happened, we fail
-		throw AudioPlayerOpenError(error_message);
+    case WAIT_OBJECT_0 + 1:
+        // error happened, we fail
+        throw AudioPlayerOpenError(error_message);
 
-	default:
-		throw AudioPlayerOpenError("Failed wait for thread start or thread error in DirectSoundPlayer2. This is bad.");
-	}
+    default:
+        throw AudioPlayerOpenError("Failed wait for thread start or thread error in DirectSoundPlayer2. This is bad.");
+    }
 }
 
 DirectSoundPlayer2Thread::~DirectSoundPlayer2Thread()
 {
-	SetEvent(event_kill_self);
-	WaitForSingleObject(thread_handle, INFINITE);
+    SetEvent(event_kill_self);
+    WaitForSingleObject(thread_handle, INFINITE);
 }
 
 void DirectSoundPlayer2Thread::Play(int64_t start, int64_t count)
 {
-	CheckError();
-
-	start_frame = start;
-	end_frame = start+count;
-	SetEvent(event_start_playback);
-
-	last_playback_restart = GetTickCount();
-
-	// Block until playback actually begins to avoid race conditions with
-	// checking if playback is in progress
-	HANDLE events_to_wait[] = { is_playing, error_happened };
-	switch (WaitForMultipleObjects(2, events_to_wait, FALSE, INFINITE))
-	{
-	case WAIT_OBJECT_0+0: // Playing
-		LOG_D("audio/player/dsound") << "Playback begun";
-		break;
-	case WAIT_OBJECT_0+1: // Error
-		throw error_message;
-	default:
-		throw agi::InternalError("Unexpected result from WaitForMultipleObjects in DirectSoundPlayer2Thread::Play");
-	}
+    CheckError();
+
+    start_frame = start;
+    end_frame = start + count;
+    SetEvent(event_start_playback);
+
+    last_playback_restart = GetTickCount();
+
+    // Block until playback actually begins to avoid race conditions with
+    // checking if playback is in progress
+    HANDLE events_to_wait[] = { is_playing, error_happened };
+    switch (WaitForMultipleObjects(2, events_to_wait, FALSE, INFINITE)) {
+    case WAIT_OBJECT_0+0: // Playing
+        LOG_D("audio/player/dsound") << "Playback begun";
+        break;
+    case WAIT_OBJECT_0+1: // Error
+        throw error_message;
+    default:
+        throw agi::InternalError("Unexpected result from WaitForMultipleObjects in DirectSoundPlayer2Thread::Play");
+    }
 }
 
 void DirectSoundPlayer2Thread::Stop()
 {
-	CheckError();
+    CheckError();
 
-	SetEvent(event_stop_playback);
+    SetEvent(event_stop_playback);
 }
 
 void DirectSoundPlayer2Thread::SetEndFrame(int64_t new_end_frame)
 {
-	CheckError();
+    CheckError();
 
-	end_frame = new_end_frame;
-	SetEvent(event_update_end_time);
+    end_frame = new_end_frame;
+    SetEvent(event_update_end_time);
 }
 
 void DirectSoundPlayer2Thread::SetVolume(double new_volume)
 {
-	CheckError();
+    CheckError();
 
-	volume = new_volume;
-	SetEvent(event_set_volume);
+    volume = new_volume;
+    SetEvent(event_set_volume);
 }
 
 bool DirectSoundPlayer2Thread::IsPlaying()
 {
-	CheckError();
+    CheckError();
 
-	switch (WaitForSingleObject(is_playing, 0))
-	{
-	case WAIT_ABANDONED:
-		throw "The DirectShowPlayer2Thread playback state event was abandoned, somehow. This should not happen.";
+    switch (WaitForSingleObject(is_playing, 0)) {
+    case WAIT_ABANDONED:
+        throw "The DirectShowPlayer2Thread playback state event was abandoned, somehow. This should not happen.";
 
-	case WAIT_FAILED:
-		throw "Failed checking state of DirectShowPlayer2Thread playback state event.";
+    case WAIT_FAILED:
+        throw "Failed checking state of DirectShowPlayer2Thread playback state event.";
 
-	case WAIT_OBJECT_0:
-		return true;
+    case WAIT_OBJECT_0:
+        return true;
 
-	case WAIT_TIMEOUT:
-	default:
-		return false;
-	}
+    case WAIT_TIMEOUT:
+    default:
+        return false;
+    }
 }
 
 int64_t DirectSoundPlayer2Thread::GetCurrentFrame()
 {
-	CheckError();
+    CheckError();
 
-	if (!IsPlaying()) return 0;
+    if (!IsPlaying()) return 0;
 
-	int64_t milliseconds_elapsed = GetTickCount() - last_playback_restart;
+    int64_t milliseconds_elapsed = GetTickCount() - last_playback_restart;
 
-	return start_frame + milliseconds_elapsed * provider->GetSampleRate() / 1000;
+    return start_frame + milliseconds_elapsed * provider->GetSampleRate() / 1000;
 }
 
 int64_t DirectSoundPlayer2Thread::GetEndFrame()
 {
-	CheckError();
+    CheckError();
 
-	return end_frame;
+    return end_frame;
 }
 
 bool DirectSoundPlayer2Thread::IsDead()
 {
-	switch (WaitForSingleObject(thread_running, 0))
-	{
-	case WAIT_OBJECT_0:
-		return false;
-
-	default:
-		return true;
-	}
+    switch (WaitForSingleObject(thread_running, 0)) {
+    case WAIT_OBJECT_0:
+        return false;
+
+    default:
+        return true;
+    }
 }
 
 DirectSoundPlayer2::DirectSoundPlayer2(agi::AudioProvider *provider, wxWindow *parent)
-: AudioPlayer(provider)
+    : AudioPlayer(provider)
 {
-	// The buffer will hold BufferLength times WantedLatency milliseconds of audio
-	WantedLatency = OPT_GET("Player/Audio/DirectSound/Buffer Latency")->GetInt();
-	BufferLength = OPT_GET("Player/Audio/DirectSound/Buffer Length")->GetInt();
-
-	// sanity checking
-	if (WantedLatency <= 0)
-		WantedLatency = 100;
-	if (BufferLength <= 0)
-		BufferLength = 5;
-
-	try
-	{
-		thread = agi::make_unique<DirectSoundPlayer2Thread>(provider, WantedLatency, BufferLength, parent);
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-		throw AudioPlayerOpenError(msg);
-	}
+    // The buffer will hold BufferLength times WantedLatency milliseconds of audio
+    WantedLatency = OPT_GET("Player/Audio/DirectSound/Buffer Latency")->GetInt();
+    BufferLength = OPT_GET("Player/Audio/DirectSound/Buffer Length")->GetInt();
+
+    // sanity checking
+    if (WantedLatency <= 0)
+        WantedLatency = 100;
+    if (BufferLength <= 0)
+        BufferLength = 5;
+
+    try {
+        thread = agi::make_unique<DirectSoundPlayer2Thread>(provider, WantedLatency, BufferLength, parent);
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+        throw AudioPlayerOpenError(msg);
+    }
 }
 
 DirectSoundPlayer2::~DirectSoundPlayer2()
@@ -829,107 +793,93 @@ DirectSoundPlayer2::~DirectSoundPlayer2()
 
 bool DirectSoundPlayer2::IsThreadAlive()
 {
-	if (thread && thread->IsDead())
-	{
-		thread.reset();
-	}
+    if (thread && thread->IsDead()) {
+        thread.reset();
+    }
 
-	return !!thread;
+    return !!thread;
 }
 
-void DirectSoundPlayer2::Play(int64_t start,int64_t count)
+void DirectSoundPlayer2::Play(int64_t start, int64_t count)
 {
-	try
-	{
-		thread->Play(start, count);
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-	}
+    try {
+        thread->Play(start, count);
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+    }
 }
 
 void DirectSoundPlayer2::Stop()
 {
-	try
-	{
-		if (IsThreadAlive()) thread->Stop();
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-	}
+    try {
+        if (IsThreadAlive()) thread->Stop();
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+    }
 }
 
 bool DirectSoundPlayer2::IsPlaying()
 {
-	try
-	{
-		if (!IsThreadAlive()) return false;
-		return thread->IsPlaying();
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-		return false;
-	}
+    try {
+        if (!IsThreadAlive()) return false;
+        return thread->IsPlaying();
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+        return false;
+    }
 }
 
 int64_t DirectSoundPlayer2::GetEndPosition()
 {
-	try
-	{
-		if (!IsThreadAlive()) return 0;
-		return thread->GetEndFrame();
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-		return 0;
-	}
+    try {
+        if (!IsThreadAlive()) return 0;
+        return thread->GetEndFrame();
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+        return 0;
+    }
 }
 
 int64_t DirectSoundPlayer2::GetCurrentPosition()
 {
-	try
-	{
-		if (!IsThreadAlive()) return 0;
-		return thread->GetCurrentFrame();
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-		return 0;
-	}
+    try {
+        if (!IsThreadAlive()) return 0;
+        return thread->GetCurrentFrame();
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+        return 0;
+    }
 }
 
 void DirectSoundPlayer2::SetEndPosition(int64_t pos)
 {
-	try
-	{
-		if (IsThreadAlive()) thread->SetEndFrame(pos);
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-	}
+    try {
+        if (IsThreadAlive()) thread->SetEndFrame(pos);
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+    }
 }
 
 void DirectSoundPlayer2::SetVolume(double vol)
 {
-	try
-	{
-		if (IsThreadAlive()) thread->SetVolume(vol);
-	}
-	catch (const char *msg)
-	{
-		LOG_E("audio/player/dsound") << msg;
-	}
+    try {
+        if (IsThreadAlive()) thread->SetVolume(vol);
+    }
+    catch (const char *msg) {
+        LOG_E("audio/player/dsound") << msg;
+    }
 }
 }
 
-std::unique_ptr<AudioPlayer> CreateDirectSound2Player(agi::AudioProvider *provider, wxWindow *parent) {
-	return agi::make_unique<DirectSoundPlayer2>(provider, parent);
+std::unique_ptr<AudioPlayer> CreateDirectSound2Player(agi::AudioProvider *provider, wxWindow *parent)
+{
+    return agi::make_unique<DirectSoundPlayer2>(provider, parent);
 }
 
 #endif // WITH_DIRECTSOUND
diff --git a/src/audio_player_openal.cpp b/src/audio_player_openal.cpp
index b0f8372bdc5134d4982e733ab19b30595182965f..5fb529f4e801f38ce6d1b2e27bb6c485e8822263 100644
--- a/src/audio_player_openal.cpp
+++ b/src/audio_player_openal.cpp
@@ -63,249 +63,248 @@
 
 namespace {
 class OpenALPlayer final : public AudioPlayer, wxTimer {
-	/// Number of OpenAL buffers to use
-	static const ALsizei num_buffers = 8;
+    /// Number of OpenAL buffers to use
+    static const ALsizei num_buffers = 8;
 
-	bool playing = false; ///< Is audio currently playing?
+    bool playing = false; ///< Is audio currently playing?
 
-	float volume = 1.f; ///< Current audio volume
-	ALsizei samplerate; ///< Sample rate of the audio
-	int bpf; ///< Bytes per frame
+    float volume = 1.f; ///< Current audio volume
+    ALsizei samplerate; ///< Sample rate of the audio
+    int bpf; ///< Bytes per frame
 
-	int64_t start_frame = 0; ///< First frame of playbacka
-	int64_t cur_frame = 0; ///< Next frame to write to playback buffers
-	int64_t end_frame = 0; ///< Last frame to play
+    int64_t start_frame = 0; ///< First frame of playbacka
+    int64_t cur_frame = 0; ///< Next frame to write to playback buffers
+    int64_t end_frame = 0; ///< Last frame to play
 
-	ALCdevice *device = nullptr; ///< OpenAL device handle
-	ALCcontext *context = nullptr; ///< OpenAL sound context
-	ALuint buffers[num_buffers]; ///< OpenAL sound buffers
-	ALuint source = 0; ///< OpenAL playback source
+    ALCdevice *device = nullptr; ///< OpenAL device handle
+    ALCcontext *context = nullptr; ///< OpenAL sound context
+    ALuint buffers[num_buffers]; ///< OpenAL sound buffers
+    ALuint source = 0; ///< OpenAL playback source
 
-	/// Index into buffers, first free (unqueued) buffer to be filled
-	ALsizei buf_first_free = 0;
+    /// Index into buffers, first free (unqueued) buffer to be filled
+    ALsizei buf_first_free = 0;
 
-	/// Index into buffers, first queued (non-free) buffer
-	ALsizei buf_first_queued = 0;
+    /// Index into buffers, first queued (non-free) buffer
+    ALsizei buf_first_queued = 0;
 
-	/// Number of free buffers
-	ALsizei buffers_free = 0;
+    /// Number of free buffers
+    ALsizei buffers_free = 0;
 
-	/// Number of buffers which have been fully played since playback was last started
-	ALsizei buffers_played = 0;
+    /// Number of buffers which have been fully played since playback was last started
+    ALsizei buffers_played = 0;
 
-	wxStopWatch playback_segment_timer;
+    wxStopWatch playback_segment_timer;
 
-	/// Buffer to decode audio into
-	std::vector<char> decode_buffer;
+    /// Buffer to decode audio into
+    std::vector<char> decode_buffer;
 
-	/// Fill count OpenAL buffers
-	void FillBuffers(ALsizei count);
+    /// Fill count OpenAL buffers
+    void FillBuffers(ALsizei count);
 
-	/// wxTimer override to periodically fill available buffers
-	void Notify() override;
+    /// wxTimer override to periodically fill available buffers
+    void Notify() override;
 
-	void InitContext();
-	void TeardownContext();
+    void InitContext();
+    void TeardownContext();
 
 public:
-	OpenALPlayer(agi::AudioProvider *provider);
-	~OpenALPlayer();
+    OpenALPlayer(agi::AudioProvider *provider);
+    ~OpenALPlayer();
 
-	void Play(int64_t start,int64_t count) override;
-	void Stop() override;
-	bool IsPlaying() override { return playing; }
+    void Play(int64_t start, int64_t count) override;
+    void Stop() override;
+    bool IsPlaying() override { return playing; }
 
-	int64_t GetEndPosition() override { return end_frame; }
-	int64_t GetCurrentPosition() override;
-	void SetEndPosition(int64_t pos) override;
+    int64_t GetEndPosition() override { return end_frame; }
+    int64_t GetCurrentPosition() override;
+    void SetEndPosition(int64_t pos) override;
 
-	void SetVolume(double vol) override { volume = vol; }
+    void SetVolume(double vol) override { volume = vol; }
 };
 
 OpenALPlayer::OpenALPlayer(agi::AudioProvider *provider)
-: AudioPlayer(provider)
-, samplerate(provider->GetSampleRate())
-, bpf(provider->GetChannels() * provider->GetBytesPerSample())
+    : AudioPlayer(provider)
+    , samplerate(provider->GetSampleRate())
+    , bpf(provider->GetChannels() * provider->GetBytesPerSample())
 {
-	device = alcOpenDevice(nullptr);
-	if (!device) throw AudioPlayerOpenError("Failed opening default OpenAL device");
+    device = alcOpenDevice(nullptr);
+    if (!device) throw AudioPlayerOpenError("Failed opening default OpenAL device");
 
-	// Determine buffer length
-	decode_buffer.resize(samplerate * bpf / num_buffers / 2); // buffers for half a second of audio
+    // Determine buffer length
+    decode_buffer.resize(samplerate * bpf / num_buffers / 2); // buffers for half a second of audio
 }
 
 OpenALPlayer::~OpenALPlayer()
 {
-	Stop();
-	alcCloseDevice(device);
+    Stop();
+    alcCloseDevice(device);
 }
 
 void OpenALPlayer::InitContext()
 {
-	if (context) return;
-
-	try {
-		// Create context
-		context = alcCreateContext(device, nullptr);
-		if (!context) throw AudioPlayerOpenError("Failed creating OpenAL context");
-		if (!alcMakeContextCurrent(context)) throw AudioPlayerOpenError("Failed selecting OpenAL context");
-
-		// Clear error code
-		alGetError();
-
-		// Generate buffers
-		alGenBuffers(num_buffers, buffers);
-		if (alGetError() != AL_NO_ERROR) throw AudioPlayerOpenError("Error generating OpenAL buffers");
-
-		// Generate source
-		alGenSources(1, &source);
-		if (alGetError() != AL_NO_ERROR) {
-			alDeleteBuffers(num_buffers, buffers);
-			throw AudioPlayerOpenError("Error generating OpenAL source");
-		}
-	}
-	catch (...)
-	{
-		alcDestroyContext(context);
-		context = nullptr;
-		throw;
-	}
+    if (context) return;
+
+    try {
+        // Create context
+        context = alcCreateContext(device, nullptr);
+        if (!context) throw AudioPlayerOpenError("Failed creating OpenAL context");
+        if (!alcMakeContextCurrent(context)) throw AudioPlayerOpenError("Failed selecting OpenAL context");
+
+        // Clear error code
+        alGetError();
+
+        // Generate buffers
+        alGenBuffers(num_buffers, buffers);
+        if (alGetError() != AL_NO_ERROR) throw AudioPlayerOpenError("Error generating OpenAL buffers");
+
+        // Generate source
+        alGenSources(1, &source);
+        if (alGetError() != AL_NO_ERROR) {
+            alDeleteBuffers(num_buffers, buffers);
+            throw AudioPlayerOpenError("Error generating OpenAL source");
+        }
+    }
+    catch (...) {
+        alcDestroyContext(context);
+        context = nullptr;
+        throw;
+    }
 }
 
 void OpenALPlayer::TeardownContext()
 {
-	if (!context) return;
-	alcMakeContextCurrent(context);
-	alDeleteSources(1, &source);
-	alDeleteBuffers(num_buffers, buffers);
-	alcMakeContextCurrent(nullptr);
-	alcDestroyContext(context);
-	context = nullptr;
+    if (!context) return;
+    alcMakeContextCurrent(context);
+    alDeleteSources(1, &source);
+    alDeleteBuffers(num_buffers, buffers);
+    alcMakeContextCurrent(nullptr);
+    alcDestroyContext(context);
+    context = nullptr;
 }
 
 void OpenALPlayer::Play(int64_t start, int64_t count)
 {
-	InitContext();
-	alcMakeContextCurrent(context);
-	if (playing) {
-		// Quick reset
-		playing = false;
-		alSourceStop(source);
-		alSourcei(source, AL_BUFFER, 0);
-	}
-
-	// Set params
-	start_frame = start;
-	cur_frame = start;
-	end_frame = start + count;
-	playing = true;
-
-	// Prepare buffers
-	buffers_free = num_buffers;
-	buffers_played = 0;
-	buf_first_free = 0;
-	buf_first_queued = 0;
-	FillBuffers(num_buffers);
-
-	// And go!
-	alSourcePlay(source);
-	wxTimer::Start(100);
-	playback_segment_timer.Start();
+    InitContext();
+    alcMakeContextCurrent(context);
+    if (playing) {
+        // Quick reset
+        playing = false;
+        alSourceStop(source);
+        alSourcei(source, AL_BUFFER, 0);
+    }
+
+    // Set params
+    start_frame = start;
+    cur_frame = start;
+    end_frame = start + count;
+    playing = true;
+
+    // Prepare buffers
+    buffers_free = num_buffers;
+    buffers_played = 0;
+    buf_first_free = 0;
+    buf_first_queued = 0;
+    FillBuffers(num_buffers);
+
+    // And go!
+    alSourcePlay(source);
+    wxTimer::Start(100);
+    playback_segment_timer.Start();
 }
 
 void OpenALPlayer::Stop()
 {
-	TeardownContext();
-	if (!playing) return;
-
-	// Reset data
-	wxTimer::Stop();
-	playing = false;
-	start_frame = 0;
-	cur_frame = 0;
-	end_frame = 0;
-
-	// Then drop the playback
-	alcMakeContextCurrent(context);
-	alSourceStop(source);
-	alSourcei(source, AL_BUFFER, 0);
-	alcMakeContextCurrent(nullptr);
+    TeardownContext();
+    if (!playing) return;
+
+    // Reset data
+    wxTimer::Stop();
+    playing = false;
+    start_frame = 0;
+    cur_frame = 0;
+    end_frame = 0;
+
+    // Then drop the playback
+    alcMakeContextCurrent(context);
+    alSourceStop(source);
+    alSourcei(source, AL_BUFFER, 0);
+    alcMakeContextCurrent(nullptr);
 }
 
 void OpenALPlayer::FillBuffers(ALsizei count)
 {
-	InitContext();
-	// Do the actual filling/queueing
-	for (count = mid(1, count, buffers_free); count > 0; --count) {
-		ALsizei fill_len = mid<ALsizei>(0, decode_buffer.size() / bpf, end_frame - cur_frame);
-
-		if (fill_len > 0)
-			// Get fill_len frames of audio
-			provider->GetAudioWithVolume(&decode_buffer[0], cur_frame, fill_len, volume);
-		if ((size_t)fill_len * bpf < decode_buffer.size())
-			// And zerofill the rest
-			memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf);
-
-		cur_frame += fill_len;
-
-		alBufferData(buffers[buf_first_free], AL_FORMAT_MONO16, &decode_buffer[0], decode_buffer.size(), samplerate);
-		alSourceQueueBuffers(source, 1, &buffers[buf_first_free]); // FIXME: collect buffer handles and queue all at once instead of one at a time?
-		buf_first_free = (buf_first_free + 1) % num_buffers;
-		--buffers_free;
-	}
+    InitContext();
+    // Do the actual filling/queueing
+    for (count = mid(1, count, buffers_free); count > 0; --count) {
+        ALsizei fill_len = mid<ALsizei>(0, decode_buffer.size() / bpf, end_frame - cur_frame);
+
+        if (fill_len > 0)
+            // Get fill_len frames of audio
+            provider->GetAudioWithVolume(&decode_buffer[0], cur_frame, fill_len, volume);
+        if ((size_t)fill_len * bpf < decode_buffer.size())
+            // And zerofill the rest
+            memset(&decode_buffer[fill_len * bpf], 0, decode_buffer.size() - fill_len * bpf);
+
+        cur_frame += fill_len;
+
+        alBufferData(buffers[buf_first_free], AL_FORMAT_MONO16, &decode_buffer[0], decode_buffer.size(), samplerate);
+        alSourceQueueBuffers(source, 1, &buffers[buf_first_free]); // FIXME: collect buffer handles and queue all at once instead of one at a time?
+        buf_first_free = (buf_first_free + 1) % num_buffers;
+        --buffers_free;
+    }
 }
 
 void OpenALPlayer::Notify()
 {
-	InitContext();
-	alcMakeContextCurrent(context);
-	ALsizei newplayed;
-	alGetSourcei(source, AL_BUFFERS_PROCESSED, &newplayed);
-
-	LOG_D("player/audio/openal") << "buffers_played=" << buffers_played << " newplayed=" << newplayed;
-
-	if (newplayed > 0) {
-		// Reclaim buffers
-		ALuint bufs[num_buffers];
-		for (ALsizei i = 0; i < newplayed; ++i) {
-			bufs[i] = buffers[buf_first_queued];
-			buf_first_queued = (buf_first_queued + 1) % num_buffers;
-		}
-		alSourceUnqueueBuffers(source, newplayed, bufs);
-		buffers_free += newplayed;
-
-		// Update
-		buffers_played += newplayed;
-		playback_segment_timer.Start();
-
-		// Fill more buffers
-		FillBuffers(newplayed);
-	}
-
-	LOG_D("player/audio/openal") << "frames played=" << (buffers_played - num_buffers) * decode_buffer.size() / bpf << " num frames=" << end_frame - start_frame;
-	// Check that all of the selected audio plus one full set of buffers has been queued
-	if ((buffers_played - num_buffers) * (int64_t)decode_buffer.size() > (end_frame - start_frame) * bpf) {
-		Stop();
-	}
+    InitContext();
+    alcMakeContextCurrent(context);
+    ALsizei newplayed;
+    alGetSourcei(source, AL_BUFFERS_PROCESSED, &newplayed);
+
+    LOG_D("player/audio/openal") << "buffers_played=" << buffers_played << " newplayed=" << newplayed;
+
+    if (newplayed > 0) {
+        // Reclaim buffers
+        ALuint bufs[num_buffers];
+        for (ALsizei i = 0; i < newplayed; ++i) {
+            bufs[i] = buffers[buf_first_queued];
+            buf_first_queued = (buf_first_queued + 1) % num_buffers;
+        }
+        alSourceUnqueueBuffers(source, newplayed, bufs);
+        buffers_free += newplayed;
+
+        // Update
+        buffers_played += newplayed;
+        playback_segment_timer.Start();
+
+        // Fill more buffers
+        FillBuffers(newplayed);
+    }
+
+    LOG_D("player/audio/openal") << "frames played=" << (buffers_played - num_buffers) * decode_buffer.size() / bpf << " num frames=" << end_frame - start_frame;
+    // Check that all of the selected audio plus one full set of buffers has been queued
+    if ((buffers_played - num_buffers) * (int64_t)decode_buffer.size() > (end_frame - start_frame) * bpf) {
+        Stop();
+    }
 }
 
 void OpenALPlayer::SetEndPosition(int64_t pos)
 {
-	end_frame = pos;
+    end_frame = pos;
 }
 
 int64_t OpenALPlayer::GetCurrentPosition()
 {
-	// FIXME: this should be based on not duration played but actual sample being heard
-	// (during video playback, cur_frame might get changed to resync)
-	long extra = playback_segment_timer.Time();
-	return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000;
+    // FIXME: this should be based on not duration played but actual sample being heard
+    // (during video playback, cur_frame might get changed to resync)
+    long extra = playback_segment_timer.Time();
+    return buffers_played * decode_buffer.size() / bpf + start_frame + extra * samplerate / 1000;
 }
 }
 
 std::unique_ptr<AudioPlayer> CreateOpenALPlayer(agi::AudioProvider *provider, wxWindow *)
 {
-	return agi::make_unique<OpenALPlayer>(provider);
+    return agi::make_unique<OpenALPlayer>(provider);
 }
 
 #endif // WITH_OPENAL
diff --git a/src/audio_player_oss.cpp b/src/audio_player_oss.cpp
index 2d7abd560bc3e6b1922e83c05d24045252900477..69d8fd33736815326910a078d98b20877d981153 100644
--- a/src/audio_player_oss.cpp
+++ b/src/audio_player_oss.cpp
@@ -90,8 +90,7 @@ class OSSPlayer final : public AudioPlayer {
 
 public:
     OSSPlayer(agi::AudioProvider *provider)
-    : AudioPlayer(provider)
-    {
+        : AudioPlayer(provider) {
         OpenStream();
     }
 
@@ -120,7 +119,7 @@ class OSSPlayerThread final : public wxThread {
 public:
     /// Constructor
     /// @param parent Player to get audio data and playback state from
-    OSSPlayerThread(OSSPlayer *parent) : wxThread(wxTHREAD_JOINABLE) , parent(parent) { }
+    OSSPlayerThread(OSSPlayer *parent) : wxThread(wxTHREAD_JOINABLE), parent(parent) { }
 
     /// Main thread entry point
     wxThread::ExitCode Entry() {
@@ -170,14 +169,14 @@ void OSSPlayer::OpenStream()
     // Set sample format
     int sample_format;
     switch (provider->GetBytesPerSample()) {
-        case 1:
-            sample_format = AFMT_S8;
-            break;
-        case 2:
-            sample_format = AFMT_S16_LE;
-            break;
-        default:
-            throw AudioPlayerOpenError("OSS player: can only handle 8 and 16 bit sound");
+    case 1:
+        sample_format = AFMT_S8;
+        break;
+    case 2:
+        sample_format = AFMT_S16_LE;
+        break;
+    default:
+        throw AudioPlayerOpenError("OSS player: can only handle 8 and 16 bit sound");
     }
 
     if (ioctl(dspdev, SNDCTL_DSP_SETFMT, &sample_format) < 0) {
@@ -279,7 +278,8 @@ int64_t OSSPlayer::GetCurrentPosition()
 }
 }
 
-std::unique_ptr<AudioPlayer> CreateOSSPlayer(agi::AudioProvider *provider, wxWindow *) {
+std::unique_ptr<AudioPlayer> CreateOSSPlayer(agi::AudioProvider *provider, wxWindow *)
+{
     return agi::make_unique<OSSPlayer>(provider);
 }
 
diff --git a/src/audio_player_portaudio.cpp b/src/audio_player_portaudio.cpp
index 7a5babcdc10974192f52c01ba41b8c0030220da1..3c0353741e352fb1b869ea06119dfcc328553bcf 100644
--- a/src/audio_player_portaudio.cpp
+++ b/src/audio_player_portaudio.cpp
@@ -49,238 +49,249 @@
 
 /// Order that the host APIs should be tried if there are multiple available
 static const PaHostApiTypeId pa_host_api_priority[] = {
-	// No WDMKS or ASIO as they don't support shared mode (and WDMKS is pretty broken)
-	paWASAPI,
-	paDirectSound,
-	paMME,
+    // No WDMKS or ASIO as they don't support shared mode (and WDMKS is pretty broken)
+    paWASAPI,
+    paDirectSound,
+    paMME,
 
-	paCoreAudio,
+    paCoreAudio,
 #ifdef __APPLE__
-	paAL,
+    paAL,
 #endif
 
-	paALSA,
-	paOSS
+    paALSA,
+    paOSS
 };
 static const size_t pa_host_api_priority_count = sizeof(pa_host_api_priority) / sizeof(pa_host_api_priority[0]);
 
-PortAudioPlayer::PortAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(provider) {
-	PaError err = Pa_Initialize();
-
-	if (err != paNoError)
-		throw AudioPlayerOpenError(std::string("Failed opening PortAudio: ") + Pa_GetErrorText(err));
-
-	// Build a list of host API-specific devices we can use
-	// Some host APIs may not support all audio formats, so build a priority
-	// list of host APIs for each device rather than just always using the best
-	for (size_t i = 0; i < pa_host_api_priority_count; ++i) {
-		PaHostApiIndex host_idx = Pa_HostApiTypeIdToHostApiIndex(pa_host_api_priority[i]);
-		if (host_idx >= 0)
-			GatherDevices(host_idx);
-	}
-	GatherDevices(Pa_GetDefaultHostApi());
-
-	if (devices.empty())
-		throw AudioPlayerOpenError("No PortAudio output devices found");
-
-	if (provider)
-		OpenStream();
+PortAudioPlayer::PortAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(provider)
+{
+    PaError err = Pa_Initialize();
+
+    if (err != paNoError)
+        throw AudioPlayerOpenError(std::string("Failed opening PortAudio: ") + Pa_GetErrorText(err));
+
+    // Build a list of host API-specific devices we can use
+    // Some host APIs may not support all audio formats, so build a priority
+    // list of host APIs for each device rather than just always using the best
+    for (size_t i = 0; i < pa_host_api_priority_count; ++i) {
+        PaHostApiIndex host_idx = Pa_HostApiTypeIdToHostApiIndex(pa_host_api_priority[i]);
+        if (host_idx >= 0)
+            GatherDevices(host_idx);
+    }
+    GatherDevices(Pa_GetDefaultHostApi());
+
+    if (devices.empty())
+        throw AudioPlayerOpenError("No PortAudio output devices found");
+
+    if (provider)
+        OpenStream();
 }
 
-void PortAudioPlayer::GatherDevices(PaHostApiIndex host_idx) {
-	const PaHostApiInfo *host_info = Pa_GetHostApiInfo(host_idx);
-	if (!host_info) return;
-
-	for (int host_device_idx = 0; host_device_idx < host_info->deviceCount; ++host_device_idx) {
-		PaDeviceIndex real_idx = Pa_HostApiDeviceIndexToDeviceIndex(host_idx, host_device_idx);
-		if (real_idx < 0) continue;
-
-		const PaDeviceInfo *device_info = Pa_GetDeviceInfo(real_idx);
-		if (!device_info) continue;
-		if (device_info->maxOutputChannels <= 0) continue;
-
-		// MME truncates device names so check for prefix rather than exact match
-		auto dev_it = devices.lower_bound(device_info->name);
-		if (dev_it == devices.end() || dev_it->first.find(device_info->name) != 0) {
-			devices[device_info->name];
-			--dev_it;
-		}
-
-		dev_it->second.push_back(real_idx);
-		if (real_idx == host_info->defaultOutputDevice)
-			default_device.push_back(real_idx);
-	}
+void PortAudioPlayer::GatherDevices(PaHostApiIndex host_idx)
+{
+    const PaHostApiInfo *host_info = Pa_GetHostApiInfo(host_idx);
+    if (!host_info) return;
+
+    for (int host_device_idx = 0; host_device_idx < host_info->deviceCount; ++host_device_idx) {
+        PaDeviceIndex real_idx = Pa_HostApiDeviceIndexToDeviceIndex(host_idx, host_device_idx);
+        if (real_idx < 0) continue;
+
+        const PaDeviceInfo *device_info = Pa_GetDeviceInfo(real_idx);
+        if (!device_info) continue;
+        if (device_info->maxOutputChannels <= 0) continue;
+
+        // MME truncates device names so check for prefix rather than exact match
+        auto dev_it = devices.lower_bound(device_info->name);
+        if (dev_it == devices.end() || dev_it->first.find(device_info->name) != 0) {
+            devices[device_info->name];
+            --dev_it;
+        }
+
+        dev_it->second.push_back(real_idx);
+        if (real_idx == host_info->defaultOutputDevice)
+            default_device.push_back(real_idx);
+    }
 }
 
-PortAudioPlayer::~PortAudioPlayer() {
-	if (stream) {
-		Stop();
-		Pa_CloseStream(stream);
-	}
-	Pa_Terminate();
+PortAudioPlayer::~PortAudioPlayer()
+{
+    if (stream) {
+        Stop();
+        Pa_CloseStream(stream);
+    }
+    Pa_Terminate();
 }
 
-void PortAudioPlayer::OpenStream() {
-	DeviceVec *device_ids = nullptr;
-	std::string device_name = OPT_GET("Player/Audio/PortAudio/Device Name")->GetString();
-
-	if (devices.count(device_name)) {
-		device_ids = &devices[device_name];
-		LOG_D("audio/player/portaudio") << "using config device: " << device_name;
-	}
-
-	if (!device_ids || device_ids->empty()) {
-		device_ids = &default_device;
-		LOG_D("audio/player/portaudio") << "using default output device";
-	}
-
-	std::string error;
-
-	for (size_t i = 0; i < device_ids->size(); ++i) {
-		const PaDeviceInfo *device_info = Pa_GetDeviceInfo((*device_ids)[i]);
-		PaStreamParameters pa_output_p;
-		pa_output_p.device = (*device_ids)[i];
-		pa_output_p.channelCount = provider->GetChannels();
-		pa_output_p.sampleFormat = paInt16;
-		pa_output_p.suggestedLatency = device_info->defaultLowOutputLatency;
-		pa_output_p.hostApiSpecificStreamInfo = nullptr;
-
-		LOG_D("audio/player/portaudio") << "OpenStream:"
-			<< " output channels: " << pa_output_p.channelCount
-			<< " latency: " << pa_output_p.suggestedLatency
-			<< " sample rate: " << provider->GetSampleRate()
-			<< " sample format: " << pa_output_p.sampleFormat;
-
-		PaError err = Pa_OpenStream(&stream, nullptr, &pa_output_p, provider->GetSampleRate(), 0, paPrimeOutputBuffersUsingStreamCallback, paCallback, this);
-
-		if (err == paNoError) {
-			LOG_D("audo/player/portaudio") << "Using device " << pa_output_p.device << " " << device_info->name << " " << Pa_GetHostApiInfo(device_info->hostApi)->name;
-			return;
-		}
-		else {
-			const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo();
-			LOG_D_IF(pa_err->errorCode != 0, "audio/player/portaudio") << "HostError: API: " << pa_err->hostApiType << ", " << pa_err->errorText << ", " << pa_err->errorCode;
-			LOG_D("audio/player/portaudio") << "Failed initializing PortAudio stream with error: " << Pa_GetErrorText(err);
-			error += Pa_GetErrorText(err);
-			error += " ";
-		}
-	}
-
-	throw AudioPlayerOpenError("Failed initializing PortAudio stream: " + error);
+void PortAudioPlayer::OpenStream()
+{
+    DeviceVec *device_ids = nullptr;
+    std::string device_name = OPT_GET("Player/Audio/PortAudio/Device Name")->GetString();
+
+    if (devices.count(device_name)) {
+        device_ids = &devices[device_name];
+        LOG_D("audio/player/portaudio") << "using config device: " << device_name;
+    }
+
+    if (!device_ids || device_ids->empty()) {
+        device_ids = &default_device;
+        LOG_D("audio/player/portaudio") << "using default output device";
+    }
+
+    std::string error;
+
+    for (size_t i = 0; i < device_ids->size(); ++i) {
+        const PaDeviceInfo *device_info = Pa_GetDeviceInfo((*device_ids)[i]);
+        PaStreamParameters pa_output_p;
+        pa_output_p.device = (*device_ids)[i];
+        pa_output_p.channelCount = provider->GetChannels();
+        pa_output_p.sampleFormat = paInt16;
+        pa_output_p.suggestedLatency = device_info->defaultLowOutputLatency;
+        pa_output_p.hostApiSpecificStreamInfo = nullptr;
+
+        LOG_D("audio/player/portaudio") << "OpenStream:"
+                                        << " output channels: " << pa_output_p.channelCount
+                                        << " latency: " << pa_output_p.suggestedLatency
+                                        << " sample rate: " << provider->GetSampleRate()
+                                        << " sample format: " << pa_output_p.sampleFormat;
+
+        PaError err = Pa_OpenStream(&stream, nullptr, &pa_output_p, provider->GetSampleRate(), 0, paPrimeOutputBuffersUsingStreamCallback, paCallback, this);
+
+        if (err == paNoError) {
+            LOG_D("audo/player/portaudio") << "Using device " << pa_output_p.device << " " << device_info->name << " " << Pa_GetHostApiInfo(device_info->hostApi)->name;
+            return;
+        }
+        else {
+            const PaHostErrorInfo *pa_err = Pa_GetLastHostErrorInfo();
+            LOG_D_IF(pa_err->errorCode != 0, "audio/player/portaudio") << "HostError: API: " << pa_err->hostApiType << ", " << pa_err->errorText << ", " << pa_err->errorCode;
+            LOG_D("audio/player/portaudio") << "Failed initializing PortAudio stream with error: " << Pa_GetErrorText(err);
+            error += Pa_GetErrorText(err);
+            error += " ";
+        }
+    }
+
+    throw AudioPlayerOpenError("Failed initializing PortAudio stream: " + error);
 }
 
-void PortAudioPlayer::paStreamFinishedCallback(void *) {
-	LOG_D("audio/player/portaudio") << "stopping stream";
+void PortAudioPlayer::paStreamFinishedCallback(void *)
+{
+    LOG_D("audio/player/portaudio") << "stopping stream";
 }
 
-void PortAudioPlayer::Play(int64_t start_sample, int64_t count) {
-	current = start_sample;
-	start = start_sample;
-	end = start_sample + count;
-
-	// Start playing
-	if (!IsPlaying()) {
-		PaError err = Pa_SetStreamFinishedCallback(stream, paStreamFinishedCallback);
-		if (err != paNoError) {
-			LOG_D("audio/player/portaudio") << "could not set FinishedCallback";
-			return;
-		}
-
-		err = Pa_StartStream(stream);
-		if (err != paNoError) {
-			LOG_D("audio/player/portaudio") << "error playing stream";
-			return;
-		}
-	}
-	pa_start = Pa_GetStreamTime(stream);
+void PortAudioPlayer::Play(int64_t start_sample, int64_t count)
+{
+    current = start_sample;
+    start = start_sample;
+    end = start_sample + count;
+
+    // Start playing
+    if (!IsPlaying()) {
+        PaError err = Pa_SetStreamFinishedCallback(stream, paStreamFinishedCallback);
+        if (err != paNoError) {
+            LOG_D("audio/player/portaudio") << "could not set FinishedCallback";
+            return;
+        }
+
+        err = Pa_StartStream(stream);
+        if (err != paNoError) {
+            LOG_D("audio/player/portaudio") << "error playing stream";
+            return;
+        }
+    }
+    pa_start = Pa_GetStreamTime(stream);
 }
 
-void PortAudioPlayer::Stop() {
-	Pa_StopStream(stream);
+void PortAudioPlayer::Stop()
+{
+    Pa_StopStream(stream);
 }
 
 int PortAudioPlayer::paCallback(const void *inputBuffer, void *outputBuffer,
-	unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
-	PaStreamCallbackFlags statusFlags, void *userData)
+                                unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
+                                PaStreamCallbackFlags statusFlags, void *userData)
 {
-	PortAudioPlayer *player = (PortAudioPlayer *)userData;
+    PortAudioPlayer *player = (PortAudioPlayer *)userData;
 
 #ifdef PORTAUDIO_DEBUG
-	LOG_D("audio/player/portaudio") << "psCallback:"
-		<< " current: " << player->current
-		<< " start: " << player->start
-		<< " pa_start: " << player->pa_start
-		<< " currentTime: " << timeInfo->currentTime
-		<< " AdcTime: " << timeInfo->inputBufferAdcTime
-		<< " DacTime: " << timeInfo->outputBufferDacTime
-		<< " framesPerBuffer: " << framesPerBuffer
-		<< " CPU: " << Pa_GetStreamCpuLoad(player->stream);
+    LOG_D("audio/player/portaudio") << "psCallback:"
+                                    << " current: " << player->current
+                                    << " start: " << player->start
+                                    << " pa_start: " << player->pa_start
+                                    << " currentTime: " << timeInfo->currentTime
+                                    << " AdcTime: " << timeInfo->inputBufferAdcTime
+                                    << " DacTime: " << timeInfo->outputBufferDacTime
+                                    << " framesPerBuffer: " << framesPerBuffer
+                                    << " CPU: " << Pa_GetStreamCpuLoad(player->stream);
 #endif
 
-	// Calculate how much left
-	int64_t lenAvailable = std::min<int64_t>(player->end - player->current, framesPerBuffer);
+    // Calculate how much left
+    int64_t lenAvailable = std::min<int64_t>(player->end - player->current, framesPerBuffer);
 
-	// Play something
-	if (lenAvailable > 0) {
-		player->provider->GetAudioWithVolume(outputBuffer, player->current, lenAvailable, player->GetVolume());
+    // Play something
+    if (lenAvailable > 0) {
+        player->provider->GetAudioWithVolume(outputBuffer, player->current, lenAvailable, player->GetVolume());
 
-		// Set play position
-		player->current += lenAvailable;
+        // Set play position
+        player->current += lenAvailable;
 
-		// Continue as normal
-		return 0;
-	}
+        // Continue as normal
+        return 0;
+    }
 
-	// Abort stream and stop the callback.
-	return paAbort;
+    // Abort stream and stop the callback.
+    return paAbort;
 }
 
-int64_t PortAudioPlayer::GetCurrentPosition() {
-	if (!IsPlaying()) return 0;
+int64_t PortAudioPlayer::GetCurrentPosition()
+{
+    if (!IsPlaying()) return 0;
 
-	PaTime pa_time = Pa_GetStreamTime(stream);
-	int64_t real = (pa_time - pa_start) * provider->GetSampleRate() + start;
+    PaTime pa_time = Pa_GetStreamTime(stream);
+    int64_t real = (pa_time - pa_start) * provider->GetSampleRate() + start;
 
-	// If portaudio isn't giving us time info then estimate based on buffer fill and current latency
-	if (pa_time == 0 && pa_start == 0)
-		real = current - Pa_GetStreamInfo(stream)->outputLatency * provider->GetSampleRate();
+    // If portaudio isn't giving us time info then estimate based on buffer fill and current latency
+    if (pa_time == 0 && pa_start == 0)
+        real = current - Pa_GetStreamInfo(stream)->outputLatency * provider->GetSampleRate();
 
 #ifdef PORTAUDIO_DEBUG
-	LOG_D("audio/player/portaudio") << "GetCurrentPosition:"
-		<< " pa_time: " << pa_time
-		<< " start: " << start
-		<< " current: " << current
-		<< " pa_start: " << pa_start
-		<< " real: " << real
-		<< " diff: " << pa_time - pa_start;
+    LOG_D("audio/player/portaudio") << "GetCurrentPosition:"
+                                    << " pa_time: " << pa_time
+                                    << " start: " << start
+                                    << " current: " << current
+                                    << " pa_start: " << pa_start
+                                    << " real: " << real
+                                    << " diff: " << pa_time - pa_start;
 #endif
 
-	return real;
+    return real;
 }
 
-wxArrayString PortAudioPlayer::GetOutputDevices() {
-	wxArrayString list;
-	list.push_back("Default");
+wxArrayString PortAudioPlayer::GetOutputDevices()
+{
+    wxArrayString list;
+    list.push_back("Default");
 
-	try {
-		PortAudioPlayer player(0);
+    try {
+        PortAudioPlayer player(0);
 
-		for (auto it = player.devices.begin(); it != player.devices.end(); ++it)
-			list.push_back(to_wx(it->first));
-	}
-	catch (AudioPlayerOpenError const&) {
-		// No output devices, just return the list with only Default
-	}
+        for (auto it = player.devices.begin(); it != player.devices.end(); ++it)
+            list.push_back(to_wx(it->first));
+    }
+    catch (AudioPlayerOpenError const &) {
+        // No output devices, just return the list with only Default
+    }
 
-	return list;
+    return list;
 }
 
-bool PortAudioPlayer::IsPlaying() {
-	return !!Pa_IsStreamActive(stream);
+bool PortAudioPlayer::IsPlaying()
+{
+    return !!Pa_IsStreamActive(stream);
 }
 
-std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(agi::AudioProvider *provider, wxWindow *) {
-	return agi::make_unique<PortAudioPlayer>(provider);
+std::unique_ptr<AudioPlayer> CreatePortAudioPlayer(agi::AudioProvider *provider, wxWindow *)
+{
+    return agi::make_unique<PortAudioPlayer>(provider);
 }
 
 #endif // WITH_PORTAUDIO
diff --git a/src/audio_player_portaudio.h b/src/audio_player_portaudio.h
index a3e851e0514b4f8de46231330ab825a187672972..2f724689bd49e7e04f4b7c4e0d48ec71b8664e70 100644
--- a/src/audio_player_portaudio.h
+++ b/src/audio_player_portaudio.h
@@ -50,89 +50,89 @@ class wxArrayString;
 /// @brief PortAudio Player
 ///
 class PortAudioPlayer final : public AudioPlayer {
-	typedef std::vector<PaDeviceIndex> DeviceVec;
-	/// Map of supported output devices from name -> device index
-	std::map<std::string, DeviceVec> devices;
-
-	/// The index of the default output devices sorted by host API priority
-	DeviceVec default_device;
-
-	float volume = 1.f;  ///< Current volume level
-	int64_t current = 0; ///< Current position
-	int64_t start = 0;   ///< Start position
-	int64_t end = 0;     ///< End position
-	PaTime pa_start;     ///< PortAudio internal start position
-
-	PaStream *stream = nullptr; ///< PortAudio stream
-
-	/// @brief PortAudio callback, used to fill buffer for playback, and prime the playback buffer.
-	/// @param inputBuffer     Input buffer.
-	/// @param outputBuffer    Output buffer.
-	/// @param framesPerBuffer Frames per buffer.
-	/// @param timeInfo        PortAudio time information.
-	/// @param statusFlags     Status flags
-	/// @param userData        Local data to hand callback
-	/// @return Whether to stop playback.
-	static int paCallback(
-		const void *inputBuffer,
-		void *outputBuffer,
-		unsigned long framesPerBuffer,
-		const PaStreamCallbackTimeInfo*
-		timeInfo,
-		PaStreamCallbackFlags
-		statusFlags,
-		void *userData);
-
-	/// @brief Called when the callback has finished.
-	/// @param userData Local data to be handed to the callback.
-	static void paStreamFinishedCallback(void *userData);
-
-	/// Gather the list of output devices supported by a host API
-	/// @param host_idx Host API ID
-	void GatherDevices(PaHostApiIndex host_idx);
-
-	void OpenStream();
+    typedef std::vector<PaDeviceIndex> DeviceVec;
+    /// Map of supported output devices from name -> device index
+    std::map<std::string, DeviceVec> devices;
+
+    /// The index of the default output devices sorted by host API priority
+    DeviceVec default_device;
+
+    float volume = 1.f;  ///< Current volume level
+    int64_t current = 0; ///< Current position
+    int64_t start = 0;   ///< Start position
+    int64_t end = 0;     ///< End position
+    PaTime pa_start;     ///< PortAudio internal start position
+
+    PaStream *stream = nullptr; ///< PortAudio stream
+
+    /// @brief PortAudio callback, used to fill buffer for playback, and prime the playback buffer.
+    /// @param inputBuffer     Input buffer.
+    /// @param outputBuffer    Output buffer.
+    /// @param framesPerBuffer Frames per buffer.
+    /// @param timeInfo        PortAudio time information.
+    /// @param statusFlags     Status flags
+    /// @param userData        Local data to hand callback
+    /// @return Whether to stop playback.
+    static int paCallback(
+        const void *inputBuffer,
+        void *outputBuffer,
+        unsigned long framesPerBuffer,
+        const PaStreamCallbackTimeInfo *
+        timeInfo,
+        PaStreamCallbackFlags
+        statusFlags,
+        void *userData);
+
+    /// @brief Called when the callback has finished.
+    /// @param userData Local data to be handed to the callback.
+    static void paStreamFinishedCallback(void *userData);
+
+    /// Gather the list of output devices supported by a host API
+    /// @param host_idx Host API ID
+    void GatherDevices(PaHostApiIndex host_idx);
+
+    void OpenStream();
 
 public:
-	/// @brief Constructor
-	PortAudioPlayer(agi::AudioProvider *provider);
-
-	/// @brief Destructor
-	~PortAudioPlayer();
-
-	/// @brief Play audio.
-	/// @param start Start position.
-	/// @param count Frame count
-	void Play(int64_t start,int64_t count);
-	/// @brief Stop Playback
-	/// @param timerToo Stop display timer?
-	void Stop();
-
-	/// @brief Whether audio is currently being played.
-	/// @return Status
-	bool IsPlaying();
-
-	/// @brief End position playback will stop at.
-	/// @return End position.
-	int64_t GetEndPosition() { return end; }
-	/// @brief Get current stream position.
-	/// @return Stream position
-	int64_t GetCurrentPosition();
-
-	/// @brief Set end position of playback
-	/// @param pos End position
-	void SetEndPosition(int64_t position) { end = position; }
-
-
-	/// @brief Set volume level
-	/// @param vol Volume
-	void SetVolume(double vol) { volume = vol; }
-
-	/// @brief Get current volume level
-	/// @return Volume level
-	double GetVolume() { return volume; }
-
-	/// Get list of available output devices
-	static wxArrayString GetOutputDevices();
+    /// @brief Constructor
+    PortAudioPlayer(agi::AudioProvider *provider);
+
+    /// @brief Destructor
+    ~PortAudioPlayer();
+
+    /// @brief Play audio.
+    /// @param start Start position.
+    /// @param count Frame count
+    void Play(int64_t start, int64_t count);
+    /// @brief Stop Playback
+    /// @param timerToo Stop display timer?
+    void Stop();
+
+    /// @brief Whether audio is currently being played.
+    /// @return Status
+    bool IsPlaying();
+
+    /// @brief End position playback will stop at.
+    /// @return End position.
+    int64_t GetEndPosition() { return end; }
+    /// @brief Get current stream position.
+    /// @return Stream position
+    int64_t GetCurrentPosition();
+
+    /// @brief Set end position of playback
+    /// @param pos End position
+    void SetEndPosition(int64_t position) { end = position; }
+
+
+    /// @brief Set volume level
+    /// @param vol Volume
+    void SetVolume(double vol) { volume = vol; }
+
+    /// @brief Get current volume level
+    /// @return Volume level
+    double GetVolume() { return volume; }
+
+    /// Get list of available output devices
+    static wxArrayString GetOutputDevices();
 };
 #endif //ifdef WITH_PORTAUDIO
diff --git a/src/audio_player_pulse.cpp b/src/audio_player_pulse.cpp
index 7174356bddc910e60f5fdb157c0f87eaa0729704..acac239c306a1a4539802a2106c9d3f89c70b785 100644
--- a/src/audio_player_pulse.cpp
+++ b/src/audio_player_pulse.cpp
@@ -48,280 +48,285 @@
 
 namespace {
 class PulseAudioPlayer final : public AudioPlayer {
-	float volume = 1.f;
-	bool is_playing = false;
+    float volume = 1.f;
+    bool is_playing = false;
 
-	volatile unsigned long start_frame = 0;
-	volatile unsigned long cur_frame = 0;
-	volatile unsigned long end_frame = 0;
+    volatile unsigned long start_frame = 0;
+    volatile unsigned long cur_frame = 0;
+    volatile unsigned long end_frame = 0;
 
-	unsigned long bpf = 0; // bytes per frame
+    unsigned long bpf = 0; // bytes per frame
 
-	wxSemaphore context_notify{0, 1};
-	wxSemaphore stream_notify{0, 1};
-	wxSemaphore stream_success{0, 1};
-	volatile int stream_success_val;
+    wxSemaphore context_notify{0, 1};
+    wxSemaphore stream_notify{0, 1};
+    wxSemaphore stream_success{0, 1};
+    volatile int stream_success_val;
 
-	pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle
-	pa_context *context = nullptr; // connection context
-	volatile pa_context_state_t cstate;
+    pa_threaded_mainloop *mainloop = nullptr; // pulseaudio mainloop handle
+    pa_context *context = nullptr; // connection context
+    volatile pa_context_state_t cstate;
 
-	pa_stream *stream = nullptr;
-	volatile pa_stream_state_t sstate;
+    pa_stream *stream = nullptr;
+    volatile pa_stream_state_t sstate;
 
-	volatile pa_usec_t play_start_time; // timestamp when playback was started
+    volatile pa_usec_t play_start_time; // timestamp when playback was started
 
-	int paerror = 0;
+    int paerror = 0;
 
-	/// Called by PA to notify about other context-related stuff
-	static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread);
-	/// Called by PA when a stream operation completes
-	static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread);
-	/// Called by PA to request more data written to stream
-	static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread);
-	/// Called by PA to notify about other stream-related stuff
-	static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread);
+    /// Called by PA to notify about other context-related stuff
+    static void pa_context_notify(pa_context *c, PulseAudioPlayer *thread);
+    /// Called by PA when a stream operation completes
+    static void pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread);
+    /// Called by PA to request more data written to stream
+    static void pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread);
+    /// Called by PA to notify about other stream-related stuff
+    static void pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread);
 
 public:
-	PulseAudioPlayer(agi::AudioProvider *provider);
-	~PulseAudioPlayer();
+    PulseAudioPlayer(agi::AudioProvider *provider);
+    ~PulseAudioPlayer();
 
-	void Play(int64_t start,int64_t count);
-	void Stop();
-	bool IsPlaying() { return is_playing; }
+    void Play(int64_t start, int64_t count);
+    void Stop();
+    bool IsPlaying() { return is_playing; }
 
-	int64_t GetEndPosition() { return end_frame; }
-	int64_t GetCurrentPosition();
-	void SetEndPosition(int64_t pos);
+    int64_t GetEndPosition() { return end_frame; }
+    int64_t GetCurrentPosition();
+    void SetEndPosition(int64_t pos);
 
-	void SetVolume(double vol) { volume = vol; }
+    void SetVolume(double vol) { volume = vol; }
 };
 
-PulseAudioPlayer::PulseAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(provider) {
-	// Initialise a mainloop
-	mainloop = pa_threaded_mainloop_new();
-	if (!mainloop)
-		throw AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object");
-
-	pa_threaded_mainloop_start(mainloop);
-
-	// Create context
-	context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
-	if (!context) {
-		pa_threaded_mainloop_free(mainloop);
-		throw AudioPlayerOpenError("Failed to create PulseAudio context");
-	}
-	pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this);
-
-	// Connect the context
-	pa_context_connect(context, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr);
-
-	// Wait for connection
-	while (true) {
-		context_notify.Wait();
-		if (cstate == PA_CONTEXT_READY) {
-			break;
-		} else if (cstate == PA_CONTEXT_FAILED) {
-			// eww
-			paerror = pa_context_errno(context);
-			pa_context_unref(context);
-			pa_threaded_mainloop_stop(mainloop);
-			pa_threaded_mainloop_free(mainloop);
-			throw AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror));
-		}
-		// otherwise loop once more
-	}
-
-	// Set up stream
-	bpf = provider->GetChannels() * provider->GetBytesPerSample();
-	pa_sample_spec ss;
-	ss.format = PA_SAMPLE_S16LE; // FIXME
-	ss.rate = provider->GetSampleRate();
-	ss.channels = provider->GetChannels();
-	pa_channel_map map;
-	pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
-
-	stream = pa_stream_new(context, "Sound", &ss, &map);
-	if (!stream) {
-		// argh!
-		pa_context_disconnect(context);
-		pa_context_unref(context);
-		pa_threaded_mainloop_stop(mainloop);
-		pa_threaded_mainloop_free(mainloop);
-		throw AudioPlayerOpenError("PulseAudio could not create stream");
-	}
-	pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this);
-	pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this);
-
-	// Connect stream
-	paerror = pa_stream_connect_playback(stream, nullptr, nullptr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_NOT_MONOTONOUS|PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr);
-	if (paerror) {
-		LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
-		throw AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror));
-	}
-	while (true) {
-		stream_notify.Wait();
-		if (sstate == PA_STREAM_READY) {
-			break;
-		} else if (sstate == PA_STREAM_FAILED) {
-			paerror = pa_context_errno(context);
-			LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
-			throw AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream");
-		}
-	}
+PulseAudioPlayer::PulseAudioPlayer(agi::AudioProvider *provider) : AudioPlayer(provider)
+{
+    // Initialise a mainloop
+    mainloop = pa_threaded_mainloop_new();
+    if (!mainloop)
+        throw AudioPlayerOpenError("Failed to initialise PulseAudio threaded mainloop object");
+
+    pa_threaded_mainloop_start(mainloop);
+
+    // Create context
+    context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Aegisub");
+    if (!context) {
+        pa_threaded_mainloop_free(mainloop);
+        throw AudioPlayerOpenError("Failed to create PulseAudio context");
+    }
+    pa_context_set_state_callback(context, (pa_context_notify_cb_t)pa_context_notify, this);
+
+    // Connect the context
+    pa_context_connect(context, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr);
+
+    // Wait for connection
+    while (true) {
+        context_notify.Wait();
+        if (cstate == PA_CONTEXT_READY) {
+            break;
+        }
+        else if (cstate == PA_CONTEXT_FAILED) {
+            // eww
+            paerror = pa_context_errno(context);
+            pa_context_unref(context);
+            pa_threaded_mainloop_stop(mainloop);
+            pa_threaded_mainloop_free(mainloop);
+            throw AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror));
+        }
+        // otherwise loop once more
+    }
+
+    // Set up stream
+    bpf = provider->GetChannels() * provider->GetBytesPerSample();
+    pa_sample_spec ss;
+    ss.format = PA_SAMPLE_S16LE; // FIXME
+    ss.rate = provider->GetSampleRate();
+    ss.channels = provider->GetChannels();
+    pa_channel_map map;
+    pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    stream = pa_stream_new(context, "Sound", &ss, &map);
+    if (!stream) {
+        // argh!
+        pa_context_disconnect(context);
+        pa_context_unref(context);
+        pa_threaded_mainloop_stop(mainloop);
+        pa_threaded_mainloop_free(mainloop);
+        throw AudioPlayerOpenError("PulseAudio could not create stream");
+    }
+    pa_stream_set_state_callback(stream, (pa_stream_notify_cb_t)pa_stream_notify, this);
+    pa_stream_set_write_callback(stream, (pa_stream_request_cb_t)pa_stream_write, this);
+
+    // Connect stream
+    paerror = pa_stream_connect_playback(stream, nullptr, nullptr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_NOT_MONOTONOUS | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr);
+    if (paerror) {
+        LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
+        throw AudioPlayerOpenError(std::string("PulseAudio reported error: ") + pa_strerror(paerror));
+    }
+    while (true) {
+        stream_notify.Wait();
+        if (sstate == PA_STREAM_READY) {
+            break;
+        }
+        else if (sstate == PA_STREAM_FAILED) {
+            paerror = pa_context_errno(context);
+            LOG_E("audio/player/pulse") << "Stream connection failed: " << pa_strerror(paerror) << "(" << paerror << ")";
+            throw AudioPlayerOpenError("PulseAudio player: Something went wrong connecting the stream");
+        }
+    }
 }
 
 PulseAudioPlayer::~PulseAudioPlayer()
 {
-	if (is_playing) Stop();
-
-	// Hope for the best and just do things as quickly as possible
-	pa_stream_disconnect(stream);
-	pa_stream_unref(stream);
-	pa_context_disconnect(context);
-	pa_context_unref(context);
-	pa_threaded_mainloop_stop(mainloop);
-	pa_threaded_mainloop_free(mainloop);
+    if (is_playing) Stop();
+
+    // Hope for the best and just do things as quickly as possible
+    pa_stream_disconnect(stream);
+    pa_stream_unref(stream);
+    pa_context_disconnect(context);
+    pa_context_unref(context);
+    pa_threaded_mainloop_stop(mainloop);
+    pa_threaded_mainloop_free(mainloop);
 }
 
-void PulseAudioPlayer::Play(int64_t start,int64_t count)
+void PulseAudioPlayer::Play(int64_t start, int64_t count)
 {
-	if (is_playing) {
-		// If we're already playing, do a quick "reset"
-		is_playing = false;
-
-		pa_threaded_mainloop_lock(mainloop);
-		pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
-		pa_threaded_mainloop_unlock(mainloop);
-		stream_success.Wait();
-		pa_operation_unref(op);
-		if (!stream_success_val) {
-			paerror = pa_context_errno(context);
-			LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
-		}
-	}
-
-	start_frame = start;
-	cur_frame = start;
-	end_frame = start + count;
-
-	is_playing = true;
-
-	play_start_time = 0;
-	pa_threaded_mainloop_lock(mainloop);
-	paerror = pa_stream_get_time(stream, (pa_usec_t*) &play_start_time);
-	pa_threaded_mainloop_unlock(mainloop);
-	if (paerror)
-		LOG_E("audio/player/pulse") << "Error getting stream time: " << pa_strerror(paerror) << "(" << paerror << ")";
-
-	PulseAudioPlayer::pa_stream_write(stream, pa_stream_writable_size(stream), this);
-
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *op = pa_stream_trigger(stream, (pa_stream_success_cb_t)pa_stream_success, this);
-	pa_threaded_mainloop_unlock(mainloop);
-	stream_success.Wait();
-	pa_operation_unref(op);
-	if (!stream_success_val) {
-		paerror = pa_context_errno(context);
-		LOG_E("audio/player/pulse") << "Error triggering stream: " << pa_strerror(paerror) << "(" << paerror << ")";
-	}
+    if (is_playing) {
+        // If we're already playing, do a quick "reset"
+        is_playing = false;
+
+        pa_threaded_mainloop_lock(mainloop);
+        pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
+        pa_threaded_mainloop_unlock(mainloop);
+        stream_success.Wait();
+        pa_operation_unref(op);
+        if (!stream_success_val) {
+            paerror = pa_context_errno(context);
+            LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
+        }
+    }
+
+    start_frame = start;
+    cur_frame = start;
+    end_frame = start + count;
+
+    is_playing = true;
+
+    play_start_time = 0;
+    pa_threaded_mainloop_lock(mainloop);
+    paerror = pa_stream_get_time(stream, (pa_usec_t *) &play_start_time);
+    pa_threaded_mainloop_unlock(mainloop);
+    if (paerror)
+        LOG_E("audio/player/pulse") << "Error getting stream time: " << pa_strerror(paerror) << "(" << paerror << ")";
+
+    PulseAudioPlayer::pa_stream_write(stream, pa_stream_writable_size(stream), this);
+
+    pa_threaded_mainloop_lock(mainloop);
+    pa_operation *op = pa_stream_trigger(stream, (pa_stream_success_cb_t)pa_stream_success, this);
+    pa_threaded_mainloop_unlock(mainloop);
+    stream_success.Wait();
+    pa_operation_unref(op);
+    if (!stream_success_val) {
+        paerror = pa_context_errno(context);
+        LOG_E("audio/player/pulse") << "Error triggering stream: " << pa_strerror(paerror) << "(" << paerror << ")";
+    }
 }
 
 void PulseAudioPlayer::Stop()
 {
-	if (!is_playing) return;
-
-	is_playing = false;
-
-	start_frame = 0;
-	cur_frame = 0;
-	end_frame = 0;
-
-	// Flush the stream of data
-	pa_threaded_mainloop_lock(mainloop);
-	pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
-	pa_threaded_mainloop_unlock(mainloop);
-	stream_success.Wait();
-	pa_operation_unref(op);
-	if (!stream_success_val) {
-		paerror = pa_context_errno(context);
-		LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
-	}
+    if (!is_playing) return;
+
+    is_playing = false;
+
+    start_frame = 0;
+    cur_frame = 0;
+    end_frame = 0;
+
+    // Flush the stream of data
+    pa_threaded_mainloop_lock(mainloop);
+    pa_operation *op = pa_stream_flush(stream, (pa_stream_success_cb_t)pa_stream_success, this);
+    pa_threaded_mainloop_unlock(mainloop);
+    stream_success.Wait();
+    pa_operation_unref(op);
+    if (!stream_success_val) {
+        paerror = pa_context_errno(context);
+        LOG_E("audio/player/pulse") << "Error flushing stream: " << pa_strerror(paerror) << "(" << paerror << ")";
+    }
 }
 
 void PulseAudioPlayer::SetEndPosition(int64_t pos)
 {
-	end_frame = pos;
+    end_frame = pos;
 }
 
 int64_t PulseAudioPlayer::GetCurrentPosition()
 {
-	if (!is_playing) return 0;
+    if (!is_playing) return 0;
 
-	// FIXME: this should be based on not duration played but actual sample being heard
-	// (during vidoeo playback, cur_frame might get changed to resync)
+    // FIXME: this should be based on not duration played but actual sample being heard
+    // (during vidoeo playback, cur_frame might get changed to resync)
 
-	// Calculation duration we have played, in microseconds
-	pa_usec_t play_cur_time;
-	pa_stream_get_time(stream, &play_cur_time);
-	pa_usec_t playtime = play_cur_time - play_start_time;
+    // Calculation duration we have played, in microseconds
+    pa_usec_t play_cur_time;
+    pa_stream_get_time(stream, &play_cur_time);
+    pa_usec_t playtime = play_cur_time - play_start_time;
 
-	return start_frame + playtime * provider->GetSampleRate() / (1000*1000);
+    return start_frame + playtime * provider->GetSampleRate() / (1000 * 1000);
 }
 
 /// @brief Called by PA to notify about other context-related stuff
 void PulseAudioPlayer::pa_context_notify(pa_context *c, PulseAudioPlayer *thread)
 {
-	thread->cstate = pa_context_get_state(thread->context);
-	thread->context_notify.Post();
+    thread->cstate = pa_context_get_state(thread->context);
+    thread->context_notify.Post();
 }
 
 /// @brief Called by PA when an operation completes
 void PulseAudioPlayer::pa_stream_success(pa_stream *p, int success, PulseAudioPlayer *thread)
 {
-	thread->stream_success_val = success;
-	thread->stream_success.Post();
+    thread->stream_success_val = success;
+    thread->stream_success.Post();
 }
 
 /// @brief Called by PA to request more data (and other things?)
 void PulseAudioPlayer::pa_stream_write(pa_stream *p, size_t length, PulseAudioPlayer *thread)
 {
-	if (!thread->is_playing) return;
-
-	if (thread->cur_frame >= thread->end_frame + thread->provider->GetSampleRate()) {
-		// More than a second past end of stream
-		thread->is_playing = false;
-		pa_operation *op = pa_stream_drain(p, nullptr, nullptr);
-		pa_operation_unref(op);
-		return;
-
-	} else if (thread->cur_frame >= thread->end_frame) {
-		// Past end of stream, but not a full second, add some silence
-		void *buf = calloc(length, 1);
-		::pa_stream_write(p, buf, length, free, 0, PA_SEEK_RELATIVE);
-		thread->cur_frame += length / thread->bpf;
-		return;
-	}
-
-	unsigned long bpf = thread->bpf;
-	unsigned long frames = length / thread->bpf;
-	unsigned long maxframes = thread->end_frame - thread->cur_frame;
-	if (frames > maxframes) frames = maxframes;
-	void *buf = malloc(frames * bpf);
-	thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume);
-	::pa_stream_write(p, buf, frames*bpf, free, 0, PA_SEEK_RELATIVE);
-	thread->cur_frame += frames;
+    if (!thread->is_playing) return;
+
+    if (thread->cur_frame >= thread->end_frame + thread->provider->GetSampleRate()) {
+        // More than a second past end of stream
+        thread->is_playing = false;
+        pa_operation *op = pa_stream_drain(p, nullptr, nullptr);
+        pa_operation_unref(op);
+        return;
+
+    }
+    else if (thread->cur_frame >= thread->end_frame) {
+        // Past end of stream, but not a full second, add some silence
+        void *buf = calloc(length, 1);
+        ::pa_stream_write(p, buf, length, free, 0, PA_SEEK_RELATIVE);
+        thread->cur_frame += length / thread->bpf;
+        return;
+    }
+
+    unsigned long bpf = thread->bpf;
+    unsigned long frames = length / thread->bpf;
+    unsigned long maxframes = thread->end_frame - thread->cur_frame;
+    if (frames > maxframes) frames = maxframes;
+    void *buf = malloc(frames * bpf);
+    thread->provider->GetAudioWithVolume(buf, thread->cur_frame, frames, thread->volume);
+    ::pa_stream_write(p, buf, frames * bpf, free, 0, PA_SEEK_RELATIVE);
+    thread->cur_frame += frames;
 }
 
 /// @brief Called by PA to notify about other stuff
 void PulseAudioPlayer::pa_stream_notify(pa_stream *p, PulseAudioPlayer *thread)
 {
-	thread->sstate = pa_stream_get_state(thread->stream);
-	thread->stream_notify.Post();
+    thread->sstate = pa_stream_get_state(thread->stream);
+    thread->stream_notify.Post();
 }
 }
 
-std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *provider, wxWindow *) {
-	return agi::make_unique<PulseAudioPlayer>(provider);
+std::unique_ptr<AudioPlayer> CreatePulseAudioPlayer(agi::AudioProvider *provider, wxWindow *)
+{
+    return agi::make_unique<PulseAudioPlayer>(provider);
 }
 #endif // WITH_LIBPULSE
diff --git a/src/audio_provider_avs.cpp b/src/audio_provider_avs.cpp
index b94cb4df4c0d24ea0e497cba65cd036da6590ce0..8318bdffef570c3a655f12f9b59e243e522b7f96 100644
--- a/src/audio_provider_avs.cpp
+++ b/src/audio_provider_avs.cpp
@@ -51,99 +51,103 @@
 
 namespace {
 class AvisynthAudioProvider final : public agi::AudioProvider {
-	AviSynthWrapper avs_wrapper;
-	PClip clip;
+    AviSynthWrapper avs_wrapper;
+    PClip clip;
 
-	void LoadFromClip(AVSValue clip);
-	void FillBuffer(void *buf, int64_t start, int64_t count) const;
+    void LoadFromClip(AVSValue clip);
+    void FillBuffer(void *buf, int64_t start, int64_t count) const;
 
 public:
-	AvisynthAudioProvider(agi::fs::path const& filename);
+    AvisynthAudioProvider(agi::fs::path const &filename);
 
-	bool NeedsCache() const override { return true; }
+    bool NeedsCache() const override { return true; }
 };
 
-AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const& filename) {
-	agi::acs::CheckFileRead(filename);
-
-	std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
-
-	try {
-		IScriptEnvironment *env = avs_wrapper.GetEnv();
-
-		// Include
-		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(agi::fs::ShortName(filename).c_str()), false, true };
-
-			// Load DirectShowSource.dll from app dir if it exists
-			agi::fs::path dsspath(config::path->Decode("?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"))
-				LoadFromClip(env->Invoke("DirectShowSource", AVSValue(args, 3), argnames));
-			// Otherwise fail
-			else
-				throw agi::AudioProviderError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory.");
-		}
-	}
-	catch (AvisynthError &err) {
-		std::string errmsg(err.msg);
-		if (errmsg.find("filter graph manager won't talk to me") != errmsg.npos)
-			throw agi::AudioDataNotFound("Avisynth error: " + errmsg);
-		else
-			throw agi::AudioProviderError("Avisynth error: " + errmsg);
-	}
+AvisynthAudioProvider::AvisynthAudioProvider(agi::fs::path const &filename)
+{
+    agi::acs::CheckFileRead(filename);
+
+    std::lock_guard<std::mutex> lock(avs_wrapper.GetMutex());
+
+    try {
+        IScriptEnvironment *env = avs_wrapper.GetEnv();
+
+        // Include
+        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(agi::fs::ShortName(filename).c_str()), false, true };
+
+            // Load DirectShowSource.dll from app dir if it exists
+            agi::fs::path dsspath(config::path->Decode("?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"))
+                LoadFromClip(env->Invoke("DirectShowSource", AVSValue(args, 3), argnames));
+            // Otherwise fail
+            else
+                throw agi::AudioProviderError("No suitable audio source filter found. Try placing DirectShowSource.dll in the Aegisub application directory.");
+        }
+    }
+    catch (AvisynthError &err) {
+        std::string errmsg(err.msg);
+        if (errmsg.find("filter graph manager won't talk to me") != errmsg.npos)
+            throw agi::AudioDataNotFound("Avisynth error: " + errmsg);
+        else
+            throw agi::AudioProviderError("Avisynth error: " + errmsg);
+    }
 }
 
-void AvisynthAudioProvider::LoadFromClip(AVSValue clip) {
-	// Check if it has audio
-	VideoInfo vi = clip.AsClip()->GetVideoInfo();
-	if (!vi.HasAudio()) throw agi::AudioDataNotFound("No audio found.");
-
-	IScriptEnvironment *env = avs_wrapper.GetEnv();
-
-	// Convert to one channel
-	AVSValue script = env->Invoke(OPT_GET("Audio/Downmixer")->GetString().c_str(), clip);
-
-	// Convert to 16 bits per sample
-	script = env->Invoke("ConvertAudioTo16bit", script);
-	vi = script.AsClip()->GetVideoInfo();
-
-	// Convert sample rate
-	int setsample = OPT_GET("Provider/Audio/AVS/Sample Rate")->GetInt();
-	if (setsample == 0 && vi.SamplesPerSecond() < 32000)
-		setsample = 44100;
-	if (setsample != 0) {
-		AVSValue args[2] = { script, setsample };
-		script = env->Invoke("ResampleAudio", AVSValue(args, 2));
-	}
-
-	// Set clip
-	PClip tempclip = script.AsClip();
-	vi = tempclip->GetVideoInfo();
-
-	// Read properties
-	channels = vi.AudioChannels();
-	decoded_samples = num_samples = vi.num_audio_samples;
-	sample_rate = vi.SamplesPerSecond();
-	bytes_per_sample = vi.BytesPerAudioSample();
-	float_samples = false;
-
-	this->clip = tempclip;
+void AvisynthAudioProvider::LoadFromClip(AVSValue clip)
+{
+    // Check if it has audio
+    VideoInfo vi = clip.AsClip()->GetVideoInfo();
+    if (!vi.HasAudio()) throw agi::AudioDataNotFound("No audio found.");
+
+    IScriptEnvironment *env = avs_wrapper.GetEnv();
+
+    // Convert to one channel
+    AVSValue script = env->Invoke(OPT_GET("Audio/Downmixer")->GetString().c_str(), clip);
+
+    // Convert to 16 bits per sample
+    script = env->Invoke("ConvertAudioTo16bit", script);
+    vi = script.AsClip()->GetVideoInfo();
+
+    // Convert sample rate
+    int setsample = OPT_GET("Provider/Audio/AVS/Sample Rate")->GetInt();
+    if (setsample == 0 && vi.SamplesPerSecond() < 32000)
+        setsample = 44100;
+    if (setsample != 0) {
+        AVSValue args[2] = { script, setsample };
+        script = env->Invoke("ResampleAudio", AVSValue(args, 2));
+    }
+
+    // Set clip
+    PClip tempclip = script.AsClip();
+    vi = tempclip->GetVideoInfo();
+
+    // Read properties
+    channels = vi.AudioChannels();
+    decoded_samples = num_samples = vi.num_audio_samples;
+    sample_rate = vi.SamplesPerSecond();
+    bytes_per_sample = vi.BytesPerAudioSample();
+    float_samples = false;
+
+    this->clip = tempclip;
 }
 
-void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const {
-	clip->GetAudio(buf, start, count, avs_wrapper.GetEnv());
+void AvisynthAudioProvider::FillBuffer(void *buf, int64_t start, int64_t count) const
+{
+    clip->GetAudio(buf, start, count, avs_wrapper.GetEnv());
 }
 }
 
-std::unique_ptr<agi::AudioProvider> CreateAvisynthAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *) {
-	return agi::make_unique<AvisynthAudioProvider>(file);
+std::unique_ptr<agi::AudioProvider> CreateAvisynthAudioProvider(agi::fs::path const &file, agi::BackgroundRunner *)
+{
+    return agi::make_unique<AvisynthAudioProvider>(file);
 }
 #endif
diff --git a/src/audio_provider_factory.cpp b/src/audio_provider_factory.cpp
index 887783644d8e29e386a7346448723795eefa92cd..4a207876fd438c3e0a9ffc7faaa53840b97578bd 100644
--- a/src/audio_provider_factory.cpp
+++ b/src/audio_provider_factory.cpp
@@ -29,100 +29,102 @@
 
 using namespace agi;
 
-std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(fs::path const& filename, BackgroundRunner *);
-std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(fs::path const& filename, BackgroundRunner *);
+std::unique_ptr<AudioProvider> CreateAvisynthAudioProvider(fs::path const &filename, BackgroundRunner *);
+std::unique_ptr<AudioProvider> CreateFFmpegSourceAudioProvider(fs::path const &filename, BackgroundRunner *);
 
 namespace {
 struct factory {
-	const char *name;
-	std::unique_ptr<AudioProvider> (*create)(fs::path const&, BackgroundRunner *);
-	bool hidden;
+    const char *name;
+    std::unique_ptr<AudioProvider> (*create)(fs::path const &, BackgroundRunner *);
+    bool hidden;
 };
 
 const factory providers[] = {
-	{"Dummy", CreateDummyAudioProvider, true},
-	{"PCM", CreatePCMAudioProvider, true},
+    {"Dummy", CreateDummyAudioProvider, true},
+    {"PCM", CreatePCMAudioProvider, true},
 #ifdef WITH_FFMS2
-	{"FFmpegSource", CreateFFmpegSourceAudioProvider, false},
+    {"FFmpegSource", CreateFFmpegSourceAudioProvider, false},
 #endif
 #ifdef WITH_AVISYNTH
-	{"Avisynth", CreateAvisynthAudioProvider, false},
+    {"Avisynth", CreateAvisynthAudioProvider, false},
 #endif
 };
 }
 
-std::vector<std::string> GetAudioProviderNames() {
-	return ::GetClasses(boost::make_iterator_range(std::begin(providers), std::end(providers)));
+std::vector<std::string> GetAudioProviderNames()
+{
+    return ::GetClasses(boost::make_iterator_range(std::begin(providers), std::end(providers)));
 }
 
-std::unique_ptr<agi::AudioProvider> GetAudioProvider(fs::path const& filename,
-                                                     Path const& path_helper,
-                                                     BackgroundRunner *br) {
-	auto preferred = OPT_GET("Audio/Provider")->GetString();
-	auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
-
-	std::unique_ptr<AudioProvider> provider;
-	bool found_file = false;
-	bool found_audio = false;
-	std::string msg_all;     // error messages from all attempted providers
-	std::string msg_partial; // error messages from providers that could partially load the file (knows container, missing codec)
-
-	for (auto const& factory : sorted) {
-		try {
-			provider = factory->create(filename, br);
-			if (!provider) continue;
-			LOG_I("audio_provider") << "Using audio provider: " << factory->name;
-			break;
-		}
-		catch (fs::FileNotFound const& err) {
-			LOG_D("audio_provider") << err.GetMessage();
-			msg_all += std::string(factory->name) + ": " + err.GetMessage() + " not found.\n";
-		}
-		catch (AudioDataNotFound const& err) {
-			LOG_D("audio_provider") << err.GetMessage();
-			found_file = true;
-			msg_all += std::string(factory->name) + ": " + err.GetMessage() + "\n";
-		}
-		catch (AudioProviderError const& err) {
-			LOG_D("audio_provider") << err.GetMessage();
-			found_audio = true;
-			found_file = true;
-			std::string thismsg = std::string(factory->name) + ": " + err.GetMessage() + "\n";
-			msg_all += thismsg;
-			msg_partial += thismsg;
-		}
-	}
-
-	if (!provider) {
-		if (found_audio)
-			throw AudioProviderError(msg_partial);
-		if (found_file)
-			throw AudioDataNotFound(msg_all);
-		throw fs::FileNotFound(filename);
-	}
-
-	bool needs_cache = provider->NeedsCache();
-
-	// Give it a converter if needed
-	if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000 || provider->GetChannels() != 1)
-		provider = CreateConvertAudioProvider(std::move(provider));
-
-	// Change provider to RAM/HD cache if needed
-	int cache = OPT_GET("Audio/Cache/Type")->GetInt();
-	if (!cache || !needs_cache)
-		return CreateLockAudioProvider(std::move(provider));
-
-	// Convert to RAM
-	if (cache == 1) return CreateRAMAudioProvider(std::move(provider));
-
-	// Convert to HD
-	if (cache == 2) {
-		auto path = OPT_GET("Audio/Cache/HD/Location")->GetString();
-		if (path == "default")
-			path = "?temp";
-		auto cache_dir = path_helper.MakeAbsolute(path_helper.Decode(path), "?temp");
-		return CreateHDAudioProvider(std::move(provider), cache_dir);
-	}
-
-	throw InternalError("Invalid audio caching method");
+std::unique_ptr<agi::AudioProvider> GetAudioProvider(fs::path const &filename,
+        Path const &path_helper,
+        BackgroundRunner *br)
+{
+    auto preferred = OPT_GET("Audio/Provider")->GetString();
+    auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
+
+    std::unique_ptr<AudioProvider> provider;
+    bool found_file = false;
+    bool found_audio = false;
+    std::string msg_all;     // error messages from all attempted providers
+    std::string msg_partial; // error messages from providers that could partially load the file (knows container, missing codec)
+
+    for (auto const &factory : sorted) {
+        try {
+            provider = factory->create(filename, br);
+            if (!provider) continue;
+            LOG_I("audio_provider") << "Using audio provider: " << factory->name;
+            break;
+        }
+        catch (fs::FileNotFound const &err) {
+            LOG_D("audio_provider") << err.GetMessage();
+            msg_all += std::string(factory->name) + ": " + err.GetMessage() + " not found.\n";
+        }
+        catch (AudioDataNotFound const &err) {
+            LOG_D("audio_provider") << err.GetMessage();
+            found_file = true;
+            msg_all += std::string(factory->name) + ": " + err.GetMessage() + "\n";
+        }
+        catch (AudioProviderError const &err) {
+            LOG_D("audio_provider") << err.GetMessage();
+            found_audio = true;
+            found_file = true;
+            std::string thismsg = std::string(factory->name) + ": " + err.GetMessage() + "\n";
+            msg_all += thismsg;
+            msg_partial += thismsg;
+        }
+    }
+
+    if (!provider) {
+        if (found_audio)
+            throw AudioProviderError(msg_partial);
+        if (found_file)
+            throw AudioDataNotFound(msg_all);
+        throw fs::FileNotFound(filename);
+    }
+
+    bool needs_cache = provider->NeedsCache();
+
+    // Give it a converter if needed
+    if (provider->GetBytesPerSample() != 2 || provider->GetSampleRate() < 32000 || provider->GetChannels() != 1)
+        provider = CreateConvertAudioProvider(std::move(provider));
+
+    // Change provider to RAM/HD cache if needed
+    int cache = OPT_GET("Audio/Cache/Type")->GetInt();
+    if (!cache || !needs_cache)
+        return CreateLockAudioProvider(std::move(provider));
+
+    // Convert to RAM
+    if (cache == 1) return CreateRAMAudioProvider(std::move(provider));
+
+    // Convert to HD
+    if (cache == 2) {
+        auto path = OPT_GET("Audio/Cache/HD/Location")->GetString();
+        if (path == "default")
+            path = "?temp";
+        auto cache_dir = path_helper.MakeAbsolute(path_helper.Decode(path), "?temp");
+        return CreateHDAudioProvider(std::move(provider), cache_dir);
+    }
+
+    throw InternalError("Invalid audio caching method");
 }
diff --git a/src/audio_provider_factory.h b/src/audio_provider_factory.h
index b419d6d0a22ceb65c43db1a52c767b34ac1f9969..fcef9012aafd8df25648c06b4b721d7077bae205 100644
--- a/src/audio_provider_factory.h
+++ b/src/audio_provider_factory.h
@@ -20,12 +20,12 @@
 #include <vector>
 
 namespace agi {
-	class AudioProvider;
-	class BackgroundRunner;
-	class Path;
+class AudioProvider;
+class BackgroundRunner;
+class Path;
 }
 
-std::unique_ptr<agi::AudioProvider> GetAudioProvider(agi::fs::path const& filename,
-                                                     agi::Path const& path_helper,
-                                                     agi::BackgroundRunner *br);
+std::unique_ptr<agi::AudioProvider> GetAudioProvider(agi::fs::path const &filename,
+        agi::Path const &path_helper,
+        agi::BackgroundRunner *br);
 std::vector<std::string> GetAudioProviderNames();
diff --git a/src/audio_provider_ffmpegsource.cpp b/src/audio_provider_ffmpegsource.cpp
index 0a201855ac6caca0be11b845403616417675d198..6800d7d9e9efaef2ba25ce2b400181454c3b735f 100644
--- a/src/audio_provider_ffmpegsource.cpp
+++ b/src/audio_provider_ffmpegsource.cpp
@@ -45,145 +45,148 @@
 
 namespace {
 class FFmpegSourceAudioProvider final : public agi::AudioProvider, FFmpegSourceProvider {
-	/// audio source object
-	agi::scoped_holder<FFMS_AudioSource*, void (FFMS_CC *)(FFMS_AudioSource*)> AudioSource;
+    /// audio source object
+    agi::scoped_holder<FFMS_AudioSource *, void (FFMS_CC *)(FFMS_AudioSource *)> AudioSource;
 
-	mutable char FFMSErrMsg[1024];			///< FFMS error message
-	mutable FFMS_ErrorInfo ErrInfo;			///< FFMS error codes/messages
+    mutable char FFMSErrMsg[1024];			///< FFMS error message
+    mutable FFMS_ErrorInfo ErrInfo;			///< FFMS error codes/messages
 
-	void LoadAudio(agi::fs::path const& filename);
-	void FillBuffer(void *Buf, int64_t Start, int64_t Count) const override {
-		if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
-			throw agi::AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
-	}
+    void LoadAudio(agi::fs::path const &filename);
+    void FillBuffer(void *Buf, int64_t Start, int64_t Count) const override {
+        if (FFMS_GetAudio(AudioSource, Buf, Start, Count, &ErrInfo))
+            throw agi::AudioDecodeError(std::string("Failed to get audio samples: ") + ErrInfo.Buffer);
+    }
 
 public:
-	FFmpegSourceAudioProvider(agi::fs::path const& filename, agi::BackgroundRunner *br);
+    FFmpegSourceAudioProvider(agi::fs::path const &filename, agi::BackgroundRunner *br);
 
-	bool NeedsCache() const override { return true; }
+    bool NeedsCache() const override { return true; }
 };
 
 /// @brief Constructor
 /// @param filename The filename to open
-FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const& filename, agi::BackgroundRunner *br) try
+FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(agi::fs::path const &filename, agi::BackgroundRunner *br) try
 : FFmpegSourceProvider(br)
-, AudioSource(nullptr, FFMS_DestroyAudioSource)
+    , AudioSource(nullptr, FFMS_DestroyAudioSource)
 {
-	ErrInfo.Buffer		= FFMSErrMsg;
-	ErrInfo.BufferSize	= sizeof(FFMSErrMsg);
-	ErrInfo.ErrorType	= FFMS_ERROR_SUCCESS;
-	ErrInfo.SubType		= FFMS_ERROR_SUCCESS;
-	SetLogLevel();
+    ErrInfo.Buffer		= FFMSErrMsg;
+    ErrInfo.BufferSize	= sizeof(FFMSErrMsg);
+    ErrInfo.ErrorType	= FFMS_ERROR_SUCCESS;
+    ErrInfo.SubType		= FFMS_ERROR_SUCCESS;
+    SetLogLevel();
 
-	LoadAudio(filename);
+    LoadAudio(filename);
 }
-catch (agi::EnvironmentError const& err) {
-	throw agi::AudioProviderError(err.GetMessage());
+catch (agi::EnvironmentError const &err)
+{
+    throw agi::AudioProviderError(err.GetMessage());
 }
 
-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::fs::FileNotFound(std::string(ErrInfo.Buffer));
-		else
-			throw agi::AudioDataNotFound(ErrInfo.Buffer);
-	}
-
-	std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
-
-	// initialize the track number to an invalid value so we can detect later on
-	// whether the user actually had to choose a track or not
-	int TrackNumber = -1;
-	if (TrackList.size() > 1) {
-		auto Selection = AskForTrackSelection(TrackList, FFMS_TYPE_AUDIO);
-		if (Selection == TrackSelection::None)
-			throw agi::UserCancelException("audio loading canceled by user");
-		TrackNumber = static_cast<int>(Selection);
-	}
-	else if (TrackList.size() == 1)
-		TrackNumber = TrackList.begin()->first;
-	else
-		throw agi::AudioDataNotFound("no audio tracks found");
-
-	// generate a name for the cache file
-	agi::fs::path CacheName = GetCacheFilename(filename);
-
-	// try to read index
-	agi::scoped_holder<FFMS_Index*, void (FFMS_CC*)(FFMS_Index*)>
-		Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
-
-	if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo))
-		Index = nullptr;
-
-	if (Index) {
-		// we already have an index, but the desired track may not have been
-		// indexed, and if it wasn't we need to reindex
-		FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
-		if (FFMS_GetNumFrames(TempTrackData) <= 0)
-			Index = nullptr;
-	}
-
-	// reindex if the error handling mode has changed
-	FFMS_IndexErrorHandling ErrorHandling = GetErrorHandlingMode();
-	if (Index && FFMS_GetErrorHandling(Index) != ErrorHandling)
-		Index = nullptr;
-
-	// moment of truth
-	if (!Index) {
-		TrackSelection TrackMask = static_cast<TrackSelection>(TrackNumber);
-		if (OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool())
-			TrackMask = TrackSelection::All;
-		Index = DoIndexing(Indexer, CacheName, TrackMask, ErrorHandling);
-	}
-	else
-		FFMS_CancelIndexing(Indexer);
-
-	// update access time of index file so it won't get cleaned away
-	agi::fs::Touch(CacheName);
-
-	AudioSource = FFMS_CreateAudioSource(filename.string().c_str(), TrackNumber, Index, FFMS_DELAY_FIRST_VIDEO_TRACK, &ErrInfo);
-	if (!AudioSource)
-		throw agi::AudioProviderError(std::string("Failed to open audio track: ") + ErrInfo.Buffer);
-
-	const FFMS_AudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource);
-
-	channels	= AudioInfo.Channels;
-	sample_rate	= AudioInfo.SampleRate;
-	num_samples = AudioInfo.NumSamples;
-	decoded_samples = AudioInfo.NumSamples;
-	if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
-		throw agi::AudioProviderError("sanity check failed, consult your local psychiatrist");
-
-	switch (AudioInfo.SampleFormat) {
-		case FFMS_FMT_U8:  bytes_per_sample = 1; float_samples = false; break;
-		case FFMS_FMT_S16: bytes_per_sample = 2; float_samples = false; break;
-		case FFMS_FMT_S32: bytes_per_sample = 4; float_samples = false; break;
-		case FFMS_FMT_FLT: bytes_per_sample = 4; float_samples = true; break;
-		case FFMS_FMT_DBL: bytes_per_sample = 8; float_samples = true; break;
-		default:
-			throw agi::AudioProviderError("unknown or unsupported sample format");
-	}
-
-	if (channels > 1 || bytes_per_sample != 2) {
-		std::unique_ptr<FFMS_ResampleOptions, decltype(&FFMS_DestroyResampleOptions)>
-			opt(FFMS_CreateResampleOptions(AudioSource), FFMS_DestroyResampleOptions);
-		opt->ChannelLayout = FFMS_CH_FRONT_CENTER;
-		opt->SampleFormat = FFMS_FMT_S16;
-
-		// Might fail if FFMS2 wasn't built with libavresample
-		if (!FFMS_SetOutputFormatA(AudioSource, opt.get(), nullptr)) {
-			channels = 1;
-			bytes_per_sample = 2;
-			float_samples = false;
-		}
-	}
+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::fs::FileNotFound(std::string(ErrInfo.Buffer));
+        else
+            throw agi::AudioDataNotFound(ErrInfo.Buffer);
+    }
+
+    std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_AUDIO);
+
+    // initialize the track number to an invalid value so we can detect later on
+    // whether the user actually had to choose a track or not
+    int TrackNumber = -1;
+    if (TrackList.size() > 1) {
+        auto Selection = AskForTrackSelection(TrackList, FFMS_TYPE_AUDIO);
+        if (Selection == TrackSelection::None)
+            throw agi::UserCancelException("audio loading canceled by user");
+        TrackNumber = static_cast<int>(Selection);
+    }
+    else if (TrackList.size() == 1)
+        TrackNumber = TrackList.begin()->first;
+    else
+        throw agi::AudioDataNotFound("no audio tracks found");
+
+    // generate a name for the cache file
+    agi::fs::path CacheName = GetCacheFilename(filename);
+
+    // try to read index
+    agi::scoped_holder<FFMS_Index *, void (FFMS_CC *)(FFMS_Index *)>
+    Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
+
+    if (Index && FFMS_IndexBelongsToFile(Index, filename.string().c_str(), &ErrInfo))
+        Index = nullptr;
+
+    if (Index) {
+        // we already have an index, but the desired track may not have been
+        // indexed, and if it wasn't we need to reindex
+        FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
+        if (FFMS_GetNumFrames(TempTrackData) <= 0)
+            Index = nullptr;
+    }
+
+    // reindex if the error handling mode has changed
+    FFMS_IndexErrorHandling ErrorHandling = GetErrorHandlingMode();
+    if (Index && FFMS_GetErrorHandling(Index) != ErrorHandling)
+        Index = nullptr;
+
+    // moment of truth
+    if (!Index) {
+        TrackSelection TrackMask = static_cast<TrackSelection>(TrackNumber);
+        if (OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool())
+            TrackMask = TrackSelection::All;
+        Index = DoIndexing(Indexer, CacheName, TrackMask, ErrorHandling);
+    }
+    else
+        FFMS_CancelIndexing(Indexer);
+
+    // update access time of index file so it won't get cleaned away
+    agi::fs::Touch(CacheName);
+
+    AudioSource = FFMS_CreateAudioSource(filename.string().c_str(), TrackNumber, Index, FFMS_DELAY_FIRST_VIDEO_TRACK, &ErrInfo);
+    if (!AudioSource)
+        throw agi::AudioProviderError(std::string("Failed to open audio track: ") + ErrInfo.Buffer);
+
+    const FFMS_AudioProperties AudioInfo = *FFMS_GetAudioProperties(AudioSource);
+
+    channels	= AudioInfo.Channels;
+    sample_rate	= AudioInfo.SampleRate;
+    num_samples = AudioInfo.NumSamples;
+    decoded_samples = AudioInfo.NumSamples;
+    if (channels <= 0 || sample_rate <= 0 || num_samples <= 0)
+        throw agi::AudioProviderError("sanity check failed, consult your local psychiatrist");
+
+    switch (AudioInfo.SampleFormat) {
+    case FFMS_FMT_U8:  bytes_per_sample = 1; float_samples = false; break;
+    case FFMS_FMT_S16: bytes_per_sample = 2; float_samples = false; break;
+    case FFMS_FMT_S32: bytes_per_sample = 4; float_samples = false; break;
+    case FFMS_FMT_FLT: bytes_per_sample = 4; float_samples = true; break;
+    case FFMS_FMT_DBL: bytes_per_sample = 8; float_samples = true; break;
+    default:
+        throw agi::AudioProviderError("unknown or unsupported sample format");
+    }
+
+    if (channels > 1 || bytes_per_sample != 2) {
+        std::unique_ptr<FFMS_ResampleOptions, decltype(&FFMS_DestroyResampleOptions)>
+        opt(FFMS_CreateResampleOptions(AudioSource), FFMS_DestroyResampleOptions);
+        opt->ChannelLayout = FFMS_CH_FRONT_CENTER;
+        opt->SampleFormat = FFMS_FMT_S16;
+
+        // Might fail if FFMS2 wasn't built with libavresample
+        if (!FFMS_SetOutputFormatA(AudioSource, opt.get(), nullptr)) {
+            channels = 1;
+            bytes_per_sample = 2;
+            float_samples = false;
+        }
+    }
 }
 
 }
 
-std::unique_ptr<agi::AudioProvider> CreateFFmpegSourceAudioProvider(agi::fs::path const& file, agi::BackgroundRunner *br) {
-	return agi::make_unique<FFmpegSourceAudioProvider>(file, br);
+std::unique_ptr<agi::AudioProvider> CreateFFmpegSourceAudioProvider(agi::fs::path const &file, agi::BackgroundRunner *br)
+{
+    return agi::make_unique<FFmpegSourceAudioProvider>(file, br);
 }
 
 #endif /* WITH_FFMS2 */
diff --git a/src/audio_renderer.cpp b/src/audio_renderer.cpp
index e90586b026681273d62efe90f237af7f4b6663cd..ff698b3ba03136ffc57b00848993047f5519a65e 100644
--- a/src/audio_renderer.cpp
+++ b/src/audio_renderer.cpp
@@ -40,203 +40,194 @@
 #include <wx/dc.h>
 
 namespace {
-	template<typename T>
-	bool compare_and_set(T &var, const T new_value)
-	{
-		if (var == new_value) return false;
-		var = new_value;
-		return true;
-	}
+template<typename T>
+bool compare_and_set(T &var, const T new_value)
+{
+    if (var == new_value) return false;
+    var = new_value;
+    return true;
+}
 }
 
 AudioRendererBitmapCacheBitmapFactory::AudioRendererBitmapCacheBitmapFactory(AudioRenderer *renderer)
-: renderer(renderer)
+    : renderer(renderer)
 {
-	assert(renderer);
+    assert(renderer);
 }
 
 std::unique_ptr<wxBitmap> AudioRendererBitmapCacheBitmapFactory::ProduceBlock(int /* i */)
 {
-	return agi::make_unique<wxBitmap>(renderer->cache_bitmap_width, renderer->pixel_height, 24);
+    return agi::make_unique<wxBitmap>(renderer->cache_bitmap_width, renderer->pixel_height, 24);
 }
 
 size_t AudioRendererBitmapCacheBitmapFactory::GetBlockSize() const
 {
-	return sizeof(wxBitmap) + renderer->cache_bitmap_width * renderer->pixel_height * 3;
+    return sizeof(wxBitmap) + renderer->cache_bitmap_width * renderer->pixel_height * 3;
 }
 
 AudioRenderer::AudioRenderer()
 {
-	bitmaps.reserve(AudioStyle_MAX);
-	for (int i = 0; i < AudioStyle_MAX; ++i)
-		bitmaps.emplace_back(256, AudioRendererBitmapCacheBitmapFactory(this));
+    bitmaps.reserve(AudioStyle_MAX);
+    for (int i = 0; i < AudioStyle_MAX; ++i)
+        bitmaps.emplace_back(256, AudioRendererBitmapCacheBitmapFactory(this));
 
-	// Make sure there's *some* values for those fields, and in the caches
-	SetMillisecondsPerPixel(1);
-	SetHeight(1);
+    // Make sure there's *some* values for those fields, and in the caches
+    SetMillisecondsPerPixel(1);
+    SetHeight(1);
 }
 
 void AudioRenderer::SetMillisecondsPerPixel(const double new_pixel_ms)
 {
-	if (compare_and_set(pixel_ms, new_pixel_ms))
-	{
-		if (renderer)
-			renderer->SetMillisecondsPerPixel(pixel_ms);
+    if (compare_and_set(pixel_ms, new_pixel_ms)) {
+        if (renderer)
+            renderer->SetMillisecondsPerPixel(pixel_ms);
 
-		ResetBlockCount();
-	}
+        ResetBlockCount();
+    }
 }
 
 void AudioRenderer::SetHeight(const int _pixel_height)
 {
-	if (compare_and_set(pixel_height, _pixel_height))
-		Invalidate();
+    if (compare_and_set(pixel_height, _pixel_height))
+        Invalidate();
 }
 
 void AudioRenderer::SetAmplitudeScale(const float _amplitude_scale)
 {
-	if (compare_and_set(amplitude_scale, _amplitude_scale))
-	{
-		// A scaling of 0 or a negative scaling makes no sense
-		assert(amplitude_scale > 0);
-		if (renderer)
-			renderer->SetAmplitudeScale(amplitude_scale);
-		Invalidate();
-	}
+    if (compare_and_set(amplitude_scale, _amplitude_scale)) {
+        // A scaling of 0 or a negative scaling makes no sense
+        assert(amplitude_scale > 0);
+        if (renderer)
+            renderer->SetAmplitudeScale(amplitude_scale);
+        Invalidate();
+    }
 }
 
 void AudioRenderer::SetRenderer(AudioRendererBitmapProvider *const _renderer)
 {
-	if (compare_and_set(renderer, _renderer))
-	{
-		Invalidate();
+    if (compare_and_set(renderer, _renderer)) {
+        Invalidate();
 
-		if (renderer)
-		{
-			renderer->SetProvider(provider);
-			renderer->SetAmplitudeScale(amplitude_scale);
-			renderer->SetMillisecondsPerPixel(pixel_ms);
-		}
-	}
+        if (renderer) {
+            renderer->SetProvider(provider);
+            renderer->SetAmplitudeScale(amplitude_scale);
+            renderer->SetMillisecondsPerPixel(pixel_ms);
+        }
+    }
 }
 
 void AudioRenderer::SetAudioProvider(agi::AudioProvider *const _provider)
 {
-	if (compare_and_set(provider, _provider))
-	{
-		Invalidate();
+    if (compare_and_set(provider, _provider)) {
+        Invalidate();
 
-		if (renderer)
-			renderer->SetProvider(provider);
+        if (renderer)
+            renderer->SetProvider(provider);
 
-		ResetBlockCount();
-	}
+        ResetBlockCount();
+    }
 }
 
 void AudioRenderer::SetCacheMaxSize(const size_t max_size)
 {
-	// Limit the bitmap cache sizes to 16 MB hard, to avoid the risk of exhausting
-	// system bitmap object resources and similar. Experimenting shows that 16 MB
-	// bitmap cache should be plenty even if working with a one hour audio clip.
-	cache_bitmap_maxsize = std::min<size_t>(max_size/8, 0x1000000);
-	// The renderer gets whatever is left.
-	cache_renderer_maxsize = max_size - 4*cache_bitmap_maxsize;
+    // Limit the bitmap cache sizes to 16 MB hard, to avoid the risk of exhausting
+    // system bitmap object resources and similar. Experimenting shows that 16 MB
+    // bitmap cache should be plenty even if working with a one hour audio clip.
+    cache_bitmap_maxsize = std::min<size_t>(max_size / 8, 0x1000000);
+    // The renderer gets whatever is left.
+    cache_renderer_maxsize = max_size - 4 * cache_bitmap_maxsize;
 }
 
 void AudioRenderer::ResetBlockCount()
 {
-	if (provider)
-	{
-		const size_t total_blocks = NumBlocks(provider->GetNumSamples());
-		for (auto& bmp : bitmaps) bmp.SetBlockCount(total_blocks);
-	}
+    if (provider) {
+        const size_t total_blocks = NumBlocks(provider->GetNumSamples());
+        for (auto &bmp : bitmaps) bmp.SetBlockCount(total_blocks);
+    }
 }
 
 size_t AudioRenderer::NumBlocks(const int64_t samples) const
 {
-	const double duration = samples * 1000.0 / provider->GetSampleRate();
-	return static_cast<size_t>(duration / pixel_ms / cache_bitmap_width);
+    const double duration = samples * 1000.0 / provider->GetSampleRate();
+    return static_cast<size_t>(duration / pixel_ms / cache_bitmap_width);
 }
 
-wxBitmap const& AudioRenderer::GetCachedBitmap(const int i, const AudioRenderingStyle style)
+wxBitmap const &AudioRenderer::GetCachedBitmap(const int i, const AudioRenderingStyle style)
 {
-	assert(provider);
-	assert(renderer);
+    assert(provider);
+    assert(renderer);
 
-	bool created = false;
-	auto& bmp = bitmaps[style].Get(i, &created);
-	if (created)
-	{
-		renderer->Render(bmp, i*cache_bitmap_width, style);
-		needs_age = true;
-	}
+    bool created = false;
+    auto &bmp = bitmaps[style].Get(i, &created);
+    if (created) {
+        renderer->Render(bmp, i * cache_bitmap_width, style);
+        needs_age = true;
+    }
 
-	assert(bmp.IsOk());
-	return bmp;
+    assert(bmp.IsOk());
+    return bmp;
 }
 
 void AudioRenderer::Render(wxDC &dc, wxPoint origin, const int start, const int length, const AudioRenderingStyle style)
 {
-	assert(start >= 0);
-
-	if (!provider) return;
-	if (!renderer) return;
-	if (length <= 0) return;
-
-	// One past last absolute pixel strip to render
-	const int end = start + length;
-	// One past last X coordinate to render on
-	const int lastx = origin.x + length;
-	// Figure out which range of bitmaps are required
-	const int firstbitmap = start / cache_bitmap_width;
-	// And the offset in it to start its use at
-	const int firstbitmapoffset = start % cache_bitmap_width;
-	// The last bitmap required
-	const int lastbitmap = std::min<int>(end / cache_bitmap_width, NumBlocks(provider->GetDecodedSamples()) - 1);
-
-	// Set a clipping region so that the first and last bitmaps don't draw
-	// outside the requested range
-	const wxDCClipper clipper(dc, wxRect(origin, wxSize(length, pixel_height)));
-	origin.x -= firstbitmapoffset;
-
-	for (int i = firstbitmap; i <= lastbitmap; ++i)
-	{
-		dc.DrawBitmap(GetCachedBitmap(i, style), origin);
-		origin.x += cache_bitmap_width;
-	}
-
-	// Now render blank audio from origin to end
-	if (origin.x < lastx)
-		renderer->RenderBlank(dc, wxRect(origin.x-1, origin.y, lastx-origin.x+1, pixel_height), style);
-
-	if (needs_age)
-	{
-		bitmaps[style].Age(cache_bitmap_maxsize);
-		renderer->AgeCache(cache_renderer_maxsize);
-		needs_age = false;
-	}
+    assert(start >= 0);
+
+    if (!provider) return;
+    if (!renderer) return;
+    if (length <= 0) return;
+
+    // One past last absolute pixel strip to render
+    const int end = start + length;
+    // One past last X coordinate to render on
+    const int lastx = origin.x + length;
+    // Figure out which range of bitmaps are required
+    const int firstbitmap = start / cache_bitmap_width;
+    // And the offset in it to start its use at
+    const int firstbitmapoffset = start % cache_bitmap_width;
+    // The last bitmap required
+    const int lastbitmap = std::min<int>(end / cache_bitmap_width, NumBlocks(provider->GetDecodedSamples()) - 1);
+
+    // Set a clipping region so that the first and last bitmaps don't draw
+    // outside the requested range
+    const wxDCClipper clipper(dc, wxRect(origin, wxSize(length, pixel_height)));
+    origin.x -= firstbitmapoffset;
+
+    for (int i = firstbitmap; i <= lastbitmap; ++i) {
+        dc.DrawBitmap(GetCachedBitmap(i, style), origin);
+        origin.x += cache_bitmap_width;
+    }
+
+    // Now render blank audio from origin to end
+    if (origin.x < lastx)
+        renderer->RenderBlank(dc, wxRect(origin.x - 1, origin.y, lastx - origin.x + 1, pixel_height), style);
+
+    if (needs_age) {
+        bitmaps[style].Age(cache_bitmap_maxsize);
+        renderer->AgeCache(cache_renderer_maxsize);
+        needs_age = false;
+    }
 }
 
 void AudioRenderer::Invalidate()
 {
-	for (auto& bmp : bitmaps) bmp.Age(0);
-	needs_age = false;
+    for (auto &bmp : bitmaps) bmp.Age(0);
+    needs_age = false;
 }
 
 void AudioRendererBitmapProvider::SetProvider(agi::AudioProvider *const _provider)
 {
-	if (compare_and_set(provider, _provider))
-		OnSetProvider();
+    if (compare_and_set(provider, _provider))
+        OnSetProvider();
 }
 
 void AudioRendererBitmapProvider::SetMillisecondsPerPixel(const double new_pixel_ms)
 {
-	if (compare_and_set(pixel_ms, new_pixel_ms))
-		OnSetMillisecondsPerPixel();
+    if (compare_and_set(pixel_ms, new_pixel_ms))
+        OnSetMillisecondsPerPixel();
 }
 
 void AudioRendererBitmapProvider::SetAmplitudeScale(const float _amplitude_scale)
 {
-	if (compare_and_set(amplitude_scale, _amplitude_scale))
-		OnSetAmplitudeScale();
+    if (compare_and_set(amplitude_scale, _amplitude_scale))
+        OnSetAmplitudeScale();
 }
diff --git a/src/audio_renderer.h b/src/audio_renderer.h
index 1d88be3cb7bbdc12cac9491ddaa58b81029c491c..8129f1dd35eaa72cfd87a516bfe5e7ace4c2a0a3 100644
--- a/src/audio_renderer.h
+++ b/src/audio_renderer.h
@@ -45,25 +45,25 @@ namespace agi { class AudioProvider; }
 /// @class AudioRendererBitmapCacheBitmapFactory
 /// @brief Produces wxBitmap objects for DataBlockCache storage for the audio renderer
 struct AudioRendererBitmapCacheBitmapFactory {
-	typedef std::unique_ptr<wxBitmap> BlockType;
+    typedef std::unique_ptr<wxBitmap> BlockType;
 
-	/// The audio renderer we're producing bitmaps for
-	AudioRenderer *renderer;
+    /// The audio renderer we're producing bitmaps for
+    AudioRenderer *renderer;
 
-	/// @brief Constructor
-	/// @param renderer The audio renderer to produce bitmaps for
-	AudioRendererBitmapCacheBitmapFactory(AudioRenderer *renderer);
+    /// @brief Constructor
+    /// @param renderer The audio renderer to produce bitmaps for
+    AudioRendererBitmapCacheBitmapFactory(AudioRenderer *renderer);
 
-	/// @brief Create a new bitmap
-	/// @param i Unused
-	/// @return A fresh wxBitmap
-	///
-	/// Produces a wxBitmap with dimensions pulled from our master AudioRenderer.
-	std::unique_ptr<wxBitmap> ProduceBlock(int i);
+    /// @brief Create a new bitmap
+    /// @param i Unused
+    /// @return A fresh wxBitmap
+    ///
+    /// Produces a wxBitmap with dimensions pulled from our master AudioRenderer.
+    std::unique_ptr<wxBitmap> ProduceBlock(int i);
 
-	/// @brief Calculate the size of bitmaps
-	/// @return The size of bitmaps created
-	size_t GetBlockSize() const;
+    /// @brief Calculate the size of bitmaps
+    /// @return The size of bitmaps created
+    size_t GetBlockSize() const;
 };
 
 /// The type of a bitmap cache
@@ -77,142 +77,142 @@ typedef DataBlockCache<wxBitmap, 8, AudioRendererBitmapCacheBitmapFactory> Audio
 ///
 /// To implement a new audio renderer, see AudioRendererBitmapProvider.
 class AudioRenderer {
-	friend struct AudioRendererBitmapCacheBitmapFactory;
-
-	/// Horizontal zoom level, milliseconds per pixel
-	double pixel_ms = 0.f;
-	/// Rendering height in pixels
-	int pixel_height = 0;
-	/// Vertical zoom level/amplitude scale
-	float amplitude_scale = 0.f;
-
-	/// Width of bitmaps to store in cache
-	const int cache_bitmap_width = 32; // Completely arbitrary value
-
-	/// Cached bitmaps for audio ranges
-	std::vector<AudioRendererBitmapCache> bitmaps;
-	/// The maximum allowed size of each bitmap cache, in bytes
-	size_t cache_bitmap_maxsize = 0;
-	/// The maximum allowed size of the renderer's cache, in bytes
-	size_t cache_renderer_maxsize = 0;
-	/// Do the caches need to be aged?
-	bool needs_age = false;
-
-	/// Actual renderer for bitmaps
-	AudioRendererBitmapProvider *renderer = nullptr;
-
-	/// Audio provider to use as source
-	agi::AudioProvider *provider = nullptr;
-
-	/// @brief Make sure bitmap index i is in cache
-	/// @param i     Index of bitmap to get into cache
-	/// @param style Rendering style required for bitmap
-	/// @return The requested bitmap
-	///
-	/// Will attempt retrieving the requested bitmap from the cache, creating it
-	/// if the cache doesn't have it.
-	wxBitmap const& GetCachedBitmap(int i, AudioRenderingStyle style);
-
-	/// @brief Update the block count in the bitmap caches
-	///
-	/// Should be called when the width of the virtual bitmap has changed, i.e.
-	/// when the samples-per-pixel resolution or the number of audio samples
-	/// has changed.
-	void ResetBlockCount();
-
-	/// Calculate the number of cache blocks needed for a given number of samples
-	size_t NumBlocks(int64_t samples) const;
+    friend struct AudioRendererBitmapCacheBitmapFactory;
+
+    /// Horizontal zoom level, milliseconds per pixel
+    double pixel_ms = 0.f;
+    /// Rendering height in pixels
+    int pixel_height = 0;
+    /// Vertical zoom level/amplitude scale
+    float amplitude_scale = 0.f;
+
+    /// Width of bitmaps to store in cache
+    const int cache_bitmap_width = 32; // Completely arbitrary value
+
+    /// Cached bitmaps for audio ranges
+    std::vector<AudioRendererBitmapCache> bitmaps;
+    /// The maximum allowed size of each bitmap cache, in bytes
+    size_t cache_bitmap_maxsize = 0;
+    /// The maximum allowed size of the renderer's cache, in bytes
+    size_t cache_renderer_maxsize = 0;
+    /// Do the caches need to be aged?
+    bool needs_age = false;
+
+    /// Actual renderer for bitmaps
+    AudioRendererBitmapProvider *renderer = nullptr;
+
+    /// Audio provider to use as source
+    agi::AudioProvider *provider = nullptr;
+
+    /// @brief Make sure bitmap index i is in cache
+    /// @param i     Index of bitmap to get into cache
+    /// @param style Rendering style required for bitmap
+    /// @return The requested bitmap
+    ///
+    /// Will attempt retrieving the requested bitmap from the cache, creating it
+    /// if the cache doesn't have it.
+    wxBitmap const &GetCachedBitmap(int i, AudioRenderingStyle style);
+
+    /// @brief Update the block count in the bitmap caches
+    ///
+    /// Should be called when the width of the virtual bitmap has changed, i.e.
+    /// when the samples-per-pixel resolution or the number of audio samples
+    /// has changed.
+    void ResetBlockCount();
+
+    /// Calculate the number of cache blocks needed for a given number of samples
+    size_t NumBlocks(int64_t samples) const;
 
 public:
-	/// @brief Constructor
-	///
-	/// Initialises audio rendering to a do-nothing state. An audio provider
-	/// and bitmap provider must be set before the audio renderer is functional.
-	AudioRenderer();
-
-	/// @brief Set horizontal zoom
-	/// @param pixel_ms Milliseconds per pixel to render audio at
-	///
-	/// Changing the zoom level invalidates all cached bitmaps.
-	void SetMillisecondsPerPixel(double pixel_ms);
-
-	/// @brief Set rendering height
-	/// @param pixel_height Height in pixels to render at
-	///
-	/// Changing the rendering height invalidates all cached bitmaps.
-	void SetHeight(int pixel_height);
-
-	/// @brief Set vertical zoom
-	/// @param amplitude_scale Scaling factor
-	///
-	/// Changing the scaling factor invalidates all cached bitmaps.
-	///
-	/// A scaling factor of 1.0 is no scaling, a factor of 0.5 causes the audio to be
-	/// rendered as if it had half its actual amplitude, a factor of 2 causes the audio
-	/// to be rendered as if it had double amplitude. (The exact meaning of the scaling
-	/// depends on the bitmap provider used.)
-	void SetAmplitudeScale(float amplitude_scale);
-
-	/// @brief Set the maximum allowed cache size
-	/// @param max_size Size in bytes that may be used for caching
-	///
-	/// The given max size is not a hard limit and does generally not include overhead
-	/// added by the cache management. The allowed size might be distributed among
-	/// several separate objects.
-	///
-	/// Changing the max cache size does not trigger cache aging.
-	void SetCacheMaxSize(size_t max_size);
-
-	/// @brief Change renderer
-	/// @param renderer New renderer to use
-	///
-	/// The consumer of audio rendering is responsible for creating, managing and destroying
-	/// audio bitmap providers (renderers). If a renderer was previously set with this function
-	/// and a new one is set, the consumer of audio rendering is still responsible for the
-	/// life of the old renderer.
-	///
-	/// A bitmap provider must be assigned to a newly created audio renderer before it
-	/// can be functional.
-	///
-	/// Changing renderer invalidates all cached bitmaps.
-	void SetRenderer(AudioRendererBitmapProvider *renderer);
-
-	/// @brief Change audio provider
-	/// @param provider New audio provider to use
-	///
-	/// The consumer of audio rendering is responsible for managing audio providers.
-	/// If an audio provider was previously assigned to the audio renderer and a
-	/// new one is assigned, the consumer of audio rendering is still responsible for
-	/// the life of the old audio provider.
-	///
-	/// An audio provider must be assigned to a newly created audio renderer before it
-	/// can be functional.
-	///
-	/// Changing audio provider invalidates all cached bitmaps.
-	///
-	/// If a renderer is set, this will also set the audio provider for the renderer.
-	void SetAudioProvider(agi::AudioProvider *provider);
-
-	/// @brief Render audio to a device context
-	/// @param dc       The device context to draw to
-	/// @param origin   Top left corner to render at, in the DC's coordinates
-	/// @param start    First pixel from beginning of the audio stream to render
-	/// @param length   Number of pixels of audio to render
-	/// @param style    Style to render audio in
-	///
-	/// The first audio sample rendered is start*pixel_samples, and the number
-	/// of audio samples rendered is length*pixel_samples.
-	void Render(wxDC &dc, wxPoint origin, int start, int length, AudioRenderingStyle style);
-
-	/// @brief Invalidate all cached data
-	///
-	/// Invalidates all cached bitmaps for another reason, usually as a signal that
-	/// implementation-defined data in the bitmap provider have been changed.
-	///
-	/// If the consumer of audio rendering changes properties of the bitmap renderer
-	/// that will affect the rendered images, it should call this function to ensure
-	/// the cache is kept consistent.
-	void Invalidate();
+    /// @brief Constructor
+    ///
+    /// Initialises audio rendering to a do-nothing state. An audio provider
+    /// and bitmap provider must be set before the audio renderer is functional.
+    AudioRenderer();
+
+    /// @brief Set horizontal zoom
+    /// @param pixel_ms Milliseconds per pixel to render audio at
+    ///
+    /// Changing the zoom level invalidates all cached bitmaps.
+    void SetMillisecondsPerPixel(double pixel_ms);
+
+    /// @brief Set rendering height
+    /// @param pixel_height Height in pixels to render at
+    ///
+    /// Changing the rendering height invalidates all cached bitmaps.
+    void SetHeight(int pixel_height);
+
+    /// @brief Set vertical zoom
+    /// @param amplitude_scale Scaling factor
+    ///
+    /// Changing the scaling factor invalidates all cached bitmaps.
+    ///
+    /// A scaling factor of 1.0 is no scaling, a factor of 0.5 causes the audio to be
+    /// rendered as if it had half its actual amplitude, a factor of 2 causes the audio
+    /// to be rendered as if it had double amplitude. (The exact meaning of the scaling
+    /// depends on the bitmap provider used.)
+    void SetAmplitudeScale(float amplitude_scale);
+
+    /// @brief Set the maximum allowed cache size
+    /// @param max_size Size in bytes that may be used for caching
+    ///
+    /// The given max size is not a hard limit and does generally not include overhead
+    /// added by the cache management. The allowed size might be distributed among
+    /// several separate objects.
+    ///
+    /// Changing the max cache size does not trigger cache aging.
+    void SetCacheMaxSize(size_t max_size);
+
+    /// @brief Change renderer
+    /// @param renderer New renderer to use
+    ///
+    /// The consumer of audio rendering is responsible for creating, managing and destroying
+    /// audio bitmap providers (renderers). If a renderer was previously set with this function
+    /// and a new one is set, the consumer of audio rendering is still responsible for the
+    /// life of the old renderer.
+    ///
+    /// A bitmap provider must be assigned to a newly created audio renderer before it
+    /// can be functional.
+    ///
+    /// Changing renderer invalidates all cached bitmaps.
+    void SetRenderer(AudioRendererBitmapProvider *renderer);
+
+    /// @brief Change audio provider
+    /// @param provider New audio provider to use
+    ///
+    /// The consumer of audio rendering is responsible for managing audio providers.
+    /// If an audio provider was previously assigned to the audio renderer and a
+    /// new one is assigned, the consumer of audio rendering is still responsible for
+    /// the life of the old audio provider.
+    ///
+    /// An audio provider must be assigned to a newly created audio renderer before it
+    /// can be functional.
+    ///
+    /// Changing audio provider invalidates all cached bitmaps.
+    ///
+    /// If a renderer is set, this will also set the audio provider for the renderer.
+    void SetAudioProvider(agi::AudioProvider *provider);
+
+    /// @brief Render audio to a device context
+    /// @param dc       The device context to draw to
+    /// @param origin   Top left corner to render at, in the DC's coordinates
+    /// @param start    First pixel from beginning of the audio stream to render
+    /// @param length   Number of pixels of audio to render
+    /// @param style    Style to render audio in
+    ///
+    /// The first audio sample rendered is start*pixel_samples, and the number
+    /// of audio samples rendered is length*pixel_samples.
+    void Render(wxDC &dc, wxPoint origin, int start, int length, AudioRenderingStyle style);
+
+    /// @brief Invalidate all cached data
+    ///
+    /// Invalidates all cached bitmaps for another reason, usually as a signal that
+    /// implementation-defined data in the bitmap provider have been changed.
+    ///
+    /// If the consumer of audio rendering changes properties of the bitmap renderer
+    /// that will affect the rendered images, it should call this function to ensure
+    /// the cache is kept consistent.
+    void Invalidate();
 };
 
 
@@ -222,69 +222,69 @@ public:
 /// Derive from this class to implement a way to render audio to images.
 class AudioRendererBitmapProvider {
 protected:
-	/// Audio provider to use for rendering
-	agi::AudioProvider *provider;
-	/// Horizontal zoom in milliseconds per pixel
-	double pixel_ms;
-	/// Vertical zoom/amplitude scale factor
-	float amplitude_scale;
-
-	/// @brief Called when the audio provider changes
-	///
-	/// Implementations can override this method to do something when the audio provider is changed
-	virtual void OnSetProvider() { }
-
-	/// @brief Called when horizontal zoom changes
-	///
-	/// Implementations can override this method to do something when the horizontal zoom is changed
-	virtual void OnSetMillisecondsPerPixel() { }
-
-	/// @brief Called when vertical zoom changes
-	///
-	/// Implementations can override this method to do something when the vertical zoom is changed
-	virtual void OnSetAmplitudeScale() { }
+    /// Audio provider to use for rendering
+    agi::AudioProvider *provider;
+    /// Horizontal zoom in milliseconds per pixel
+    double pixel_ms;
+    /// Vertical zoom/amplitude scale factor
+    float amplitude_scale;
+
+    /// @brief Called when the audio provider changes
+    ///
+    /// Implementations can override this method to do something when the audio provider is changed
+    virtual void OnSetProvider() { }
+
+    /// @brief Called when horizontal zoom changes
+    ///
+    /// Implementations can override this method to do something when the horizontal zoom is changed
+    virtual void OnSetMillisecondsPerPixel() { }
+
+    /// @brief Called when vertical zoom changes
+    ///
+    /// Implementations can override this method to do something when the vertical zoom is changed
+    virtual void OnSetAmplitudeScale() { }
 
 public:
-	/// @brief Constructor
-	AudioRendererBitmapProvider() : provider(nullptr), pixel_ms(0), amplitude_scale(0) { };
-
-	/// @brief Destructor
-	virtual ~AudioRendererBitmapProvider() = default;
-
-	/// @brief Rendering function
-	/// @param bmp   Bitmap to render to
-	/// @param start First pixel from beginning of the audio stream to render
-	/// @param style Style to render audio in
-	///
-	/// Deriving classes must implement this method. The bitmap in bmp holds
-	/// the width and height to render.
-	virtual void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) = 0;
-
-	/// @brief Blank audio rendering function
-	/// @param dc    The device context to render to
-	/// @param rect  The rectangle to fill with the image of blank audio
-	/// @param style Style to render audio in
-	///
-	/// Deriving classes must implement this method. The rectangle has the height
-	/// of the entire canvas the audio is being rendered in.
-	virtual void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) = 0;
-
-	/// @brief Change audio provider
-	/// @param provider Audio provider to change to
-	void SetProvider(agi::AudioProvider *provider);
-
-	/// @brief Change horizontal zoom
-	/// @param pixel_ms Milliseconds per pixel to zoom to
-	void SetMillisecondsPerPixel(double new_pixel_ms);
-
-	/// @brief Change vertical zoom
-	/// @param amplitude_scale Scaling factor to zoom to
-	void SetAmplitudeScale(float amplitude_scale);
-
-	/// @brief Age any caches the renderer might keep
-	/// @param max_size Maximum size in bytes the caches should be
-	///
-	/// Deriving classes should override this method if they implement any
-	/// kind of caching.
-	virtual void AgeCache(size_t max_size) { }
+    /// @brief Constructor
+    AudioRendererBitmapProvider() : provider(nullptr), pixel_ms(0), amplitude_scale(0) { };
+
+    /// @brief Destructor
+    virtual ~AudioRendererBitmapProvider() = default;
+
+    /// @brief Rendering function
+    /// @param bmp   Bitmap to render to
+    /// @param start First pixel from beginning of the audio stream to render
+    /// @param style Style to render audio in
+    ///
+    /// Deriving classes must implement this method. The bitmap in bmp holds
+    /// the width and height to render.
+    virtual void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) = 0;
+
+    /// @brief Blank audio rendering function
+    /// @param dc    The device context to render to
+    /// @param rect  The rectangle to fill with the image of blank audio
+    /// @param style Style to render audio in
+    ///
+    /// Deriving classes must implement this method. The rectangle has the height
+    /// of the entire canvas the audio is being rendered in.
+    virtual void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) = 0;
+
+    /// @brief Change audio provider
+    /// @param provider Audio provider to change to
+    void SetProvider(agi::AudioProvider *provider);
+
+    /// @brief Change horizontal zoom
+    /// @param pixel_ms Milliseconds per pixel to zoom to
+    void SetMillisecondsPerPixel(double new_pixel_ms);
+
+    /// @brief Change vertical zoom
+    /// @param amplitude_scale Scaling factor to zoom to
+    void SetAmplitudeScale(float amplitude_scale);
+
+    /// @brief Age any caches the renderer might keep
+    /// @param max_size Maximum size in bytes the caches should be
+    ///
+    /// Deriving classes should override this method if they implement any
+    /// kind of caching.
+    virtual void AgeCache(size_t max_size) { }
 };
diff --git a/src/audio_renderer_spectrum.cpp b/src/audio_renderer_spectrum.cpp
index 7884f10067ba4db87aa25744137ecd68f047f66d..c196262de4e54854659cdcff88d46c51f8e87bc8 100644
--- a/src/audio_renderer_spectrum.cpp
+++ b/src/audio_renderer_spectrum.cpp
@@ -49,251 +49,237 @@
 
 /// Allocates blocks of derived data for the audio spectrum
 struct AudioSpectrumCacheBlockFactory {
-	typedef std::unique_ptr<float, std::default_delete<float[]>> BlockType;
-
-	/// Pointer back to the owning spectrum renderer
-	AudioSpectrumRenderer *spectrum;
-
-	/// @brief Allocate and fill a data block
-	/// @param i Index of the block to produce data for
-	/// @return Newly allocated and filled block
-	///
-	/// The filling is delegated to the spectrum renderer
-	BlockType ProduceBlock(size_t i)
-	{
-		auto res = new float[((size_t)1)<<spectrum->derivation_size];
-		spectrum->FillBlock(i, res);
-		return BlockType(res);
-	}
-
-	/// @brief Calculate the in-memory size of a spec
-	/// @return The size in bytes of a spectrum cache block
-	size_t GetBlockSize() const
-	{
-		return sizeof(float) << spectrum->derivation_size;
-	}
+    typedef std::unique_ptr<float, std::default_delete<float[]>> BlockType;
+
+    /// Pointer back to the owning spectrum renderer
+    AudioSpectrumRenderer *spectrum;
+
+    /// @brief Allocate and fill a data block
+    /// @param i Index of the block to produce data for
+    /// @return Newly allocated and filled block
+    ///
+    /// The filling is delegated to the spectrum renderer
+    BlockType ProduceBlock(size_t i) {
+        auto res = new float[((size_t)1) << spectrum->derivation_size];
+        spectrum->FillBlock(i, res);
+        return BlockType(res);
+    }
+
+    /// @brief Calculate the in-memory size of a spec
+    /// @return The size in bytes of a spectrum cache block
+    size_t GetBlockSize() const {
+        return sizeof(float) << spectrum->derivation_size;
+    }
 };
 
 /// @brief Cache for audio spectrum frequency-power data
 class AudioSpectrumCache
-: public DataBlockCache<float, 10, AudioSpectrumCacheBlockFactory> {
+    : public DataBlockCache<float, 10, AudioSpectrumCacheBlockFactory> {
 public:
-	AudioSpectrumCache(size_t block_count, AudioSpectrumRenderer *renderer)
-	: DataBlockCache(block_count, AudioSpectrumCacheBlockFactory{renderer})
-	{
-	}
+    AudioSpectrumCache(size_t block_count, AudioSpectrumRenderer *renderer)
+        : DataBlockCache(block_count, AudioSpectrumCacheBlockFactory{renderer}) {
+    }
 };
 
-AudioSpectrumRenderer::AudioSpectrumRenderer(std::string const& color_scheme_name)
+AudioSpectrumRenderer::AudioSpectrumRenderer(std::string const &color_scheme_name)
 {
-	colors.reserve(AudioStyle_MAX);
-	for (int i = 0; i < AudioStyle_MAX; ++i)
-		colors.emplace_back(12, color_scheme_name, i);
+    colors.reserve(AudioStyle_MAX);
+    for (int i = 0; i < AudioStyle_MAX; ++i)
+        colors.emplace_back(12, color_scheme_name, i);
 }
 
 AudioSpectrumRenderer::~AudioSpectrumRenderer()
 {
-	// This sequence will clean up
-	provider = nullptr;
-	RecreateCache();
+    // This sequence will clean up
+    provider = nullptr;
+    RecreateCache();
 }
 
 void AudioSpectrumRenderer::RecreateCache()
 {
 #ifdef WITH_FFTW3
-	if (dft_plan)
-	{
-		fftw_destroy_plan(dft_plan);
-		fftw_free(dft_input);
-		fftw_free(dft_output);
-		dft_plan = nullptr;
-		dft_input = nullptr;
-		dft_output = nullptr;
-	}
+    if (dft_plan) {
+        fftw_destroy_plan(dft_plan);
+        fftw_free(dft_input);
+        fftw_free(dft_output);
+        dft_plan = nullptr;
+        dft_input = nullptr;
+        dft_output = nullptr;
+    }
 #endif
 
-	if (provider)
-	{
-		size_t block_count = (size_t)((provider->GetNumSamples() + (size_t)(1<<derivation_dist) - 1) >> derivation_dist);
-		cache = agi::make_unique<AudioSpectrumCache>(block_count, this);
+    if (provider) {
+        size_t block_count = (size_t)((provider->GetNumSamples() + (size_t)(1 << derivation_dist) - 1) >> derivation_dist);
+        cache = agi::make_unique<AudioSpectrumCache>(block_count, this);
 
 #ifdef WITH_FFTW3
-		dft_input = fftw_alloc_real(2<<derivation_size);
-		dft_output = fftw_alloc_complex(2<<derivation_size);
-		dft_plan = fftw_plan_dft_r2c_1d(
-			2<<derivation_size,
-			dft_input,
-			dft_output,
-			FFTW_MEASURE);
+        dft_input = fftw_alloc_real(2 << derivation_size);
+        dft_output = fftw_alloc_complex(2 << derivation_size);
+        dft_plan = fftw_plan_dft_r2c_1d(
+                       2 << derivation_size,
+                       dft_input,
+                       dft_output,
+                       FFTW_MEASURE);
 #else
-		// Allocate scratch for 6x the derivation size:
-		// 2x for the input sample data
-		// 2x for the real part of the output
-		// 2x for the imaginary part of the output
-		fft_scratch.resize(6 << derivation_size);
+        // Allocate scratch for 6x the derivation size:
+        // 2x for the input sample data
+        // 2x for the real part of the output
+        // 2x for the imaginary part of the output
+        fft_scratch.resize(6 << derivation_size);
 #endif
-		audio_scratch.resize(2 << derivation_size);
-	}
+        audio_scratch.resize(2 << derivation_size);
+    }
 }
 
 void AudioSpectrumRenderer::OnSetProvider()
 {
-	RecreateCache();
+    RecreateCache();
 }
 
 void AudioSpectrumRenderer::SetResolution(size_t _derivation_size, size_t _derivation_dist)
 {
-	if (derivation_dist != _derivation_dist)
-	{
-		derivation_dist = _derivation_dist;
-		if (cache)
-			cache->Age(0);
-	}
-
-	if (derivation_size != _derivation_size)
-	{
-		derivation_size = _derivation_size;
-		RecreateCache();
-	}
+    if (derivation_dist != _derivation_dist) {
+        derivation_dist = _derivation_dist;
+        if (cache)
+            cache->Age(0);
+    }
+
+    if (derivation_size != _derivation_size) {
+        derivation_size = _derivation_size;
+        RecreateCache();
+    }
 }
 
 template<class T>
-void AudioSpectrumRenderer::ConvertToFloat(size_t count, T *dest) {
-	for (size_t si = 0; si < count; ++si)
-	{
-		dest[si] = (T)(audio_scratch[si]) / 32768.0;
-	}
+void AudioSpectrumRenderer::ConvertToFloat(size_t count, T *dest)
+{
+    for (size_t si = 0; si < count; ++si) {
+        dest[si] = (T)(audio_scratch[si]) / 32768.0;
+    }
 }
 
 void AudioSpectrumRenderer::FillBlock(size_t block_index, float *block)
 {
-	assert(cache);
-	assert(block);
+    assert(cache);
+    assert(block);
 
-	int64_t first_sample = ((int64_t)block_index) << derivation_dist;
-	provider->GetAudio(&audio_scratch[0], first_sample, 2 << derivation_size);
+    int64_t first_sample = ((int64_t)block_index) << derivation_dist;
+    provider->GetAudio(&audio_scratch[0], first_sample, 2 << derivation_size);
 
 #ifdef WITH_FFTW3
-	ConvertToFloat(2 << derivation_size, dft_input);
+    ConvertToFloat(2 << derivation_size, dft_input);
 
-	fftw_execute(dft_plan);
+    fftw_execute(dft_plan);
 
-	double scale_factor = 9 / sqrt(2 << (derivation_size + 1));
+    double scale_factor = 9 / sqrt(2 << (derivation_size + 1));
 
-	fftw_complex *o = dft_output;
-	for (size_t si = 1<<derivation_size; si > 0; --si)
-	{
-		*block++ = log10( sqrt(o[0][0] * o[0][0] + o[0][1] * o[0][1]) * scale_factor + 1 );
-		o++;
-	}
+    fftw_complex *o = dft_output;
+    for (size_t si = 1 << derivation_size; si > 0; --si) {
+        *block++ = log10( sqrt(o[0][0] * o[0][0] + o[0][1] * o[0][1]) * scale_factor + 1 );
+        o++;
+    }
 #else
-	ConvertToFloat(2 << derivation_size, &fft_scratch[0]);
+    ConvertToFloat(2 << derivation_size, &fft_scratch[0]);
 
-	float *fft_input = &fft_scratch[0];
-	float *fft_real = &fft_scratch[0] + (2 << derivation_size);
-	float *fft_imag = &fft_scratch[0] + (4 << derivation_size);
+    float *fft_input = &fft_scratch[0];
+    float *fft_real = &fft_scratch[0] + (2 << derivation_size);
+    float *fft_imag = &fft_scratch[0] + (4 << derivation_size);
 
-	FFT fft;
-	fft.Transform(2<<derivation_size, fft_input, fft_real, fft_imag);
+    FFT fft;
+    fft.Transform(2 << derivation_size, fft_input, fft_real, fft_imag);
 
-	float scale_factor = 9 / sqrt(2 * (float)(2<<derivation_size));
+    float scale_factor = 9 / sqrt(2 * (float)(2 << derivation_size));
 
-	for (size_t si = 1<<derivation_size; si > 0; --si)
-	{
-		// With x in range [0;1], log10(x*9+1) will also be in range [0;1],
-		// although the FFT output can apparently get greater magnitudes than 1
-		// despite the input being limited to [-1;+1).
-		*block++ = log10( sqrt(*fft_real * *fft_real + *fft_imag * *fft_imag) * scale_factor + 1 );
-		fft_real++; fft_imag++;
-	}
+    for (size_t si = 1 << derivation_size; si > 0; --si) {
+        // With x in range [0;1], log10(x*9+1) will also be in range [0;1],
+        // although the FFT output can apparently get greater magnitudes than 1
+        // despite the input being limited to [-1;+1).
+        *block++ = log10( sqrt(*fft_real * *fft_real + *fft_imag * *fft_imag) * scale_factor + 1 );
+        fft_real++; fft_imag++;
+    }
 #endif
 }
 
 void AudioSpectrumRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle style)
 {
-	if (!cache)
-		return;
-
-	assert(bmp.IsOk());
-	assert(bmp.GetDepth() == 24);
-
-	int end = start + bmp.GetWidth();
-
-	assert(start >= 0);
-	assert(end >= 0);
-	assert(end >= start);
-
-	// Prepare an image buffer to write
-	wxImage img(bmp.GetSize());
-	unsigned char *imgdata = img.GetData();
-	ptrdiff_t stride = img.GetWidth()*3;
-	int imgheight = img.GetHeight();
-
-	const AudioColorScheme *pal = &colors[style];
-
-	/// @todo Make minband and maxband configurable
-	int minband = 0;
-	int maxband = 1 << derivation_size;
-
-	// ax = absolute x, absolute to the virtual spectrum bitmap
-	for (int ax = start; ax < end; ++ax)
-	{
-		// Derived audio data
-		size_t block_index = (size_t)(ax * pixel_ms * provider->GetSampleRate() / 1000) >> derivation_dist;
-		float *power = &cache->Get(block_index);
-
-		// Prepare bitmap writing
-		unsigned char *px = imgdata + (imgheight-1) * stride + (ax - start) * 3;
-
-		// Scale up or down vertically?
-		if (imgheight > 1<<derivation_size)
-		{
-			// Interpolate
-			for (int y = 0; y < imgheight; ++y)
-			{
-				assert(px >= imgdata);
-				assert(px < imgdata + imgheight*stride);
-				auto ideal = (double)(y+1.)/imgheight * (maxband-minband) + minband;
-				float sample1 = power[(int)floor(ideal)+minband];
-				float sample2 = power[(int)ceil(ideal)+minband];
-				float frac = ideal - floor(ideal);
-				float val = (1-frac)*sample1 + frac*sample2;
-				pal->map(val*amplitude_scale, px);
-				px -= stride;
-			}
-		}
-		else
-		{
-			// Pick greatest
-			for (int y = 0; y < imgheight; ++y)
-			{
-				assert(px >= imgdata);
-				assert(px < imgdata + imgheight*stride);
-				int sample1 = std::max(0, maxband * y/imgheight + minband);
-				int sample2 = std::min((1<<derivation_size)-1, maxband * (y+1)/imgheight + minband);
-				float maxval = *std::max_element(&power[sample1], &power[sample2 + 1]);
-				pal->map(maxval*amplitude_scale, px);
-				px -= stride;
-			}
-		}
-	}
-
-	wxBitmap tmpbmp(img);
-	wxMemoryDC targetdc(bmp);
-	targetdc.DrawBitmap(tmpbmp, 0, 0);
+    if (!cache)
+        return;
+
+    assert(bmp.IsOk());
+    assert(bmp.GetDepth() == 24);
+
+    int end = start + bmp.GetWidth();
+
+    assert(start >= 0);
+    assert(end >= 0);
+    assert(end >= start);
+
+    // Prepare an image buffer to write
+    wxImage img(bmp.GetSize());
+    unsigned char *imgdata = img.GetData();
+    ptrdiff_t stride = img.GetWidth() * 3;
+    int imgheight = img.GetHeight();
+
+    const AudioColorScheme *pal = &colors[style];
+
+    /// @todo Make minband and maxband configurable
+    int minband = 0;
+    int maxband = 1 << derivation_size;
+
+    // ax = absolute x, absolute to the virtual spectrum bitmap
+    for (int ax = start; ax < end; ++ax) {
+        // Derived audio data
+        size_t block_index = (size_t)(ax * pixel_ms * provider->GetSampleRate() / 1000) >> derivation_dist;
+        float *power = &cache->Get(block_index);
+
+        // Prepare bitmap writing
+        unsigned char *px = imgdata + (imgheight - 1) * stride + (ax - start) * 3;
+
+        // Scale up or down vertically?
+        if (imgheight > 1 << derivation_size) {
+            // Interpolate
+            for (int y = 0; y < imgheight; ++y) {
+                assert(px >= imgdata);
+                assert(px < imgdata + imgheight * stride);
+                auto ideal = (double)(y + 1.) / imgheight * (maxband - minband) + minband;
+                float sample1 = power[(int)floor(ideal) + minband];
+                float sample2 = power[(int)ceil(ideal) + minband];
+                float frac = ideal - floor(ideal);
+                float val = (1 - frac) * sample1 + frac * sample2;
+                pal->map(val * amplitude_scale, px);
+                px -= stride;
+            }
+        }
+        else {
+            // Pick greatest
+            for (int y = 0; y < imgheight; ++y) {
+                assert(px >= imgdata);
+                assert(px < imgdata + imgheight * stride);
+                int sample1 = std::max(0, maxband * y / imgheight + minband);
+                int sample2 = std::min((1 << derivation_size) - 1, maxband * (y + 1) / imgheight + minband);
+                float maxval = *std::max_element(&power[sample1], &power[sample2 + 1]);
+                pal->map(maxval * amplitude_scale, px);
+                px -= stride;
+            }
+        }
+    }
+
+    wxBitmap tmpbmp(img);
+    wxMemoryDC targetdc(bmp);
+    targetdc.DrawBitmap(tmpbmp, 0, 0);
 }
 
 void AudioSpectrumRenderer::RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style)
 {
-	// Get the colour of silence
-	wxColour col = colors[style].get(0.0f);
-	dc.SetBrush(wxBrush(col));
-	dc.SetPen(wxPen(col));
-	dc.DrawRectangle(rect);
+    // Get the colour of silence
+    wxColour col = colors[style].get(0.0f);
+    dc.SetBrush(wxBrush(col));
+    dc.SetPen(wxPen(col));
+    dc.DrawRectangle(rect);
 }
 
 void AudioSpectrumRenderer::AgeCache(size_t max_size)
 {
-	if (cache)
-		cache->Age(max_size);
+    if (cache)
+        cache->Age(max_size);
 }
diff --git a/src/audio_renderer_spectrum.h b/src/audio_renderer_spectrum.h
index d4641f37de329d59e1c04d18b2b8f280e04dc810..a08370220da783cd2146447c8d170e46502128a0 100644
--- a/src/audio_renderer_spectrum.h
+++ b/src/audio_renderer_spectrum.h
@@ -53,87 +53,87 @@ struct AudioSpectrumCacheBlockFactory;
 /// Renders frequency-power spectrum graphs of PCM audio data using a derivation function
 /// such as the fast fourier transform.
 class AudioSpectrumRenderer final : public AudioRendererBitmapProvider {
-	friend struct AudioSpectrumCacheBlockFactory;
+    friend struct AudioSpectrumCacheBlockFactory;
 
-	/// Internal cache management for the spectrum
-	std::unique_ptr<AudioSpectrumCache> cache;
+    /// Internal cache management for the spectrum
+    std::unique_ptr<AudioSpectrumCache> cache;
 
-	/// Colour tables used for rendering
-	std::vector<AudioColorScheme> colors;
+    /// Colour tables used for rendering
+    std::vector<AudioColorScheme> colors;
 
-	/// Binary logarithm of number of samples to use in deriving frequency-power data
-	size_t derivation_size = 0;
+    /// Binary logarithm of number of samples to use in deriving frequency-power data
+    size_t derivation_size = 0;
 
-	/// Binary logarithm of number of samples between the start of derivations
-	size_t derivation_dist = 0;
+    /// Binary logarithm of number of samples between the start of derivations
+    size_t derivation_dist = 0;
 
-	/// @brief Reset in response to changing audio provider
-	///
-	/// Overrides the OnSetProvider event handler in the base class, to reset things
-	/// when the audio provider is changed.
-	void OnSetProvider() override;
+    /// @brief Reset in response to changing audio provider
+    ///
+    /// Overrides the OnSetProvider event handler in the base class, to reset things
+    /// when the audio provider is changed.
+    void OnSetProvider() override;
 
-	/// @brief Recreates the cache
-	///
-	/// To be called when the number of blocks in cache might have changed,
-	/// e.g. new audio provider or new resolution.
-	void RecreateCache();
+    /// @brief Recreates the cache
+    ///
+    /// To be called when the number of blocks in cache might have changed,
+    /// e.g. new audio provider or new resolution.
+    void RecreateCache();
 
-	/// @brief Fill a block with frequency-power data for a time range
-	/// @param      block_index Index of the block to fill data for
-	/// @param[out] block       Address to write the data to
-	void FillBlock(size_t block_index, float *block);
+    /// @brief Fill a block with frequency-power data for a time range
+    /// @param      block_index Index of the block to fill data for
+    /// @param[out] block       Address to write the data to
+    void FillBlock(size_t block_index, float *block);
 
-	/// @brief Convert audio data to float range [-1;+1)
-	/// @param count Samples to convert
-	/// @param dest Buffer to fill
-	template<class T>
-	void ConvertToFloat(size_t count, T *dest);
+    /// @brief Convert audio data to float range [-1;+1)
+    /// @param count Samples to convert
+    /// @param dest Buffer to fill
+    template<class T>
+    void ConvertToFloat(size_t count, T *dest);
 
 #ifdef WITH_FFTW3
-	/// FFTW plan data
-	fftw_plan dft_plan = nullptr;
-	/// Pre-allocated input array for FFTW
-	double *dft_input = nullptr;
-	/// Pre-allocated output array for FFTW
-	fftw_complex *dft_output = nullptr;
+    /// FFTW plan data
+    fftw_plan dft_plan = nullptr;
+    /// Pre-allocated input array for FFTW
+    double *dft_input = nullptr;
+    /// Pre-allocated output array for FFTW
+    fftw_complex *dft_output = nullptr;
 #else
-	/// Pre-allocated scratch area for doing FFT derivations
-	std::vector<float> fft_scratch;
+    /// Pre-allocated scratch area for doing FFT derivations
+    std::vector<float> fft_scratch;
 #endif
 
-	/// Pre-allocated scratch area for storing raw audio data
-	std::vector<int16_t> audio_scratch;
+    /// Pre-allocated scratch area for storing raw audio data
+    std::vector<int16_t> audio_scratch;
 
 public:
-	/// @brief Constructor
-	/// @param color_scheme_name Name of the color scheme to use
-	AudioSpectrumRenderer(std::string const& color_scheme_name);
-
-	/// @brief Destructor
-	~AudioSpectrumRenderer();
-
-	/// @brief Render a range of audio spectrum
-	/// @param bmp   [in,out] Bitmap to render into, also carries length information
-	/// @param start First column of pixel data in display to render
-	/// @param style Style to render audio in
-	void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) override;
-
-	/// @brief Render blank area
-	void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) override;
-
-	/// @brief Set the derivation resolution
-	/// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data
-	/// @param derivation_dist Binary logarithm of number of samples between the start of derivations
-	///
-	/// The derivations done will each use 2^derivation_size audio samples and at a distance
-	/// of 2^derivation_dist samples.
-	///
-	/// The derivation distance must be smaller than or equal to the size. If the distance
-	/// is specified too large, it will be clamped to the size.
-	void SetResolution(size_t derivation_size, size_t derivation_dist);
-
-	/// @brief Cleans up the cache
-	/// @param max_size Maximum size in bytes for the cache
-	void AgeCache(size_t max_size) override;
+    /// @brief Constructor
+    /// @param color_scheme_name Name of the color scheme to use
+    AudioSpectrumRenderer(std::string const &color_scheme_name);
+
+    /// @brief Destructor
+    ~AudioSpectrumRenderer();
+
+    /// @brief Render a range of audio spectrum
+    /// @param bmp   [in,out] Bitmap to render into, also carries length information
+    /// @param start First column of pixel data in display to render
+    /// @param style Style to render audio in
+    void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) override;
+
+    /// @brief Render blank area
+    void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) override;
+
+    /// @brief Set the derivation resolution
+    /// @param derivation_size Binary logarithm of number of samples to use in deriving frequency-power data
+    /// @param derivation_dist Binary logarithm of number of samples between the start of derivations
+    ///
+    /// The derivations done will each use 2^derivation_size audio samples and at a distance
+    /// of 2^derivation_dist samples.
+    ///
+    /// The derivation distance must be smaller than or equal to the size. If the distance
+    /// is specified too large, it will be clamped to the size.
+    void SetResolution(size_t derivation_size, size_t derivation_dist);
+
+    /// @brief Cleans up the cache
+    /// @param max_size Maximum size in bytes for the cache
+    void AgeCache(size_t max_size) override;
 };
diff --git a/src/audio_renderer_waveform.cpp b/src/audio_renderer_waveform.cpp
index d5bb802fbd91a7e969ed1965f4a1b6afa01c6faa..5f57bf978b8bd3ff3fc8dca1feacf4c5f444fcd6 100644
--- a/src/audio_renderer_waveform.cpp
+++ b/src/audio_renderer_waveform.cpp
@@ -38,121 +38,117 @@
 #include <wx/dcmemory.h>
 
 enum {
-	/// Only render the peaks
-	Waveform_MaxOnly = 0,
-	/// Render the peaks and averages
-	Waveform_MaxAvg,
-	Waveform_Continuous
+    /// Only render the peaks
+    Waveform_MaxOnly = 0,
+    /// Render the peaks and averages
+    Waveform_MaxAvg,
+    Waveform_Continuous
 };
 
-AudioWaveformRenderer::AudioWaveformRenderer(std::string const& color_scheme_name)
-: render_averages(OPT_GET("Audio/Display/Waveform Style")->GetInt() == Waveform_MaxAvg)
+AudioWaveformRenderer::AudioWaveformRenderer(std::string const &color_scheme_name)
+    : render_averages(OPT_GET("Audio/Display/Waveform Style")->GetInt() == Waveform_MaxAvg)
 {
-	colors.reserve(AudioStyle_MAX);
-	for (int i = 0; i < AudioStyle_MAX; ++i)
-		colors.emplace_back(6, color_scheme_name, i);
+    colors.reserve(AudioStyle_MAX);
+    for (int i = 0; i < AudioStyle_MAX; ++i)
+        colors.emplace_back(6, color_scheme_name, i);
 }
 
 AudioWaveformRenderer::~AudioWaveformRenderer() { }
 
 void AudioWaveformRenderer::Render(wxBitmap &bmp, int start, AudioRenderingStyle style)
 {
-	wxMemoryDC dc(bmp);
-	wxRect rect(wxPoint(0, 0), bmp.GetSize());
-	int midpoint = rect.height / 2;
-
-	const AudioColorScheme *pal = &colors[style];
-
-	double pixel_samples = pixel_ms * provider->GetSampleRate() / 1000.0;
-
-	// Fill the background
-	dc.SetBrush(wxBrush(pal->get(0.0f)));
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.DrawRectangle(rect);
-
-	// Make sure we've got a buffer to fill with audio data
-	if (!audio_buffer)
-	{
-		// Buffer for one pixel strip of audio
-		size_t buffer_needed = pixel_samples * provider->GetChannels() * provider->GetBytesPerSample();
-		audio_buffer.reset(new char[buffer_needed]);
-	}
-
-	double cur_sample = start * pixel_samples;
-
-	assert(provider->GetBytesPerSample() == 2);
-	assert(provider->GetChannels() == 1);
-
-	wxPen pen_peaks(wxPen(pal->get(0.4f)));
-	wxPen pen_avgs(wxPen(pal->get(0.7f)));
-
-	for (int x = 0; x < rect.width; ++x)
-	{
-		provider->GetAudio(audio_buffer.get(), (int64_t)cur_sample, (int64_t)pixel_samples);
-		cur_sample += pixel_samples;
-
-		int peak_min = 0, peak_max = 0;
-		int64_t avg_min_accum = 0, avg_max_accum = 0;
-		auto aud = reinterpret_cast<const int16_t *>(audio_buffer.get());
-		for (int si = pixel_samples; si > 0; --si, ++aud)
-		{
-			if (*aud > 0)
-			{
-				peak_max = std::max(peak_max, (int)*aud);
-				avg_max_accum += *aud;
-			}
-			else
-			{
-				peak_min = std::min(peak_min, (int)*aud);
-				avg_min_accum += *aud;
-			}
-		}
-
-		// midpoint is half height
-		peak_min = std::max((int)(peak_min * amplitude_scale * midpoint) / 0x8000, -midpoint);
-		peak_max = std::min((int)(peak_max * amplitude_scale * midpoint) / 0x8000, midpoint);
-		int avg_min = std::max((int)(avg_min_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, -midpoint);
-		int avg_max = std::min((int)(avg_max_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, midpoint);
-
-		dc.SetPen(pen_peaks);
-		dc.DrawLine(x, midpoint - peak_max, x, midpoint - peak_min);
-		if (render_averages) {
-			dc.SetPen(pen_avgs);
-			dc.DrawLine(x, midpoint - avg_max, x, midpoint - avg_min);
-		}
-	}
-
-	// Horizontal zero-point line
-	if (render_averages)
-		dc.SetPen(wxPen(pal->get(1.0f)));
-	else
-		dc.SetPen(pen_peaks);
-
-	dc.DrawLine(0, midpoint, rect.width, midpoint);
+    wxMemoryDC dc(bmp);
+    wxRect rect(wxPoint(0, 0), bmp.GetSize());
+    int midpoint = rect.height / 2;
+
+    const AudioColorScheme *pal = &colors[style];
+
+    double pixel_samples = pixel_ms * provider->GetSampleRate() / 1000.0;
+
+    // Fill the background
+    dc.SetBrush(wxBrush(pal->get(0.0f)));
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.DrawRectangle(rect);
+
+    // Make sure we've got a buffer to fill with audio data
+    if (!audio_buffer) {
+        // Buffer for one pixel strip of audio
+        size_t buffer_needed = pixel_samples * provider->GetChannels() * provider->GetBytesPerSample();
+        audio_buffer.reset(new char[buffer_needed]);
+    }
+
+    double cur_sample = start * pixel_samples;
+
+    assert(provider->GetBytesPerSample() == 2);
+    assert(provider->GetChannels() == 1);
+
+    wxPen pen_peaks(wxPen(pal->get(0.4f)));
+    wxPen pen_avgs(wxPen(pal->get(0.7f)));
+
+    for (int x = 0; x < rect.width; ++x) {
+        provider->GetAudio(audio_buffer.get(), (int64_t)cur_sample, (int64_t)pixel_samples);
+        cur_sample += pixel_samples;
+
+        int peak_min = 0, peak_max = 0;
+        int64_t avg_min_accum = 0, avg_max_accum = 0;
+        auto aud = reinterpret_cast<const int16_t *>(audio_buffer.get());
+        for (int si = pixel_samples; si > 0; --si, ++aud) {
+            if (*aud > 0) {
+                peak_max = std::max(peak_max, (int) * aud);
+                avg_max_accum += *aud;
+            }
+            else {
+                peak_min = std::min(peak_min, (int) * aud);
+                avg_min_accum += *aud;
+            }
+        }
+
+        // midpoint is half height
+        peak_min = std::max((int)(peak_min * amplitude_scale * midpoint) / 0x8000, -midpoint);
+        peak_max = std::min((int)(peak_max * amplitude_scale * midpoint) / 0x8000, midpoint);
+        int avg_min = std::max((int)(avg_min_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, -midpoint);
+        int avg_max = std::min((int)(avg_max_accum * amplitude_scale * midpoint / pixel_samples) / 0x8000, midpoint);
+
+        dc.SetPen(pen_peaks);
+        dc.DrawLine(x, midpoint - peak_max, x, midpoint - peak_min);
+        if (render_averages) {
+            dc.SetPen(pen_avgs);
+            dc.DrawLine(x, midpoint - avg_max, x, midpoint - avg_min);
+        }
+    }
+
+    // Horizontal zero-point line
+    if (render_averages)
+        dc.SetPen(wxPen(pal->get(1.0f)));
+    else
+        dc.SetPen(pen_peaks);
+
+    dc.DrawLine(0, midpoint, rect.width, midpoint);
 }
 
 void AudioWaveformRenderer::RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style)
 {
-	const AudioColorScheme *pal = &colors[style];
-	wxColor line(pal->get(1.0));
-	wxColor bg(pal->get(0.0));
+    const AudioColorScheme *pal = &colors[style];
+    wxColor line(pal->get(1.0));
+    wxColor bg(pal->get(0.0));
 
-	// Draw the line as background above and below, and line in the middle, to avoid
-	// overdraw flicker (the common theme in all of audio display direct drawing).
-	int halfheight = rect.height / 2;
+    // Draw the line as background above and below, and line in the middle, to avoid
+    // overdraw flicker (the common theme in all of audio display direct drawing).
+    int halfheight = rect.height / 2;
 
-	dc.SetBrush(wxBrush(bg));
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.DrawRectangle(rect.x, rect.y, rect.width, halfheight);
-	dc.DrawRectangle(rect.x, rect.y + halfheight + 1, rect.width, rect.height - halfheight - 1);
+    dc.SetBrush(wxBrush(bg));
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.DrawRectangle(rect.x, rect.y, rect.width, halfheight);
+    dc.DrawRectangle(rect.x, rect.y + halfheight + 1, rect.width, rect.height - halfheight - 1);
 
-	dc.SetPen(wxPen(line));
-	dc.DrawLine(rect.x, rect.y+halfheight, rect.x+rect.width, rect.y+halfheight);
+    dc.SetPen(wxPen(line));
+    dc.DrawLine(rect.x, rect.y + halfheight, rect.x + rect.width, rect.y + halfheight);
 }
 
-wxArrayString AudioWaveformRenderer::GetWaveformStyles() {
-	wxArrayString ret;
-	ret.push_back(_("Maximum"));
-	ret.push_back(_("Maximum + Average"));
-	return ret;
+wxArrayString AudioWaveformRenderer::GetWaveformStyles()
+{
+    wxArrayString ret;
+    ret.push_back(_("Maximum"));
+    ret.push_back(_("Maximum + Average"));
+    return ret;
 }
diff --git a/src/audio_renderer_waveform.h b/src/audio_renderer_waveform.h
index 52c88e954ed2711ab450f73c9f3fe34dbac18211..dd18264ea1eba7b0f4225fb70e12e2cb8bea281d 100644
--- a/src/audio_renderer_waveform.h
+++ b/src/audio_renderer_waveform.h
@@ -37,41 +37,41 @@ class wxArrayString;
 
 /// Render a waveform display of PCM audio data
 class AudioWaveformRenderer final : public AudioRendererBitmapProvider {
-	/// Colour tables used for rendering
-	std::vector<AudioColorScheme> colors;
+    /// Colour tables used for rendering
+    std::vector<AudioColorScheme> colors;
 
-	/// Pre-allocated buffer for audio fetched from provider
-	std::unique_ptr<char[]> audio_buffer;
+    /// Pre-allocated buffer for audio fetched from provider
+    std::unique_ptr<char[]> audio_buffer;
 
-	/// Whether to render max+avg or just max
-	bool render_averages;
+    /// Whether to render max+avg or just max
+    bool render_averages;
 
-	void OnSetProvider() override { audio_buffer.reset(); }
-	void OnSetMillisecondsPerPixel() override { audio_buffer.reset(); }
+    void OnSetProvider() override { audio_buffer.reset(); }
+    void OnSetMillisecondsPerPixel() override { audio_buffer.reset(); }
 
 public:
-	/// @brief Constructor
-	/// @param color_scheme_name Name of the color scheme to use
-	AudioWaveformRenderer(std::string const& color_scheme_name);
+    /// @brief Constructor
+    /// @param color_scheme_name Name of the color scheme to use
+    AudioWaveformRenderer(std::string const &color_scheme_name);
 
-	/// @brief Destructor
-	~AudioWaveformRenderer();
+    /// @brief Destructor
+    ~AudioWaveformRenderer();
 
-	/// @brief Render a range of audio waveform
-	/// @param bmp   [in,out] Bitmap to render into, also carries length information
-	/// @param start First column of pixel data in display to render
-	/// @param style Style to render audio in
-	void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) override;
+    /// @brief Render a range of audio waveform
+    /// @param bmp   [in,out] Bitmap to render into, also carries length information
+    /// @param start First column of pixel data in display to render
+    /// @param style Style to render audio in
+    void Render(wxBitmap &bmp, int start, AudioRenderingStyle style) override;
 
-	/// @brief Render blank area
-	void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) override;
+    /// @brief Render blank area
+    void RenderBlank(wxDC &dc, const wxRect &rect, AudioRenderingStyle style) override;
 
-	/// @brief Cleans up the cache
-	/// @param max_size Maximum size in bytes for the cache
-	///
-	/// Does nothing for waveform renderer, since it does not have a backend cache
-	void AgeCache(size_t max_size) override { }
+    /// @brief Cleans up the cache
+    /// @param max_size Maximum size in bytes for the cache
+    ///
+    /// Does nothing for waveform renderer, since it does not have a backend cache
+    void AgeCache(size_t max_size) override { }
 
-	/// Get a list of waveform rendering modes
-	static wxArrayString GetWaveformStyles();
+    /// Get a list of waveform rendering modes
+    static wxArrayString GetWaveformStyles();
 };
diff --git a/src/audio_rendering_style.h b/src/audio_rendering_style.h
index 312d98f24997108f99616285bdf09d89fdf68362..edc19f55252ea620b7f5f748806a53bf710f3b24 100644
--- a/src/audio_rendering_style.h
+++ b/src/audio_rendering_style.h
@@ -33,17 +33,17 @@
 /// Primary has highest priority and should overlap selected, which should
 /// overlap inactive, which should overlap normal regions.
 enum AudioRenderingStyle {
-	/// Regular audio with no special properties
-	AudioStyle_Normal = 0,
-	/// Audio belonging to objects that are not part of the current selection
-	AudioStyle_Inactive,
-	/// Audio belonging to objects that are part of the current selection,
-	/// but not the primary work rage
-	AudioStyle_Selected,
-	/// Primary selection for work, usually coinciding with the primary playback range
-	AudioStyle_Primary,
-	/// Number of audio styles
-	AudioStyle_MAX
+    /// Regular audio with no special properties
+    AudioStyle_Normal = 0,
+    /// Audio belonging to objects that are not part of the current selection
+    AudioStyle_Inactive,
+    /// Audio belonging to objects that are part of the current selection,
+    /// but not the primary work rage
+    AudioStyle_Selected,
+    /// Primary selection for work, usually coinciding with the primary playback range
+    AudioStyle_Primary,
+    /// Number of audio styles
+    AudioStyle_MAX
 };
 
 
@@ -54,11 +54,11 @@ enum AudioRenderingStyle {
 /// implement this interface for objects to pass to producers.
 class AudioRenderingStyleRanges {
 protected:
-	~AudioRenderingStyleRanges() { }
+    ~AudioRenderingStyleRanges() { }
 public:
-	/// @brief Add a range to the line
-	/// @param start First milisecond in range
-	/// @param end   One past last milisecond in range
-	/// @param style Style of the range added
-	virtual void AddRange(int start, int end, AudioRenderingStyle style) = 0;
+    /// @brief Add a range to the line
+    /// @param start First milisecond in range
+    /// @param end   One past last milisecond in range
+    /// @param style Style of the range added
+    virtual void AddRange(int start, int end, AudioRenderingStyle style) = 0;
 };
diff --git a/src/audio_timing.h b/src/audio_timing.h
index 00d02904a719ce01683b9da75a347ae07ca9b556..90dcb632238995252997112616d8e9a0b8569b96 100644
--- a/src/audio_timing.h
+++ b/src/audio_timing.h
@@ -51,158 +51,158 @@ namespace agi { struct Context; }
 /// clicks in empty areas of the audio display.
 class AudioTimingController : public AudioMarkerProvider, public AudioLabelProvider {
 protected:
-	/// The primary playback range has changed, usually as a result of user interaction.
-	agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
+    /// The primary playback range has changed, usually as a result of user interaction.
+    agi::signal::Signal<> AnnounceUpdatedPrimaryRange;
 
-	/// One or more rendering style ranges have changed in the timing controller.
-	agi::signal::Signal<> AnnounceUpdatedStyleRanges;
+    /// One or more rendering style ranges have changed in the timing controller.
+    agi::signal::Signal<> AnnounceUpdatedStyleRanges;
 
-	/// The tap marker has changed in the timing controller.
-	agi::signal::Signal<> AnnounceUpdatedTapMarker;
+    /// The tap marker has changed in the timing controller.
+    agi::signal::Signal<> AnnounceUpdatedTapMarker;
 
 public:
-	/// @brief Get any warning message to show in the audio display
-	/// @return The warning message to show, may be empty if there is none
-	virtual wxString GetWarningMessage() const = 0;
-
-	/// @brief Get the time range the user is most likely to want to see for the current state
-	/// @return A time range
-	///
-	/// This is used for "bring working area into view" operations.
-	virtual TimeRange GetIdealVisibleTimeRange() const = 0;
-
-	/// @brief Get the primary playback range
-	/// @return A time range
-	///
-	/// Get the time range the user is most likely to want to play back
-	/// currently.
-	virtual TimeRange GetPrimaryPlaybackRange() const = 0;
-
-	/// @brief Get the active line's time
-	/// @return A time range
-	///
-	/// Get the time range which the active line would have if any pending
-	/// modifications were committed.
-	virtual TimeRange GetActiveLineRange() const = 0;
-
-	/// @brief Get all rendering style ranges
-	/// @param[out] ranges Rendering ranges will be added to this
-	virtual void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const = 0;
-
-	/// @brief Return the position of the tap marker
-	virtual int GetTapMarkerPosition() const = 0;
-
-	/// @brief Return the index of the tap marker
-	virtual size_t GetTapMarkerIndex() const = 0;
-
-	enum NextMode {
-		/// Advance to the next timing unit, whether it's a line or a sub-part
-		/// of a line such as a karaoke syllable
-		TIMING_UNIT = 0,
-
-		/// @brief Advance to the next line
-		///
-		/// This may create a new line if there are no more lines in the file,
-		/// but should never modify existing lines
-		LINE,
-
-		/// @brief Advance to the next line using default timing
-		///
-		/// This may create new lines when needed, and should discard any
-		/// existing timing data in favor of the defaults
-		LINE_RESET_DEFAULT
-	};
-
-	/// @brief Go to next timing unit
-	/// @param mode What sort of timing unit should be advanced to
-	virtual void Next(NextMode mode) = 0;
-
-	/// @brief Go to the previous timing unit
-	///
-	/// Rewinds the timing controller to the previous timing unit.
-	virtual void Prev() = 0;
-
-	/// @brief Commit all changes
-	///
-	/// Stores all changes permanently.
-	virtual void Commit() = 0;
-
-	/// @brief Revert all changes
-	///
-	/// Revert all changes to the last committed state.
-	virtual void Revert() = 0;
-
-	/// Add lead-in time to the current timing unit
-	virtual void AddLeadIn() = 0;
-
-	/// Add lead-out time to the current timing unit
-	virtual void AddLeadOut() = 0;
-
-	/// Modify the length of the current and possibly following timing units
-	/// @param delta Amount to add in centiseconds
-	/// @param shift_following Should the following things be shifted by delta?
-	virtual void ModifyLength(int delta, bool shift_following) = 0;
-
-	/// Modify the start time of the current timing unit
-	/// @param delta Amount to add in centiseconds
-	virtual void ModifyStart(int delta) = 0;
-
-	/// Move tap marker position to given position
-	/// @param position to move marker to
-	virtual void MoveTapMarker(int ms) = 0;
-
-	/// Go to next tap marker
-	/// @return True if moved to the next marker, False if tap marker is already
-	///         the last marker of the line
-	virtual bool NextTapMarker() = 0;
-
-	/// @brief Determine if a position is close to a draggable marker
-	/// @param ms          The time in milliseconds to test
-	/// @param sensitivity Distance in milliseconds to consider markers as nearby
-	/// @return True if a marker is close by the given time, as defined by sensitivity
-	///
-	/// This is solely for hit-testing against draggable markers, for
-	/// controlling the mouse cursor.
-	virtual bool IsNearbyMarker(int ms, int sensitivity, bool alt_down) const = 0;
-
-	/// @brief Return the text of the currently selected syllab
-	virtual std::string GetCurrentSylText() const { return ""; }
-
-	/// @ brief Set the text for the currently selected syllab
-	virtual void SetCurrentSylText(std::string new_text) {}
-
-	/// @brief The user pressed the left mouse button on the audio
-	/// @param ms          The time in milliseconds the user clicked
-	/// @param ctrl_down   Is the user currently holding the ctrl key down?
-	/// @param alt_down    Is the user currently holding the alt key down?
-	/// @param sensitivity Distance in milliseconds to consider existing markers
-	/// @param snap_range  Maximum snapping range in milliseconds
-	/// @return All audio markers at the clicked position which are eligible
-	///         to be dragged, if any.
-	virtual std::vector<AudioMarker*> OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range) = 0;
-
-	/// @brief The user pressed the right mouse button on the audio
-	/// @param ms          The time in milliseconds the user clicked
-	/// @param ctrl_down   Is the user currently holding the ctrl key down?
-	/// @param sensitivity Distance in milliseconds to consider existing markers
-	/// @param snap_range  Maximum snapping range in milliseconds
-	/// @return All audio markers at the clicked position which are eligible
-	///         to be dragged, if any.
-	virtual std::vector<AudioMarker*> OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range) = 0;
-
-	/// @brief The user dragged one or more timing markers
-	/// @param marker       The markers being dragged. This is guaranteed to be
-	///                     a vector returned from OnLeftClick or OnRightClick.
-	/// @param new_position Time position the marker was dragged to
-	/// @param snap_range   Maximum snapping range in milliseconds
-	virtual void OnMarkerDrag(std::vector<AudioMarker*> const& marker, int new_position, int snap_range) = 0;
-
-	/// @brief Destructor
-	virtual ~AudioTimingController() = default;
-
-	DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceUpdatedTapMarker, AddUpdatedTapMarkerListener)
+    /// @brief Get any warning message to show in the audio display
+    /// @return The warning message to show, may be empty if there is none
+    virtual wxString GetWarningMessage() const = 0;
+
+    /// @brief Get the time range the user is most likely to want to see for the current state
+    /// @return A time range
+    ///
+    /// This is used for "bring working area into view" operations.
+    virtual TimeRange GetIdealVisibleTimeRange() const = 0;
+
+    /// @brief Get the primary playback range
+    /// @return A time range
+    ///
+    /// Get the time range the user is most likely to want to play back
+    /// currently.
+    virtual TimeRange GetPrimaryPlaybackRange() const = 0;
+
+    /// @brief Get the active line's time
+    /// @return A time range
+    ///
+    /// Get the time range which the active line would have if any pending
+    /// modifications were committed.
+    virtual TimeRange GetActiveLineRange() const = 0;
+
+    /// @brief Get all rendering style ranges
+    /// @param[out] ranges Rendering ranges will be added to this
+    virtual void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const = 0;
+
+    /// @brief Return the position of the tap marker
+    virtual int GetTapMarkerPosition() const = 0;
+
+    /// @brief Return the index of the tap marker
+    virtual size_t GetTapMarkerIndex() const = 0;
+
+    enum NextMode {
+        /// Advance to the next timing unit, whether it's a line or a sub-part
+        /// of a line such as a karaoke syllable
+        TIMING_UNIT = 0,
+
+        /// @brief Advance to the next line
+        ///
+        /// This may create a new line if there are no more lines in the file,
+        /// but should never modify existing lines
+        LINE,
+
+        /// @brief Advance to the next line using default timing
+        ///
+        /// This may create new lines when needed, and should discard any
+        /// existing timing data in favor of the defaults
+        LINE_RESET_DEFAULT
+    };
+
+    /// @brief Go to next timing unit
+    /// @param mode What sort of timing unit should be advanced to
+    virtual void Next(NextMode mode) = 0;
+
+    /// @brief Go to the previous timing unit
+    ///
+    /// Rewinds the timing controller to the previous timing unit.
+    virtual void Prev() = 0;
+
+    /// @brief Commit all changes
+    ///
+    /// Stores all changes permanently.
+    virtual void Commit() = 0;
+
+    /// @brief Revert all changes
+    ///
+    /// Revert all changes to the last committed state.
+    virtual void Revert() = 0;
+
+    /// Add lead-in time to the current timing unit
+    virtual void AddLeadIn() = 0;
+
+    /// Add lead-out time to the current timing unit
+    virtual void AddLeadOut() = 0;
+
+    /// Modify the length of the current and possibly following timing units
+    /// @param delta Amount to add in centiseconds
+    /// @param shift_following Should the following things be shifted by delta?
+    virtual void ModifyLength(int delta, bool shift_following) = 0;
+
+    /// Modify the start time of the current timing unit
+    /// @param delta Amount to add in centiseconds
+    virtual void ModifyStart(int delta) = 0;
+
+    /// Move tap marker position to given position
+    /// @param position to move marker to
+    virtual void MoveTapMarker(int ms) = 0;
+
+    /// Go to next tap marker
+    /// @return True if moved to the next marker, False if tap marker is already
+    ///         the last marker of the line
+    virtual bool NextTapMarker() = 0;
+
+    /// @brief Determine if a position is close to a draggable marker
+    /// @param ms          The time in milliseconds to test
+    /// @param sensitivity Distance in milliseconds to consider markers as nearby
+    /// @return True if a marker is close by the given time, as defined by sensitivity
+    ///
+    /// This is solely for hit-testing against draggable markers, for
+    /// controlling the mouse cursor.
+    virtual bool IsNearbyMarker(int ms, int sensitivity, bool alt_down) const = 0;
+
+    /// @brief Return the text of the currently selected syllab
+    virtual std::string GetCurrentSylText() const { return ""; }
+
+    /// @ brief Set the text for the currently selected syllab
+    virtual void SetCurrentSylText(std::string new_text) {}
+
+    /// @brief The user pressed the left mouse button on the audio
+    /// @param ms          The time in milliseconds the user clicked
+    /// @param ctrl_down   Is the user currently holding the ctrl key down?
+    /// @param alt_down    Is the user currently holding the alt key down?
+    /// @param sensitivity Distance in milliseconds to consider existing markers
+    /// @param snap_range  Maximum snapping range in milliseconds
+    /// @return All audio markers at the clicked position which are eligible
+    ///         to be dragged, if any.
+    virtual std::vector<AudioMarker *> OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range) = 0;
+
+    /// @brief The user pressed the right mouse button on the audio
+    /// @param ms          The time in milliseconds the user clicked
+    /// @param ctrl_down   Is the user currently holding the ctrl key down?
+    /// @param sensitivity Distance in milliseconds to consider existing markers
+    /// @param snap_range  Maximum snapping range in milliseconds
+    /// @return All audio markers at the clicked position which are eligible
+    ///         to be dragged, if any.
+    virtual std::vector<AudioMarker *> OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range) = 0;
+
+    /// @brief The user dragged one or more timing markers
+    /// @param marker       The markers being dragged. This is guaranteed to be
+    ///                     a vector returned from OnLeftClick or OnRightClick.
+    /// @param new_position Time position the marker was dragged to
+    /// @param snap_range   Maximum snapping range in milliseconds
+    virtual void OnMarkerDrag(std::vector<AudioMarker *> const &marker, int new_position, int snap_range) = 0;
+
+    /// @brief Destructor
+    virtual ~AudioTimingController() = default;
+
+    DEFINE_SIGNAL_ADDERS(AnnounceUpdatedPrimaryRange, AddUpdatedPrimaryRangeListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceUpdatedStyleRanges, AddUpdatedStyleRangesListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceUpdatedTapMarker, AddUpdatedTapMarkerListener)
 };
 
 /// @brief Create a standard dialogue audio timing controller
@@ -212,4 +212,4 @@ std::unique_ptr<AudioTimingController> CreateDialogueTimingController(agi::Conte
 /// @brief Create a karaoke audio timing controller
 /// @param c Project context
 /// @param kara Karaoke model
-std::unique_ptr<AudioTimingController> CreateKaraokeTimingController(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed);
+std::unique_ptr<AudioTimingController> CreateKaraokeTimingController(agi::Context *c, AssKaraoke *kara, agi::signal::Connection &file_changed);
diff --git a/src/audio_timing_dialogue.cpp b/src/audio_timing_dialogue.cpp
index 260401d2e1370ae241239543674b3a6f9eb23837..199be29993b22797f8784d0f760de639525c8eb3 100644
--- a/src/audio_timing_dialogue.cpp
+++ b/src/audio_timing_dialogue.cpp
@@ -55,99 +55,91 @@ class TimeableLine;
 /// Audio marker intended to live in pairs of two, taking styles depending
 /// on which marker in the pair is to the left and which is to the right.
 class DialogueTimingMarker final : public AudioMarker {
-	/// Current ms position of this marker
-	int position;
+    /// Current ms position of this marker
+    int position;
 
-	/// Draw style for the marker
-	const Pen *style;
+    /// Draw style for the marker
+    const Pen *style;
 
-	/// Feet style for the marker
-	FeetStyle feet;
+    /// Feet style for the marker
+    FeetStyle feet;
 
-	/// Rendering style of the owning line, needed for sorting
-	AudioRenderingStyle type;
+    /// Rendering style of the owning line, needed for sorting
+    AudioRenderingStyle type;
 
-	/// The line which owns this marker
-	TimeableLine *line;
+    /// The line which owns this marker
+    TimeableLine *line;
 
 public:
-	int       GetPosition() const override { return position; }
-	wxPen     GetStyle()    const override { return *style; }
-	FeetStyle GetFeet()     const override { return feet; }
-
-	/// Move the marker to a new position
-	/// @param new_position The position to move the marker to, in milliseconds
-	///
-	/// This notifies the owning line of the change, so that it can ensure that
-	/// this marker has the appropriate rendering style.
-	void SetPosition(int new_position);
-
-	/// Constructor
-	/// @param position Initial position of this marker
-	/// @param style Rendering style of this marker
-	/// @param feet Foot style of this marker
-	/// @param type Type of this marker, used only for sorting
-	/// @param line Line which this is a marker for
-	DialogueTimingMarker(int position, const Pen *style, FeetStyle feet, AudioRenderingStyle type, TimeableLine *line)
-	: position(position)
-	, style(style)
-	, feet(feet)
-	, type(type)
-	, line(line)
-	{
-	}
-
-	DialogueTimingMarker(DialogueTimingMarker const& other, TimeableLine *line)
-	: position(other.position)
-	, style(other.style)
-	, feet(other.feet)
-	, type(other.type)
-	, line(line)
-	{
-	}
-
-	/// Get the line which this is a marker for
-	TimeableLine *GetLine() const { return line; }
-
-	/// Implicit decay to the position of the marker
-	operator int() const { return position; }
-
-	/// Comparison operator
-	///
-	/// Compares first on position, then on audio rendering style so that the
-	/// markers for the active line end up after those for the inactive lines.
-	bool operator<(DialogueTimingMarker const& other) const
-	{
-		if (position < other.position) return true;
-		if (position > other.position) return false;
-		return type < other.type;
-	}
-
-	/// Swap the rendering style of this marker with that of the passed marker
-	void SwapStyles(DialogueTimingMarker &other)
-	{
-		std::swap(style, other.style);
-		std::swap(feet, other.feet);
-	}
+    int       GetPosition() const override { return position; }
+    wxPen     GetStyle()    const override { return *style; }
+    FeetStyle GetFeet()     const override { return feet; }
+
+    /// Move the marker to a new position
+    /// @param new_position The position to move the marker to, in milliseconds
+    ///
+    /// This notifies the owning line of the change, so that it can ensure that
+    /// this marker has the appropriate rendering style.
+    void SetPosition(int new_position);
+
+    /// Constructor
+    /// @param position Initial position of this marker
+    /// @param style Rendering style of this marker
+    /// @param feet Foot style of this marker
+    /// @param type Type of this marker, used only for sorting
+    /// @param line Line which this is a marker for
+    DialogueTimingMarker(int position, const Pen *style, FeetStyle feet, AudioRenderingStyle type, TimeableLine *line)
+        : position(position)
+        , style(style)
+        , feet(feet)
+        , type(type)
+        , line(line) {
+    }
+
+    DialogueTimingMarker(DialogueTimingMarker const &other, TimeableLine *line)
+        : position(other.position)
+        , style(other.style)
+        , feet(other.feet)
+        , type(other.type)
+        , line(line) {
+    }
+
+    /// Get the line which this is a marker for
+    TimeableLine *GetLine() const { return line; }
+
+    /// Implicit decay to the position of the marker
+    operator int() const { return position; }
+
+    /// Comparison operator
+    ///
+    /// Compares first on position, then on audio rendering style so that the
+    /// markers for the active line end up after those for the inactive lines.
+    bool operator<(DialogueTimingMarker const &other) const {
+        if (position < other.position) return true;
+        if (position > other.position) return false;
+        return type < other.type;
+    }
+
+    /// Swap the rendering style of this marker with that of the passed marker
+    void SwapStyles(DialogueTimingMarker &other) {
+        std::swap(style, other.style);
+        std::swap(feet, other.feet);
+    }
 };
 
 /// A comparison predicate for pointers to dialogue markers and millisecond positions
-struct marker_ptr_cmp
-{
-	bool operator()(const DialogueTimingMarker *lft, const DialogueTimingMarker *rgt) const
-	{
-		return *lft < *rgt;
-	}
-
-	bool operator()(const DialogueTimingMarker *lft, int rgt) const
-	{
-		return *lft < rgt;
-	}
-
-	bool operator()(int lft, const DialogueTimingMarker *rgt) const
-	{
-		return lft < *rgt;
-	}
+struct marker_ptr_cmp {
+    bool operator()(const DialogueTimingMarker *lft, const DialogueTimingMarker *rgt) const {
+        return *lft < *rgt;
+    }
+
+    bool operator()(const DialogueTimingMarker *lft, int rgt) const {
+        return *lft < rgt;
+    }
+
+    bool operator()(int lft, const DialogueTimingMarker *rgt) const {
+        return lft < *rgt;
+    }
 };
 
 /// @class TimeableLine
@@ -157,124 +149,113 @@ struct marker_ptr_cmp
 /// both active and inactive. In addition, it can apply changes made via those
 /// markers to the tracked dialogue line.
 class TimeableLine {
-	/// The current tracked dialogue line
-	AssDialogue *line = nullptr;
-	/// The rendering style of this line
-	AudioRenderingStyle style;
+    /// The current tracked dialogue line
+    AssDialogue *line = nullptr;
+    /// The rendering style of this line
+    AudioRenderingStyle style;
 
-	/// One of the markers. Initially the left marker, but the user may change this.
-	DialogueTimingMarker marker1;
-	/// One of the markers. Initially the right marker, but the user may change this.
-	DialogueTimingMarker marker2;
+    /// One of the markers. Initially the left marker, but the user may change this.
+    DialogueTimingMarker marker1;
+    /// One of the markers. Initially the right marker, but the user may change this.
+    DialogueTimingMarker marker2;
 
-	/// Pointer to whichever marker happens to be on the left
-	DialogueTimingMarker *left_marker;
-	/// Pointer to whichever marker happens to be on the right
-	DialogueTimingMarker *right_marker;
+    /// Pointer to whichever marker happens to be on the left
+    DialogueTimingMarker *left_marker;
+    /// Pointer to whichever marker happens to be on the right
+    DialogueTimingMarker *right_marker;
 
 public:
-	/// Constructor
-	/// @param style Rendering style to use for this line's time range
-	/// @param style_left The rendering style for the start marker
-	/// @param style_right The rendering style for the end marker
-	TimeableLine(AudioRenderingStyle style, const Pen *style_left, const Pen *style_right)
-	: style(style)
-	, marker1(0, style_left, AudioMarker::Feet_Right, style, this)
-	, marker2(0, style_right, AudioMarker::Feet_Left, style, this)
-	, left_marker(&marker1)
-	, right_marker(&marker2)
-	{
-	}
-
-	/// Explicit copy constructor needed due to that the markers have a pointer to this
-	TimeableLine(TimeableLine const& other)
-	: line(other.line)
-	, style(other.style)
-	, marker1(*other.left_marker, this)
-	, marker2(*other.right_marker, this)
-	, left_marker(&marker1)
-	, right_marker(&marker2)
-	{
-	}
-
-	/// Get the tracked dialogue line
-	AssDialogue *GetLine() const { return line; }
-
-	/// Get the time range for this line
-	operator TimeRange() const { return TimeRange(*left_marker, *right_marker); }
-
-	/// Add this line's style to the style ranges
-	void GetStyleRange(AudioRenderingStyleRanges *ranges) const
-	{
-		ranges->AddRange(*left_marker, *right_marker, style);
-	}
-
-	/// Get this line's markers
-	/// @param c Vector to add the markers to
-	template<typename Container>
-	void GetMarkers(Container *c) const
-	{
-		c->push_back(left_marker);
-		c->push_back(right_marker);
-	}
-
-	/// Get the leftmost of the markers
-	DialogueTimingMarker *GetLeftMarker() { return left_marker; }
-	const DialogueTimingMarker *GetLeftMarker() const { return left_marker; }
-
-	/// Get the rightmost of the markers
-	DialogueTimingMarker *GetRightMarker() { return right_marker; }
-	const DialogueTimingMarker *GetRightMarker() const { return right_marker; }
-
-	/// Does this line have a marker in the given range?
-	bool ContainsMarker(TimeRange const& range) const
-	{
-		return range.contains(marker1) || range.contains(marker2);
-	}
-
-	/// Check if the markers have the correct styles, and correct them if needed
-	void CheckMarkers()
-	{
-		if (*right_marker < *left_marker)
-		{
-			marker1.SwapStyles(marker2);
-			std::swap(left_marker, right_marker);
-		}
-	}
-
-	/// Apply any changes made here to the tracked dialogue line
-	void Apply()
-	{
-		if (line)
-		{
-			line->Start = left_marker->GetPosition();
-			line->End = right_marker->GetPosition();
-		}
-	}
-
-	/// Set the dialogue line which this is tracking and reset the markers to
-	/// the line's time range
-	/// @return Were the markers actually set to the line's time?
-	bool SetLine(AssDialogue *new_line)
-	{
-		if (!line || new_line->End > 0)
-		{
-			line = new_line;
-			marker1.SetPosition(new_line->Start);
-			marker2.SetPosition(new_line->End);
-			return true;
-		}
-		else
-		{
-			line = new_line;
-			return false;
-		}
-	}
+    /// Constructor
+    /// @param style Rendering style to use for this line's time range
+    /// @param style_left The rendering style for the start marker
+    /// @param style_right The rendering style for the end marker
+    TimeableLine(AudioRenderingStyle style, const Pen *style_left, const Pen *style_right)
+        : style(style)
+        , marker1(0, style_left, AudioMarker::Feet_Right, style, this)
+        , marker2(0, style_right, AudioMarker::Feet_Left, style, this)
+        , left_marker(&marker1)
+        , right_marker(&marker2) {
+    }
+
+    /// Explicit copy constructor needed due to that the markers have a pointer to this
+    TimeableLine(TimeableLine const &other)
+        : line(other.line)
+        , style(other.style)
+        , marker1(*other.left_marker, this)
+        , marker2(*other.right_marker, this)
+        , left_marker(&marker1)
+        , right_marker(&marker2) {
+    }
+
+    /// Get the tracked dialogue line
+    AssDialogue *GetLine() const { return line; }
+
+    /// Get the time range for this line
+    operator TimeRange() const { return TimeRange(*left_marker, *right_marker); }
+
+    /// Add this line's style to the style ranges
+    void GetStyleRange(AudioRenderingStyleRanges *ranges) const {
+        ranges->AddRange(*left_marker, *right_marker, style);
+    }
+
+    /// Get this line's markers
+    /// @param c Vector to add the markers to
+    template<typename Container>
+    void GetMarkers(Container *c) const {
+        c->push_back(left_marker);
+        c->push_back(right_marker);
+    }
+
+    /// Get the leftmost of the markers
+    DialogueTimingMarker *GetLeftMarker() { return left_marker; }
+    const DialogueTimingMarker *GetLeftMarker() const { return left_marker; }
+
+    /// Get the rightmost of the markers
+    DialogueTimingMarker *GetRightMarker() { return right_marker; }
+    const DialogueTimingMarker *GetRightMarker() const { return right_marker; }
+
+    /// Does this line have a marker in the given range?
+    bool ContainsMarker(TimeRange const &range) const {
+        return range.contains(marker1) || range.contains(marker2);
+    }
+
+    /// Check if the markers have the correct styles, and correct them if needed
+    void CheckMarkers() {
+        if (*right_marker < *left_marker) {
+            marker1.SwapStyles(marker2);
+            std::swap(left_marker, right_marker);
+        }
+    }
+
+    /// Apply any changes made here to the tracked dialogue line
+    void Apply() {
+        if (line) {
+            line->Start = left_marker->GetPosition();
+            line->End = right_marker->GetPosition();
+        }
+    }
+
+    /// Set the dialogue line which this is tracking and reset the markers to
+    /// the line's time range
+    /// @return Were the markers actually set to the line's time?
+    bool SetLine(AssDialogue *new_line) {
+        if (!line || new_line->End > 0) {
+            line = new_line;
+            marker1.SetPosition(new_line->Start);
+            marker2.SetPosition(new_line->End);
+            return true;
+        }
+        else {
+            line = new_line;
+            return false;
+        }
+    }
 };
 
-void DialogueTimingMarker::SetPosition(int new_position) {
-	position = new_position;
-	line->CheckMarkers();
+void DialogueTimingMarker::SetPosition(int new_position)
+{
+    position = new_position;
+    line->CheckMarkers();
 }
 
 /// @class AudioTimingControllerDialogue
@@ -287,722 +268,698 @@ void DialogueTimingMarker::SetPosition(int new_position) {
 /// as the active line starts/ends can optionally be dragged along with the
 /// active line's markers, updating those lines as well.
 class AudioTimingControllerDialogue final : public AudioTimingController {
-	/// The rendering style for the active line's start marker
-	Pen style_left{"Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness"};
-	/// The rendering style for the active line's end marker
-	Pen style_right{"Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness"};
-	/// The rendering style for the start and end markers of inactive lines
-	Pen style_inactive{"Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness"};
+    /// The rendering style for the active line's start marker
+    Pen style_left{"Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness"};
+    /// The rendering style for the active line's end marker
+    Pen style_right{"Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness"};
+    /// The rendering style for the start and end markers of inactive lines
+    Pen style_inactive{"Colour/Audio Display/Line Boundary Inactive Line", "Audio/Line Boundaries Thickness"};
 
-	/// The currently active line
-	TimeableLine active_line;
+    /// The currently active line
+    TimeableLine active_line;
 
-	/// Inactive lines which are currently modifiable
-	std::list<TimeableLine> inactive_lines;
+    /// Inactive lines which are currently modifiable
+    std::list<TimeableLine> inactive_lines;
 
-	/// Selected lines which are currently modifiable
-	std::list<TimeableLine> selected_lines;
+    /// Selected lines which are currently modifiable
+    std::list<TimeableLine> selected_lines;
 
-	/// All audio markers for active and inactive lines, sorted by position
-	std::vector<DialogueTimingMarker*> markers;
+    /// All audio markers for active and inactive lines, sorted by position
+    std::vector<DialogueTimingMarker *> markers;
 
-	/// Marker provider for video keyframes
-	AudioMarkerProviderKeyframes keyframes_provider;
+    /// Marker provider for video keyframes
+    AudioMarkerProviderKeyframes keyframes_provider;
 
-	/// Marker provider for video playback position
-	VideoPositionMarkerProvider video_position_provider;
+    /// Marker provider for video playback position
+    VideoPositionMarkerProvider video_position_provider;
 
-	/// Marker provider for seconds lines
-	SecondsMarkerProvider seconds_provider;
+    /// Marker provider for seconds lines
+    SecondsMarkerProvider seconds_provider;
 
-	/// The set of lines which have been modified and need to have their
-	/// changes applied on commit
-	std::set<TimeableLine*> modified_lines;
+    /// The set of lines which have been modified and need to have their
+    /// changes applied on commit
+    std::set<TimeableLine *> modified_lines;
 
-	/// Commit id for coalescing purposes when in auto commit mode
-	int commit_id =-1;
+    /// Commit id for coalescing purposes when in auto commit mode
+    int commit_id = -1;
 
-	/// The owning project context
-	agi::Context *context;
+    /// The owning project context
+    agi::Context *context;
 
-	/// The time which was clicked on for alt-dragging mode
-	int clicked_ms;
+    /// The time which was clicked on for alt-dragging mode
+    int clicked_ms;
 
-	/// Index of marker serving as tap marker
-	/// For AudioTimingControllerDialogue:
-	/// - 0 is left marker
-	/// - 1 is right marker
-	size_t tap_marker_idx = 0;
+    /// Index of marker serving as tap marker
+    /// For AudioTimingControllerDialogue:
+    /// - 0 is left marker
+    /// - 1 is right marker
+    size_t tap_marker_idx = 0;
 
-	/// Autocommit option
-	const agi::OptionValue *auto_commit = OPT_GET("Audio/Auto/Commit");
-	const agi::OptionValue *inactive_line_mode = OPT_GET("Audio/Inactive Lines Display Mode");
-	const agi::OptionValue *inactive_line_comments = OPT_GET("Audio/Display/Draw/Inactive Comments");
-	const agi::OptionValue *drag_timing = OPT_GET("Audio/Drag Timing");
+    /// Autocommit option
+    const agi::OptionValue *auto_commit = OPT_GET("Audio/Auto/Commit");
+    const agi::OptionValue *inactive_line_mode = OPT_GET("Audio/Inactive Lines Display Mode");
+    const agi::OptionValue *inactive_line_comments = OPT_GET("Audio/Display/Draw/Inactive Comments");
+    const agi::OptionValue *drag_timing = OPT_GET("Audio/Drag Timing");
 
-	agi::signal::Connection commit_connection;
-	agi::signal::Connection audio_open_connection;
-	agi::signal::Connection inactive_line_mode_connection;
-	agi::signal::Connection inactive_line_comment_connection;
-	agi::signal::Connection active_line_connection;
-	agi::signal::Connection selection_connection;
+    agi::signal::Connection commit_connection;
+    agi::signal::Connection audio_open_connection;
+    agi::signal::Connection inactive_line_mode_connection;
+    agi::signal::Connection inactive_line_comment_connection;
+    agi::signal::Connection active_line_connection;
+    agi::signal::Connection selection_connection;
 
-	/// Update the audio controller's selection
-	void UpdateSelection();
+    /// Update the audio controller's selection
+    void UpdateSelection();
 
-	/// Regenerate the list of timeable inactive lines
-	void RegenerateInactiveLines();
+    /// Regenerate the list of timeable inactive lines
+    void RegenerateInactiveLines();
 
-	/// Regenerate the list of timeable selected lines
-	void RegenerateSelectedLines();
+    /// Regenerate the list of timeable selected lines
+    void RegenerateSelectedLines();
 
-	/// Add a line to the list of timeable inactive lines
-	void AddInactiveLine(Selection const& sel, AssDialogue *diag);
+    /// Add a line to the list of timeable inactive lines
+    void AddInactiveLine(Selection const &sel, AssDialogue *diag);
 
-	/// Regenerate the list of active and inactive line markers
-	void RegenerateMarkers();
+    /// Regenerate the list of active and inactive line markers
+    void RegenerateMarkers();
 
-	/// Get the start markers for the active line and all selected lines
-	std::vector<AudioMarker*> GetLeftMarkers();
+    /// Get the start markers for the active line and all selected lines
+    std::vector<AudioMarker *> GetLeftMarkers();
 
-	/// Get the end markers for the active line and all selected lines
-	std::vector<AudioMarker*> GetRightMarkers();
+    /// Get the end markers for the active line and all selected lines
+    std::vector<AudioMarker *> GetRightMarkers();
 
-	/// @brief Set the position of markers and announce the change to the world
-	/// @param upd_markers Markers to move
-	/// @param ms New position of the markers
-	void SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms, int snap_range);
+    /// @brief Set the position of markers and announce the change to the world
+    /// @param upd_markers Markers to move
+    /// @param ms New position of the markers
+    void SetMarkers(std::vector<AudioMarker *> const &upd_markers, int ms, int snap_range);
 
-	/// Try to snap all of the active markers to any inactive markers
-	/// @param snap_range Maximum distance to snap in milliseconds
-	/// @param active     Markers which should be snapped
-	/// @return The distance the markers were shifted by
-	int SnapMarkers(int snap_range, std::vector<AudioMarker*> const& markers) const;
+    /// Try to snap all of the active markers to any inactive markers
+    /// @param snap_range Maximum distance to snap in milliseconds
+    /// @param active     Markers which should be snapped
+    /// @return The distance the markers were shifted by
+    int SnapMarkers(int snap_range, std::vector<AudioMarker *> const &markers) const;
 
-	/// Commit all pending changes to the file
-	/// @param user_triggered Is this a user-initiated commit or an autocommit
-	void DoCommit(bool user_triggered);
+    /// Commit all pending changes to the file
+    /// @param user_triggered Is this a user-initiated commit or an autocommit
+    void DoCommit(bool user_triggered);
 
-	void OnSelectedSetChanged();
+    void OnSelectedSetChanged();
 
-	// AssFile events
-	void OnFileChanged(int type);
+    // AssFile events
+    void OnFileChanged(int type);
 
 public:
-	// AudioMarkerProvider interface
-	void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const override;
-	int GetTapMarkerPosition() const override;
-	size_t GetTapMarkerIndex() const override;
-
-	// AudioTimingController interface
-	void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const override;
-	void GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const override { }
-	void Next(NextMode mode) override;
-	void Prev() override;
-	void Revert() override;
-	void AddLeadIn() override;
-	void AddLeadOut() override;
-	void ModifyLength(int delta, bool shift_following) override;
-	void ModifyStart(int delta) override;
-	void MoveTapMarker(int ms) override;
-	bool NextTapMarker() override;
-	bool IsNearbyMarker(int ms, int sensitivity, bool alt_down) const override;
-	std::vector<AudioMarker*> OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range) override;
-	std::vector<AudioMarker*> OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range) override;
-	void OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range) override;
-
-	// We have no warning messages currently, maybe add the old "Modified" message back later?
-	wxString GetWarningMessage() const override { return wxString(); }
-	TimeRange GetIdealVisibleTimeRange() const override { return active_line; }
-	TimeRange GetPrimaryPlaybackRange() const override { return active_line; }
-	TimeRange GetActiveLineRange() const override { return active_line; }
-	void Commit() override { DoCommit(true); }
-
-	/// Constructor
-	/// @param c Project context
-	AudioTimingControllerDialogue(agi::Context *c);
+    // AudioMarkerProvider interface
+    void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const override;
+    int GetTapMarkerPosition() const override;
+    size_t GetTapMarkerIndex() const override;
+
+    // AudioTimingController interface
+    void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const override;
+    void GetLabels(TimeRange const &range, std::vector<AudioLabel> &out) const override { }
+    void Next(NextMode mode) override;
+    void Prev() override;
+    void Revert() override;
+    void AddLeadIn() override;
+    void AddLeadOut() override;
+    void ModifyLength(int delta, bool shift_following) override;
+    void ModifyStart(int delta) override;
+    void MoveTapMarker(int ms) override;
+    bool NextTapMarker() override;
+    bool IsNearbyMarker(int ms, int sensitivity, bool alt_down) const override;
+    std::vector<AudioMarker *> OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range) override;
+    std::vector<AudioMarker *> OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range) override;
+    void OnMarkerDrag(std::vector<AudioMarker *> const &markers, int new_position, int snap_range) override;
+
+    // We have no warning messages currently, maybe add the old "Modified" message back later?
+    wxString GetWarningMessage() const override { return wxString(); }
+    TimeRange GetIdealVisibleTimeRange() const override { return active_line; }
+    TimeRange GetPrimaryPlaybackRange() const override { return active_line; }
+    TimeRange GetActiveLineRange() const override { return active_line; }
+    void Commit() override { DoCommit(true); }
+
+    /// Constructor
+    /// @param c Project context
+    AudioTimingControllerDialogue(agi::Context *c);
 };
 
 AudioTimingControllerDialogue::AudioTimingControllerDialogue(agi::Context *c)
-: active_line(AudioStyle_Primary, &style_left, &style_right)
-, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Dialogue Mode")
-, video_position_provider(c)
-, context(c)
-, commit_connection(c->ass->AddCommitListener(&AudioTimingControllerDialogue::OnFileChanged, this))
-, inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
-, inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
-, active_line_connection(c->selectionController->AddActiveLineListener(&AudioTimingControllerDialogue::Revert, this))
-, selection_connection(c->selectionController->AddSelectionListener(&AudioTimingControllerDialogue::OnSelectedSetChanged, this))
+    : active_line(AudioStyle_Primary, &style_left, &style_right)
+    , keyframes_provider(c, "Audio/Display/Draw/Keyframes in Dialogue Mode")
+    , video_position_provider(c)
+    , context(c)
+    , commit_connection(c->ass->AddCommitListener(&AudioTimingControllerDialogue::OnFileChanged, this))
+    , inactive_line_mode_connection(OPT_SUB("Audio/Inactive Lines Display Mode", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
+    , inactive_line_comment_connection(OPT_SUB("Audio/Display/Draw/Inactive Comments", &AudioTimingControllerDialogue::RegenerateInactiveLines, this))
+    , active_line_connection(c->selectionController->AddActiveLineListener(&AudioTimingControllerDialogue::Revert, this))
+    , selection_connection(c->selectionController->AddSelectionListener(&AudioTimingControllerDialogue::OnSelectedSetChanged, this))
 {
-	keyframes_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
-	video_position_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
-	seconds_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
+    keyframes_provider.AddMarkerMovedListener([ = ] { AnnounceMarkerMoved(); });
+    video_position_provider.AddMarkerMovedListener([ = ] { AnnounceMarkerMoved(); });
+    seconds_provider.AddMarkerMovedListener([ = ] { AnnounceMarkerMoved(); });
 
-	Revert();
+    Revert();
 }
 
 void AudioTimingControllerDialogue::GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const
 {
-	// The order matters here; later markers are painted on top of earlier
-	// markers, so the markers that we want to end up on top need to appear last
+    // The order matters here; later markers are painted on top of earlier
+    // markers, so the markers that we want to end up on top need to appear last
 
-	seconds_provider.GetMarkers(range, out_markers);
+    seconds_provider.GetMarkers(range, out_markers);
 
-	// Copy inactive line markers in the range
-	copy(
-		boost::lower_bound(markers, range.begin(), marker_ptr_cmp()),
-		boost::upper_bound(markers, range.end(), marker_ptr_cmp()),
-		back_inserter(out_markers));
+    // Copy inactive line markers in the range
+    copy(
+        boost::lower_bound(markers, range.begin(), marker_ptr_cmp()),
+        boost::upper_bound(markers, range.end(), marker_ptr_cmp()),
+        back_inserter(out_markers));
 
-	keyframes_provider.GetMarkers(range, out_markers);
-	video_position_provider.GetMarkers(range, out_markers);
+    keyframes_provider.GetMarkers(range, out_markers);
+    video_position_provider.GetMarkers(range, out_markers);
 }
 
 int AudioTimingControllerDialogue::GetTapMarkerPosition() const
 {
-	assert(tap_marker_idx <= 1);
+    assert(tap_marker_idx <= 1);
 
-	if (tap_marker_idx == 0) {
-		return *active_line.GetLeftMarker();
-	}
-	else {
-		return *active_line.GetRightMarker();
-	}
+    if (tap_marker_idx == 0) {
+        return *active_line.GetLeftMarker();
+    }
+    else {
+        return *active_line.GetRightMarker();
+    }
 }
 
 size_t AudioTimingControllerDialogue::GetTapMarkerIndex() const
 {
-	assert(tap_marker_idx <= 1);
-	return tap_marker_idx;
+    assert(tap_marker_idx <= 1);
+    return tap_marker_idx;
 }
 
 void AudioTimingControllerDialogue::OnSelectedSetChanged()
 {
-	RegenerateSelectedLines();
-	RegenerateInactiveLines();
+    RegenerateSelectedLines();
+    RegenerateInactiveLines();
 }
 
-void AudioTimingControllerDialogue::OnFileChanged(int type) {
-	if (type & AssFile::COMMIT_DIAG_TIME)
-		Revert();
-	else if (type & AssFile::COMMIT_DIAG_ADDREM)
-		RegenerateInactiveLines();
+void AudioTimingControllerDialogue::OnFileChanged(int type)
+{
+    if (type & AssFile::COMMIT_DIAG_TIME)
+        Revert();
+    else if (type & AssFile::COMMIT_DIAG_ADDREM)
+        RegenerateInactiveLines();
 }
 
 void AudioTimingControllerDialogue::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
 {
-	active_line.GetStyleRange(&ranges);
-	for (auto const& line : selected_lines)
-		line.GetStyleRange(&ranges);
-	for (auto const& line : inactive_lines)
-		line.GetStyleRange(&ranges);
+    active_line.GetStyleRange(&ranges);
+    for (auto const &line : selected_lines)
+        line.GetStyleRange(&ranges);
+    for (auto const &line : inactive_lines)
+        line.GetStyleRange(&ranges);
 }
 
 void AudioTimingControllerDialogue::Next(NextMode mode)
 {
-	if (mode == TIMING_UNIT)
-	{
-		context->selectionController->NextLine();
-		return;
-	}
+    if (mode == TIMING_UNIT) {
+        context->selectionController->NextLine();
+        return;
+    }
 
-	int new_end_ms = *active_line.GetRightMarker();
+    int new_end_ms = *active_line.GetRightMarker();
 
-	cmd::call("grid/line/next/create", context);
+    cmd::call("grid/line/next/create", context);
 
-	if (mode == LINE_RESET_DEFAULT || active_line.GetLine()->End == 0) {
-		const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
-		// Setting right first here so that they don't get switched and the
-		// same marker gets set twice
-		active_line.GetRightMarker()->SetPosition(new_end_ms + default_duration);
-		active_line.GetLeftMarker()->SetPosition(new_end_ms);
-		boost::sort(markers, marker_ptr_cmp());
-		modified_lines.insert(&active_line);
-		UpdateSelection();
-	}
+    if (mode == LINE_RESET_DEFAULT || active_line.GetLine()->End == 0) {
+        const int default_duration = OPT_GET("Timing/Default Duration")->GetInt();
+        // Setting right first here so that they don't get switched and the
+        // same marker gets set twice
+        active_line.GetRightMarker()->SetPosition(new_end_ms + default_duration);
+        active_line.GetLeftMarker()->SetPosition(new_end_ms);
+        boost::sort(markers, marker_ptr_cmp());
+        modified_lines.insert(&active_line);
+        UpdateSelection();
+    }
 }
 
 void AudioTimingControllerDialogue::Prev()
 {
-	context->selectionController->PrevLine();
+    context->selectionController->PrevLine();
 }
 
 void AudioTimingControllerDialogue::DoCommit(bool user_triggered)
 {
-	// Store back new times
-	if (modified_lines.size())
-	{
-		for (auto line : modified_lines)
-			line->Apply();
-
-		commit_connection.Block();
-		if (user_triggered)
-		{
-			context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
-			commit_id = -1; // never coalesce with a manually triggered commit
-		}
-		else
-		{
-			AssDialogue *amend = modified_lines.size() == 1 ? (*modified_lines.begin())->GetLine() : nullptr;
-			commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, amend);
-		}
-
-		commit_connection.Unblock();
-		modified_lines.clear();
-	}
+    // Store back new times
+    if (modified_lines.size()) {
+        for (auto line : modified_lines)
+            line->Apply();
+
+        commit_connection.Block();
+        if (user_triggered) {
+            context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
+            commit_id = -1; // never coalesce with a manually triggered commit
+        }
+        else {
+            AssDialogue *amend = modified_lines.size() == 1 ? (*modified_lines.begin())->GetLine() : nullptr;
+            commit_id = context->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME, commit_id, amend);
+        }
+
+        commit_connection.Unblock();
+        modified_lines.clear();
+    }
 }
 
 void AudioTimingControllerDialogue::Revert()
 {
-	commit_id = -1;
-	tap_marker_idx = 0;
-
-	if (AssDialogue *line = context->selectionController->GetActiveLine())
-	{
-		modified_lines.clear();
-		if (active_line.SetLine(line))
-		{
-			AnnounceUpdatedPrimaryRange();
-			if (inactive_line_mode->GetInt() == 0)
-				AnnounceUpdatedStyleRanges();
-			AnnounceUpdatedTapMarker();
-		}
-		else
-		{
-			modified_lines.insert(&active_line);
-		}
-	}
-
-	RegenerateInactiveLines();
-	RegenerateSelectedLines();
+    commit_id = -1;
+    tap_marker_idx = 0;
+
+    if (AssDialogue *line = context->selectionController->GetActiveLine()) {
+        modified_lines.clear();
+        if (active_line.SetLine(line)) {
+            AnnounceUpdatedPrimaryRange();
+            if (inactive_line_mode->GetInt() == 0)
+                AnnounceUpdatedStyleRanges();
+            AnnounceUpdatedTapMarker();
+        }
+        else {
+            modified_lines.insert(&active_line);
+        }
+    }
+
+    RegenerateInactiveLines();
+    RegenerateSelectedLines();
 }
 
 void AudioTimingControllerDialogue::AddLeadIn()
 {
-	DialogueTimingMarker *m = active_line.GetLeftMarker();
-	SetMarkers({ m }, *m - OPT_GET("Audio/Lead/IN")->GetInt(), 0);
+    DialogueTimingMarker *m = active_line.GetLeftMarker();
+    SetMarkers({ m }, *m - OPT_GET("Audio/Lead/IN")->GetInt(), 0);
 }
 
 void AudioTimingControllerDialogue::AddLeadOut()
 {
-	DialogueTimingMarker *m = active_line.GetRightMarker();
-	SetMarkers({ m }, *m + OPT_GET("Audio/Lead/OUT")->GetInt(), 0);
+    DialogueTimingMarker *m = active_line.GetRightMarker();
+    SetMarkers({ m }, *m + OPT_GET("Audio/Lead/OUT")->GetInt(), 0);
 }
 
-void AudioTimingControllerDialogue::ModifyLength(int delta, bool) {
-	DialogueTimingMarker *m = active_line.GetRightMarker();
-	SetMarkers({ m },
-		std::max<int>(*m + delta * 10, *active_line.GetLeftMarker()), 0);
+void AudioTimingControllerDialogue::ModifyLength(int delta, bool)
+{
+    DialogueTimingMarker *m = active_line.GetRightMarker();
+    SetMarkers({ m },
+               std::max<int>(*m + delta * 10, *active_line.GetLeftMarker()), 0);
 }
 
-void AudioTimingControllerDialogue::ModifyStart(int delta) {
-	DialogueTimingMarker *m = active_line.GetLeftMarker();
-	SetMarkers({ m },
-		std::min<int>(*m + delta * 10, *active_line.GetRightMarker()), 0);
+void AudioTimingControllerDialogue::ModifyStart(int delta)
+{
+    DialogueTimingMarker *m = active_line.GetLeftMarker();
+    SetMarkers({ m },
+               std::min<int>(*m + delta * 10, *active_line.GetRightMarker()), 0);
 }
 
-void AudioTimingControllerDialogue::MoveTapMarker(int ms) {
-	// Fix rounding error
-	ms = (ms + 5) / 10 * 10;
-
-	DialogueTimingMarker *left = active_line.GetLeftMarker();
-	DialogueTimingMarker *right = active_line.GetRightMarker();
-
-	clicked_ms = INT_MIN;
-	if (tap_marker_idx == 0) {
-		// Moving left marker (start time of the line)
-		if (ms > *right) SetMarkers({ right }, ms, 0);
-		SetMarkers({ left }, ms, 0);
-	}
-	else {
-		// Moving right marker (end time of the line)
-		if (ms < *left) SetMarkers({ left }, ms, 0);
-		SetMarkers({ right }, ms, 0);
-	}
+void AudioTimingControllerDialogue::MoveTapMarker(int ms)
+{
+    // Fix rounding error
+    ms = (ms + 5) / 10 * 10;
+
+    DialogueTimingMarker *left = active_line.GetLeftMarker();
+    DialogueTimingMarker *right = active_line.GetRightMarker();
+
+    clicked_ms = INT_MIN;
+    if (tap_marker_idx == 0) {
+        // Moving left marker (start time of the line)
+        if (ms > *right) SetMarkers({ right }, ms, 0);
+        SetMarkers({ left }, ms, 0);
+    }
+    else {
+        // Moving right marker (end time of the line)
+        if (ms < *left) SetMarkers({ left }, ms, 0);
+        SetMarkers({ right }, ms, 0);
+    }
+}
+
+bool AudioTimingControllerDialogue::NextTapMarker()
+{
+    if (tap_marker_idx == 0) {
+        tap_marker_idx = 1;
+        AnnounceUpdatedTapMarker();
+        return true;
+    }
+    return false;
 }
 
-bool AudioTimingControllerDialogue::NextTapMarker() {
-	if (tap_marker_idx == 0) {
-		tap_marker_idx = 1;
-		AnnounceUpdatedTapMarker();
-		return true;
-	}
-	return false;
+bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity, bool alt_down) const
+{
+    assert(sensitivity >= 0);
+    return alt_down || active_line.ContainsMarker(TimeRange(ms - sensitivity, ms + sensitivity));
 }
 
-bool AudioTimingControllerDialogue::IsNearbyMarker(int ms, int sensitivity, bool alt_down) const
+std::vector<AudioMarker *> AudioTimingControllerDialogue::OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range)
+{
+    assert(sensitivity >= 0);
+    assert(snap_range >= 0);
+
+    std::vector<AudioMarker *> ret;
+
+    clicked_ms = INT_MIN;
+    if (alt_down) {
+        clicked_ms = ms;
+        active_line.GetMarkers(&ret);
+        for (auto const &line : selected_lines)
+            line.GetMarkers(&ret);
+        return ret;
+    }
+
+    DialogueTimingMarker *left = active_line.GetLeftMarker();
+    DialogueTimingMarker *right = active_line.GetRightMarker();
+
+    int dist_l = tabs(*left - ms);
+    int dist_r = tabs(*right - ms);
+
+    if (dist_l > sensitivity && dist_r > sensitivity) {
+        // Clicked far from either marker:
+        // Insta-set the left marker to the clicked position and return the
+        // right as the dragged one, such that if the user does start dragging,
+        // he will create a new selection from scratch
+        std::vector<AudioMarker *> jump = GetLeftMarkers();
+        ret = drag_timing->GetBool() ? GetRightMarkers() : jump;
+        // Get ret before setting as setting may swap left/right
+        SetMarkers(jump, ms, snap_range);
+        // Also change tap marker to left marker
+        tap_marker_idx = 0;
+        return ret;
+    }
+
+    DialogueTimingMarker *clicked = dist_l <= dist_r ? left : right;
+
+    if (ctrl_down) {
+        // The use of GetPosition here is important, as otherwise it'll start
+        // after lines ending at the same time as the active line begins
+        auto it = boost::lower_bound(markers, clicked->GetPosition(), marker_ptr_cmp());
+        for (; it != markers.end() && !(*clicked < **it); ++it)
+            ret.push_back(*it);
+    }
+    else
+        ret.push_back(clicked);
+
+    // Left-click within drag range should still move the left marker to the
+    // clicked position, but not the right marker
+    if (clicked == left) {
+        SetMarkers(ret, ms, snap_range);
+    }
+
+    // Also change tap marker
+    if (clicked == left) {
+        tap_marker_idx = 0;
+    }
+    else {
+        tap_marker_idx = 1;
+    }
+
+    return ret;
+}
+
+std::vector<AudioMarker *> AudioTimingControllerDialogue::OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range)
 {
-	assert(sensitivity >= 0);
-	return alt_down || active_line.ContainsMarker(TimeRange(ms-sensitivity, ms+sensitivity));
-}
-
-std::vector<AudioMarker*> AudioTimingControllerDialogue::OnLeftClick(int ms, bool ctrl_down, bool alt_down, int sensitivity, int snap_range)
-{
-	assert(sensitivity >= 0);
-	assert(snap_range >= 0);
-
-	std::vector<AudioMarker*> ret;
-
-	clicked_ms = INT_MIN;
-	if (alt_down)
-	{
-		clicked_ms = ms;
-		active_line.GetMarkers(&ret);
-		for (auto const& line : selected_lines)
-			line.GetMarkers(&ret);
-		return ret;
-	}
-
-	DialogueTimingMarker *left = active_line.GetLeftMarker();
-	DialogueTimingMarker *right = active_line.GetRightMarker();
-
-	int dist_l = tabs(*left - ms);
-	int dist_r = tabs(*right - ms);
-
-	if (dist_l > sensitivity && dist_r > sensitivity)
-	{
-		// Clicked far from either marker:
-		// Insta-set the left marker to the clicked position and return the
-		// right as the dragged one, such that if the user does start dragging,
-		// he will create a new selection from scratch
-		std::vector<AudioMarker*> jump = GetLeftMarkers();
-		ret = drag_timing->GetBool() ? GetRightMarkers() : jump;
-		// Get ret before setting as setting may swap left/right
-		SetMarkers(jump, ms, snap_range);
-		// Also change tap marker to left marker
-		tap_marker_idx = 0;
-		return ret;
-	}
-
-	DialogueTimingMarker *clicked = dist_l <= dist_r ? left : right;
-
-	if (ctrl_down)
-	{
-		// The use of GetPosition here is important, as otherwise it'll start
-		// after lines ending at the same time as the active line begins
-		auto it = boost::lower_bound(markers, clicked->GetPosition(), marker_ptr_cmp());
-		for (; it != markers.end() && !(*clicked < **it); ++it)
-			ret.push_back(*it);
-	}
-	else
-		ret.push_back(clicked);
-
-	// Left-click within drag range should still move the left marker to the
-	// clicked position, but not the right marker
-	if (clicked == left) {
-		SetMarkers(ret, ms, snap_range);
-	}
-
-	// Also change tap marker
-	if (clicked == left) {
-		tap_marker_idx = 0;
-	}
-	else {
-		tap_marker_idx = 1;
-	}
-
-	return ret;
-}
-
-std::vector<AudioMarker*> AudioTimingControllerDialogue::OnRightClick(int ms, bool ctrl_down, int sensitivity, int snap_range)
-{
-	if (ctrl_down) {
-		// Ctrl-right-click: play audio
-		context->audioController->PlayToEnd(ms);
-		return {};
-	}
-	else {
-		// Normal right-click: move right marker
-		clicked_ms = INT_MIN;
-		std::vector<AudioMarker*> ret = GetRightMarkers();
-		SetMarkers(ret, ms, snap_range);
-		tap_marker_idx = 1;
-		return ret;
-	}
-}
-
-void AudioTimingControllerDialogue::OnMarkerDrag(std::vector<AudioMarker*> const& markers, int new_position, int snap_range)
-{
-	SetMarkers(markers, new_position, snap_range);
+    if (ctrl_down) {
+        // Ctrl-right-click: play audio
+        context->audioController->PlayToEnd(ms);
+        return {};
+    }
+    else {
+        // Normal right-click: move right marker
+        clicked_ms = INT_MIN;
+        std::vector<AudioMarker *> ret = GetRightMarkers();
+        SetMarkers(ret, ms, snap_range);
+        tap_marker_idx = 1;
+        return ret;
+    }
+}
+
+void AudioTimingControllerDialogue::OnMarkerDrag(std::vector<AudioMarker *> const &markers, int new_position, int snap_range)
+{
+    SetMarkers(markers, new_position, snap_range);
 }
 
 void AudioTimingControllerDialogue::UpdateSelection()
 {
-	AnnounceUpdatedPrimaryRange();
-	AnnounceUpdatedStyleRanges();
+    AnnounceUpdatedPrimaryRange();
+    AnnounceUpdatedStyleRanges();
+}
+
+void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker *> const &upd_markers, int ms, int snap_range)
+{
+    if (upd_markers.empty()) return;
+
+    int shift = clicked_ms != INT_MIN ? ms - clicked_ms : 0;
+    if (shift) clicked_ms = ms;
+
+    // Since we're moving markers, the sorted list of markers will need to be
+    // resorted. To avoid resorting the entire thing, find the subrange that
+    // is effected.
+    int min_ms = ms;
+    int max_ms = ms;
+    for (AudioMarker *upd_marker : upd_markers) {
+        auto marker = static_cast<DialogueTimingMarker *>(upd_marker);
+        if (shift < 0) {
+            min_ms = std::min<int>(*marker + shift, min_ms);
+            max_ms = std::max<int>(*marker, max_ms);
+        }
+        else {
+            min_ms = std::min<int>(*marker, min_ms);
+            max_ms = std::max<int>(*marker + shift, max_ms);
+        }
+    }
+
+    auto begin = boost::lower_bound(markers, min_ms, marker_ptr_cmp());
+    auto end = upper_bound(begin, markers.end(), max_ms, marker_ptr_cmp());
+
+    // Update the markers
+    for (auto upd_marker : upd_markers) {
+        auto marker = static_cast<DialogueTimingMarker *>(upd_marker);
+        marker->SetPosition(clicked_ms != INT_MIN ? *marker + shift : ms);
+        modified_lines.insert(marker->GetLine());
+    }
+
+    int snap = SnapMarkers(snap_range, upd_markers);
+    if (clicked_ms != INT_MIN)
+        clicked_ms += snap;
+
+    // Resort the range
+    sort(begin, end, marker_ptr_cmp());
+
+    if (auto_commit->GetBool()) DoCommit(false);
+    UpdateSelection();
+
+    AnnounceMarkerMoved();
 }
 
-void AudioTimingControllerDialogue::SetMarkers(std::vector<AudioMarker*> const& upd_markers, int ms, int snap_range)
+void AudioTimingControllerDialogue::RegenerateInactiveLines()
 {
-	if (upd_markers.empty()) return;
+    using pred = bool(*)(AssDialogue const &);
+    auto predicate = inactive_line_comments->GetBool()
+    ? static_cast<pred>([](AssDialogue const &) { return true; })
+        : static_cast<pred>([](AssDialogue const & d) { return !d.Comment; });
+
+    bool was_empty = inactive_lines.empty();
+    inactive_lines.clear();
+
+    auto const &sel = context->selectionController->GetSelectedSet();
+
+    switch (int mode = inactive_line_mode->GetInt()) {
+    case 1: // Previous line only
+    case 2: // Previous and next lines
+        if (AssDialogue *line = context->selectionController->GetActiveLine()) {
+            auto current_line = context->ass->iterator_to(*line);
+            if (current_line == context->ass->Events.end())
+                break;
+
+            if (current_line != context->ass->Events.begin()) {
+                auto prev = current_line;
+                while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
+                if (predicate(*prev))
+                    AddInactiveLine(sel, &*prev);
+            }
+
+            if (mode == 2) {
+                auto next = std::find_if(++current_line, context->ass->Events.end(), predicate);
+                if (next != context->ass->Events.end())
+                    AddInactiveLine(sel, &*next);
+            }
+        }
+        break;
+    case 3: { // All inactive lines
+        AssDialogue *active_line = context->selectionController->GetActiveLine();
+        for (auto &line : context->ass->Events) {
+            if (&line != active_line && predicate(line))
+                AddInactiveLine(sel, &line);
+        }
+        break;
+    }
+    default:
+        if (was_empty) {
+            RegenerateMarkers();
+            return;
+        }
+    }
+
+    AnnounceUpdatedStyleRanges();
+
+    RegenerateMarkers();
+}
+
+void AudioTimingControllerDialogue::AddInactiveLine(Selection const &sel, AssDialogue *diag)
+{
+    if (sel.count(diag)) return;
 
-	int shift = clicked_ms != INT_MIN ? ms - clicked_ms : 0;
-	if (shift) clicked_ms = ms;
+    inactive_lines.emplace_back(AudioStyle_Inactive, &style_inactive, &style_inactive);
+    inactive_lines.back().SetLine(diag);
+}
 
-	// Since we're moving markers, the sorted list of markers will need to be
-	// resorted. To avoid resorting the entire thing, find the subrange that
-	// is effected.
-	int min_ms = ms;
-	int max_ms = ms;
-	for (AudioMarker *upd_marker : upd_markers)
-	{
-		auto marker = static_cast<DialogueTimingMarker*>(upd_marker);
-		if (shift < 0) {
-			min_ms = std::min<int>(*marker + shift, min_ms);
-			max_ms = std::max<int>(*marker, max_ms);
-		}
-		else {
-			min_ms = std::min<int>(*marker, min_ms);
-			max_ms = std::max<int>(*marker + shift, max_ms);
-		}
-	}
+void AudioTimingControllerDialogue::RegenerateSelectedLines()
+{
+    bool was_empty = selected_lines.empty();
+    selected_lines.clear();
 
-	auto begin = boost::lower_bound(markers, min_ms, marker_ptr_cmp());
-	auto end = upper_bound(begin, markers.end(), max_ms, marker_ptr_cmp());
+    AssDialogue *active = context->selectionController->GetActiveLine();
+    for (auto line : context->selectionController->GetSelectedSet()) {
+        if (line == active) continue;
 
-	// Update the markers
-	for (auto upd_marker : upd_markers)
-	{
-		auto marker = static_cast<DialogueTimingMarker*>(upd_marker);
-		marker->SetPosition(clicked_ms != INT_MIN ? *marker + shift : ms);
-		modified_lines.insert(marker->GetLine());
-	}
+        selected_lines.emplace_back(AudioStyle_Selected, &style_inactive, &style_inactive);
+        selected_lines.back().SetLine(line);
+    }
 
-	int snap = SnapMarkers(snap_range, upd_markers);
-	if (clicked_ms != INT_MIN)
-		clicked_ms += snap;
+    if (!selected_lines.empty() || !was_empty) {
+        AnnounceUpdatedStyleRanges();
+        RegenerateMarkers();
+    }
+}
 
-	// Resort the range
-	sort(begin, end, marker_ptr_cmp());
+void AudioTimingControllerDialogue::RegenerateMarkers()
+{
+    markers.clear();
 
-	if (auto_commit->GetBool()) DoCommit(false);
-	UpdateSelection();
+    active_line.GetMarkers(&markers);
+    for (auto const &line : selected_lines)
+        line.GetMarkers(&markers);
+    for (auto const &line : inactive_lines)
+        line.GetMarkers(&markers);
+    boost::sort(markers, marker_ptr_cmp());
 
-	AnnounceMarkerMoved();
+    AnnounceMarkerMoved();
 }
 
-void AudioTimingControllerDialogue::RegenerateInactiveLines()
+std::vector<AudioMarker *> AudioTimingControllerDialogue::GetLeftMarkers()
 {
-	using pred = bool(*)(AssDialogue const&);
-	auto predicate = inactive_line_comments->GetBool()
-		? static_cast<pred>([](AssDialogue const&) { return true; })
-		: static_cast<pred>([](AssDialogue const& d) { return !d.Comment; });
-
-	bool was_empty = inactive_lines.empty();
-	inactive_lines.clear();
-
-	auto const& sel = context->selectionController->GetSelectedSet();
-
-	switch (int mode = inactive_line_mode->GetInt())
-	{
-	case 1: // Previous line only
-	case 2: // Previous and next lines
-		if (AssDialogue *line = context->selectionController->GetActiveLine())
-		{
-			auto current_line = context->ass->iterator_to(*line);
-			if (current_line == context->ass->Events.end())
-				break;
-
-			if (current_line != context->ass->Events.begin())
-			{
-				auto prev = current_line;
-				while (--prev != context->ass->Events.begin() && !predicate(*prev)) ;
-				if (predicate(*prev))
-					AddInactiveLine(sel, &*prev);
-			}
-
-			if (mode == 2)
-			{
-				auto next = std::find_if(++current_line, context->ass->Events.end(), predicate);
-				if (next != context->ass->Events.end())
-					AddInactiveLine(sel, &*next);
-			}
-		}
-		break;
-	case 3: // All inactive lines
-	{
-		AssDialogue *active_line = context->selectionController->GetActiveLine();
-		for (auto& line : context->ass->Events)
-		{
-			if (&line != active_line && predicate(line))
-				AddInactiveLine(sel, &line);
-		}
-		break;
-	}
-	default:
-		if (was_empty)
-		{
-			RegenerateMarkers();
-			return;
-		}
-	}
-
-	AnnounceUpdatedStyleRanges();
-
-	RegenerateMarkers();
-}
-
-void AudioTimingControllerDialogue::AddInactiveLine(Selection const& sel, AssDialogue *diag)
-{
-	if (sel.count(diag)) return;
-
-	inactive_lines.emplace_back(AudioStyle_Inactive, &style_inactive, &style_inactive);
-	inactive_lines.back().SetLine(diag);
+    std::vector<AudioMarker *> ret;
+    ret.reserve(selected_lines.size() + 1);
+    ret.push_back(active_line.GetLeftMarker());
+    for (auto &line : selected_lines)
+        ret.push_back(line.GetLeftMarker());
+    return ret;
 }
 
-void AudioTimingControllerDialogue::RegenerateSelectedLines()
+std::vector<AudioMarker *> AudioTimingControllerDialogue::GetRightMarkers()
 {
-	bool was_empty = selected_lines.empty();
-	selected_lines.clear();
-
-	AssDialogue *active = context->selectionController->GetActiveLine();
-	for (auto line : context->selectionController->GetSelectedSet())
-	{
-		if (line == active) continue;
-
-		selected_lines.emplace_back(AudioStyle_Selected, &style_inactive, &style_inactive);
-		selected_lines.back().SetLine(line);
-	}
-
-	if (!selected_lines.empty() || !was_empty)
-	{
-		AnnounceUpdatedStyleRanges();
-		RegenerateMarkers();
-	}
+    std::vector<AudioMarker *> ret;
+    ret.reserve(selected_lines.size() + 1);
+    ret.push_back(active_line.GetRightMarker());
+    for (auto &line : selected_lines)
+        ret.push_back(line.GetRightMarker());
+    return ret;
 }
 
-void AudioTimingControllerDialogue::RegenerateMarkers()
+int AudioTimingControllerDialogue::SnapMarkers(int snap_range, std::vector<AudioMarker *> const &active) const
 {
-	markers.clear();
-
-	active_line.GetMarkers(&markers);
-	for (auto const& line : selected_lines)
-		line.GetMarkers(&markers);
-	for (auto const& line : inactive_lines)
-		line.GetMarkers(&markers);
-	boost::sort(markers, marker_ptr_cmp());
-
-	AnnounceMarkerMoved();
-}
-
-std::vector<AudioMarker*> AudioTimingControllerDialogue::GetLeftMarkers()
-{
-	std::vector<AudioMarker*> ret;
-	ret.reserve(selected_lines.size() + 1);
-	ret.push_back(active_line.GetLeftMarker());
-	for (auto& line : selected_lines)
-		ret.push_back(line.GetLeftMarker());
-	return ret;
-}
-
-std::vector<AudioMarker*> AudioTimingControllerDialogue::GetRightMarkers()
-{
-	std::vector<AudioMarker*> ret;
-	ret.reserve(selected_lines.size() + 1);
-	ret.push_back(active_line.GetRightMarker());
-	for (auto& line : selected_lines)
-		ret.push_back(line.GetRightMarker());
-	return ret;
-}
-
-int AudioTimingControllerDialogue::SnapMarkers(int snap_range, std::vector<AudioMarker*> const& active) const
-{
-	if (snap_range <= 0 || active.empty()) return 0;
-
-	auto marker_range = [&] {
-		int front = active.front()->GetPosition();
-		int min = front;
-		int max = front;
-		for (auto m : active)
-		{
-			auto pos = m->GetPosition();
-			if (pos < min) min = pos;
-			if (pos > max) max = pos;
-		}
-		return TimeRange{min - snap_range, max + snap_range};
-	}();
-
-	std::vector<int> inactive_markers;
-	inactive_markers.reserve(inactive_lines.size() * 2 + selected_lines.size() * 2 + 2 - active.size());
-
-	// Add a marker to the set to check for snaps if it's in the right time
-	// range, isn't at the same place as a marker already in the set, and isn't
-	// one of the markers being moved
-	auto add_inactive = [&](const DialogueTimingMarker *m, bool check)
-	{
-		if (!marker_range.contains(*m)) return;
-		if (!inactive_markers.empty() && inactive_markers.back() == *m) return;
-		if (check && boost::find(active, m) != end(active)) return;
-		inactive_markers.push_back(*m);
-	};
-
-	bool moving_entire_selection = clicked_ms != INT_MIN;
-	for (auto const& line : inactive_lines)
-	{
-		// If we're alt-dragging the entire selection, there can't be any
-		// markers from inactive lines in the active set, so no need to check
-		// for them
-		add_inactive(line.GetLeftMarker(), !moving_entire_selection);
-		add_inactive(line.GetRightMarker(), !moving_entire_selection);
-	}
-
-	// And similarly, there can't be any inactive markers from selected lines
-	if (!moving_entire_selection)
-	{
-		for (auto const& line : selected_lines)
-		{
-			add_inactive(line.GetLeftMarker(), true);
-			add_inactive(line.GetRightMarker(), true);
-		}
-		add_inactive(active_line.GetLeftMarker(), true);
-		add_inactive(active_line.GetRightMarker(), true);
-	}
-
-	int snap_distance = INT_MAX;
-	auto check = [&](int marker, int pos)
-	{
-		auto dist = marker - pos;
-		if (tabs(dist) < tabs(snap_distance))
-			snap_distance = dist;
-	};
-
-	int prev = -1;
-	AudioMarkerVector snap_markers;
-	for (const auto active_marker : active)
-	{
-		auto pos = active_marker->GetPosition();
-		if (pos == prev) continue;
-
-		snap_markers.clear();
-		TimeRange range(pos - snap_range, pos + snap_range);
-		keyframes_provider.GetMarkers(range, snap_markers);
-		video_position_provider.GetMarkers(range, snap_markers);
-
-		for (const auto marker : snap_markers)
-		{
-			check(marker->GetPosition(), pos);
-			if (snap_distance == 0) return 0;
-		}
-
-		for (auto it = boost::lower_bound(inactive_markers, range.begin()); it != end(inactive_markers); ++it)
-		{
-			check(*it, pos);
-			if (snap_distance == 0) return 0;
-			if (*it > pos) break;
-		}
-	}
-
-	if (tabs(snap_distance) > snap_range)
-		return 0;
-
-	for (auto m : active)
-		static_cast<DialogueTimingMarker *>(m)->SetPosition(m->GetPosition() + snap_distance);
-	return snap_distance;
+    if (snap_range <= 0 || active.empty()) return 0;
+
+    auto marker_range = [&] {
+        int front = active.front()->GetPosition();
+        int min = front;
+        int max = front;
+        for (auto m : active)
+        {
+            auto pos = m->GetPosition();
+            if (pos < min) min = pos;
+            if (pos > max) max = pos;
+        }
+        return TimeRange{min - snap_range, max + snap_range};
+    }();
+
+    std::vector<int> inactive_markers;
+    inactive_markers.reserve(inactive_lines.size() * 2 + selected_lines.size() * 2 + 2 - active.size());
+
+    // Add a marker to the set to check for snaps if it's in the right time
+    // range, isn't at the same place as a marker already in the set, and isn't
+    // one of the markers being moved
+    auto add_inactive = [&](const DialogueTimingMarker * m, bool check) {
+        if (!marker_range.contains(*m)) return;
+        if (!inactive_markers.empty() && inactive_markers.back() == *m) return;
+        if (check && boost::find(active, m) != end(active)) return;
+        inactive_markers.push_back(*m);
+    };
+
+    bool moving_entire_selection = clicked_ms != INT_MIN;
+    for (auto const &line : inactive_lines) {
+        // If we're alt-dragging the entire selection, there can't be any
+        // markers from inactive lines in the active set, so no need to check
+        // for them
+        add_inactive(line.GetLeftMarker(), !moving_entire_selection);
+        add_inactive(line.GetRightMarker(), !moving_entire_selection);
+    }
+
+    // And similarly, there can't be any inactive markers from selected lines
+    if (!moving_entire_selection) {
+        for (auto const &line : selected_lines) {
+            add_inactive(line.GetLeftMarker(), true);
+            add_inactive(line.GetRightMarker(), true);
+        }
+        add_inactive(active_line.GetLeftMarker(), true);
+        add_inactive(active_line.GetRightMarker(), true);
+    }
+
+    int snap_distance = INT_MAX;
+    auto check = [&](int marker, int pos) {
+        auto dist = marker - pos;
+        if (tabs(dist) < tabs(snap_distance))
+            snap_distance = dist;
+    };
+
+    int prev = -1;
+    AudioMarkerVector snap_markers;
+    for (const auto active_marker : active) {
+        auto pos = active_marker->GetPosition();
+        if (pos == prev) continue;
+
+        snap_markers.clear();
+        TimeRange range(pos - snap_range, pos + snap_range);
+        keyframes_provider.GetMarkers(range, snap_markers);
+        video_position_provider.GetMarkers(range, snap_markers);
+
+        for (const auto marker : snap_markers) {
+            check(marker->GetPosition(), pos);
+            if (snap_distance == 0) return 0;
+        }
+
+        for (auto it = boost::lower_bound(inactive_markers, range.begin()); it != end(inactive_markers); ++it) {
+            check(*it, pos);
+            if (snap_distance == 0) return 0;
+            if (*it > pos) break;
+        }
+    }
+
+    if (tabs(snap_distance) > snap_range)
+        return 0;
+
+    for (auto m : active)
+        static_cast<DialogueTimingMarker *>(m)->SetPosition(m->GetPosition() + snap_distance);
+    return snap_distance;
 }
 
 } // namespace {
 
 std::unique_ptr<AudioTimingController> CreateDialogueTimingController(agi::Context *c)
 {
-	return agi::make_unique<AudioTimingControllerDialogue>(c);
+    return agi::make_unique<AudioTimingControllerDialogue>(c);
 }
diff --git a/src/audio_timing_karaoke.cpp b/src/audio_timing_karaoke.cpp
index 5476b8ce0053aa2424904be6808e0796821060ce..35d7f045b91b06a9f8cd121e3ba6dff781da79a8 100644
--- a/src/audio_timing_karaoke.cpp
+++ b/src/audio_timing_karaoke.cpp
@@ -40,27 +40,26 @@
 /// @class KaraokeMarker
 /// @brief AudioMarker implementation for AudioTimingControllerKaraoke
 class KaraokeMarker final : public AudioMarker {
-	int position;
-	Pen *pen = nullptr;
-	FeetStyle style = Feet_None;
+    int position;
+    Pen *pen = nullptr;
+    FeetStyle style = Feet_None;
 public:
 
-	int GetPosition() const override { return position; }
-	wxPen GetStyle() const override { return *pen; }
-	FeetStyle GetFeet() const override { return style; }
+    int GetPosition() const override { return position; }
+    wxPen GetStyle() const override { return *pen; }
+    FeetStyle GetFeet() const override { return style; }
 
-	void Move(int new_pos) { position = new_pos; }
+    void Move(int new_pos) { position = new_pos; }
 
-	KaraokeMarker(int position) : position(position) { }
+    KaraokeMarker(int position) : position(position) { }
 
-	KaraokeMarker(int position, Pen *pen, FeetStyle style)
-	: position(position)
-	, pen(pen)
-	, style(style)
-	{
-	}
+    KaraokeMarker(int position, Pen *pen, FeetStyle style)
+        : position(position)
+        , pen(pen)
+        , style(style) {
+    }
 
-	operator int() const { return position; }
+    operator int() const { return position; }
 };
 
 /// @class AudioTimingControllerKaraoke
@@ -72,522 +71,553 @@ public:
 /// This does not support \kt, as it inherently requires that the end time of
 /// one syllable be the same as the start time of the next one.
 class AudioTimingControllerKaraoke final : public AudioTimingController {
-	std::vector<agi::signal::Connection> connections;
-	agi::signal::Connection& file_changed_slot;
-
-	agi::Context *c;          ///< Project context
-	AssDialogue *active_line; ///< Currently active line
-	AssKaraoke *kara;         ///< Parsed karaoke model provided by karaoke controller
-
-	size_t cur_syl = 0; ///< Index of currently selected syllable in the line
-
-	/// Index of marker serving as tap marker
-	/// For AudioControllerTimingKaraoke:
-	/// - 0 is start marker
-	/// - 1 to markers.size() is a regular syllable marker
-	/// - markers.size() + 1 is end marker
-	size_t tap_marker_idx = 0;
-
-	/// Pen used for the mid-syllable markers
-	Pen separator_pen{"Colour/Audio Display/Syllable Boundaries", "Audio/Line Boundaries Thickness", wxPENSTYLE_DOT};
-	/// Pen used for the start-of-line marker
-	Pen start_pen{"Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness"};
-	/// Pen used for the end-of-line marker
-	Pen end_pen{"Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness"};
-
-	/// Immobile marker for the beginning of the line
-	KaraokeMarker start_marker;
-	/// Immobile marker for the end of the line
-	KaraokeMarker end_marker;
-	/// Mobile markers between each pair of syllables
-	std::vector<KaraokeMarker> markers;
-
-	/// Marker provider for video keyframes
-	AudioMarkerProviderKeyframes keyframes_provider;
-
-	/// Marker provider for video playback position
-	VideoPositionMarkerProvider video_position_provider;
-
-	/// Labels containing the stripped text of each syllable
-	std::vector<AudioLabel> labels;
-
-	 /// Should changes be automatically commited?
-	bool auto_commit = OPT_GET("Audio/Auto/Commit")->GetBool();
-	int commit_id = -1;   ///< Last commit id used for an autocommit
-	bool pending_changes; ///< Are there any pending changes to be committed?
-
-	void DoCommit();
-	void ApplyLead(bool announce_primary);
-	int MoveMarker(KaraokeMarker *marker, int new_position);
-	void MoveStartMarker(int new_position);
-	void MoveEndMarker(int new_position);
-	void CompressMarkers(size_t from, size_t to, int new_position);
-	void AnnounceChanges(bool announce_primary);
+    std::vector<agi::signal::Connection> connections;
+    agi::signal::Connection &file_changed_slot;
+
+    agi::Context *c;          ///< Project context
+    AssDialogue *active_line; ///< Currently active line
+    AssKaraoke *kara;         ///< Parsed karaoke model provided by karaoke controller
+
+    size_t cur_syl = 0; ///< Index of currently selected syllable in the line
+
+    /// Index of marker serving as tap marker
+    /// For AudioControllerTimingKaraoke:
+    /// - 0 is start marker
+    /// - 1 to markers.size() is a regular syllable marker
+    /// - markers.size() + 1 is end marker
+    size_t tap_marker_idx = 0;
+
+    /// Pen used for the mid-syllable markers
+    Pen separator_pen{"Colour/Audio Display/Syllable Boundaries", "Audio/Line Boundaries Thickness", wxPENSTYLE_DOT};
+    /// Pen used for the start-of-line marker
+    Pen start_pen{"Colour/Audio Display/Line boundary Start", "Audio/Line Boundaries Thickness"};
+    /// Pen used for the end-of-line marker
+    Pen end_pen{"Colour/Audio Display/Line boundary End", "Audio/Line Boundaries Thickness"};
+
+    /// Immobile marker for the beginning of the line
+    KaraokeMarker start_marker;
+    /// Immobile marker for the end of the line
+    KaraokeMarker end_marker;
+    /// Mobile markers between each pair of syllables
+    std::vector<KaraokeMarker> markers;
+
+    /// Marker provider for video keyframes
+    AudioMarkerProviderKeyframes keyframes_provider;
+
+    /// Marker provider for video playback position
+    VideoPositionMarkerProvider video_position_provider;
+
+    /// Labels containing the stripped text of each syllable
+    std::vector<AudioLabel> labels;
+
+    /// Should changes be automatically commited?
+    bool auto_commit = OPT_GET("Audio/Auto/Commit")->GetBool();
+    int commit_id = -1;   ///< Last commit id used for an autocommit
+    bool pending_changes; ///< Are there any pending changes to be committed?
+
+    void DoCommit();
+    void ApplyLead(bool announce_primary);
+    int MoveMarker(KaraokeMarker *marker, int new_position);
+    void MoveStartMarker(int new_position);
+    void MoveEndMarker(int new_position);
+    void CompressMarkers(size_t from, size_t to, int new_position);
+    void AnnounceChanges(bool announce_primary);
 
 public:
-	// AudioTimingController implementation
-	void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const override;
-	int GetTapMarkerPosition() const override;
-	size_t GetTapMarkerIndex() const override;
-	wxString GetWarningMessage() const override { return ""; }
-	TimeRange GetIdealVisibleTimeRange() const override;
-	void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const override;
-	TimeRange GetPrimaryPlaybackRange() const override;
-	TimeRange GetActiveLineRange() const override;
-	void GetLabels(const TimeRange &range, std::vector<AudioLabel> &out_labels) const override;
-	void Next(NextMode mode) override;
-	void Prev() override;
-	void Commit() override;
-	void Revert() override;
-	void AddLeadIn() override;
-	void AddLeadOut() override;
-	void ModifyLength(int delta, bool shift_following) override;
-	void ModifyStart(int delta) override;
-	void MoveTapMarker(int ms) override;
-	bool NextTapMarker() override;
-	bool IsNearbyMarker(int ms, int sensitivity, bool) const override;
-	std::vector<AudioMarker*> OnLeftClick(int ms, bool, bool, int sensitivity, int) override;
-	std::vector<AudioMarker*> OnRightClick(int ms, bool ctrl_down, int, int) override;
-	void OnMarkerDrag(std::vector<AudioMarker*> const& marker, int new_position, int) override;
-	std::string GetCurrentSylText() const override;
-	void SetCurrentSylText(std::string new_text) override;
-
-	AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed);
+    // AudioTimingController implementation
+    void GetMarkers(const TimeRange &range, AudioMarkerVector &out_markers) const override;
+    int GetTapMarkerPosition() const override;
+    size_t GetTapMarkerIndex() const override;
+    wxString GetWarningMessage() const override { return ""; }
+    TimeRange GetIdealVisibleTimeRange() const override;
+    void GetRenderingStyles(AudioRenderingStyleRanges &ranges) const override;
+    TimeRange GetPrimaryPlaybackRange() const override;
+    TimeRange GetActiveLineRange() const override;
+    void GetLabels(const TimeRange &range, std::vector<AudioLabel> &out_labels) const override;
+    void Next(NextMode mode) override;
+    void Prev() override;
+    void Commit() override;
+    void Revert() override;
+    void AddLeadIn() override;
+    void AddLeadOut() override;
+    void ModifyLength(int delta, bool shift_following) override;
+    void ModifyStart(int delta) override;
+    void MoveTapMarker(int ms) override;
+    bool NextTapMarker() override;
+    bool IsNearbyMarker(int ms, int sensitivity, bool) const override;
+    std::vector<AudioMarker *> OnLeftClick(int ms, bool, bool, int sensitivity, int) override;
+    std::vector<AudioMarker *> OnRightClick(int ms, bool ctrl_down, int, int) override;
+    void OnMarkerDrag(std::vector<AudioMarker *> const &marker, int new_position, int) override;
+    std::string GetCurrentSylText() const override;
+    void SetCurrentSylText(std::string new_text) override;
+
+    AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection &file_changed);
 };
 
-std::unique_ptr<AudioTimingController> CreateKaraokeTimingController(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed)
+std::unique_ptr<AudioTimingController> CreateKaraokeTimingController(agi::Context *c, AssKaraoke *kara, agi::signal::Connection &file_changed)
 {
-	return agi::make_unique<AudioTimingControllerKaraoke>(c, kara, file_changed);
-}
-
-AudioTimingControllerKaraoke::AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection& file_changed)
-: file_changed_slot(file_changed)
-, c(c)
-, active_line(c->selectionController->GetActiveLine())
-, kara(kara)
-, start_marker(active_line->Start, &start_pen, AudioMarker::Feet_Right)
-, end_marker(active_line->End, &end_pen, AudioMarker::Feet_Left)
-, keyframes_provider(c, "Audio/Display/Draw/Keyframes in Karaoke Mode")
-, video_position_provider(c)
+    return agi::make_unique<AudioTimingControllerKaraoke>(c, kara, file_changed);
+}
+
+AudioTimingControllerKaraoke::AudioTimingControllerKaraoke(agi::Context *c, AssKaraoke *kara, agi::signal::Connection &file_changed)
+    : file_changed_slot(file_changed)
+    , c(c)
+    , active_line(c->selectionController->GetActiveLine())
+    , kara(kara)
+    , start_marker(active_line->Start, &start_pen, AudioMarker::Feet_Right)
+    , end_marker(active_line->End, &end_pen, AudioMarker::Feet_Left)
+    , keyframes_provider(c, "Audio/Display/Draw/Keyframes in Karaoke Mode")
+    , video_position_provider(c)
 {
-	connections.push_back(kara->AddSyllablesChangedListener(&AudioTimingControllerKaraoke::Revert, this));
-	connections.push_back(OPT_SUB("Audio/Auto/Commit", [=](agi::OptionValue const& opt) { auto_commit = opt.GetBool(); }));
+    connections.push_back(kara->AddSyllablesChangedListener(&AudioTimingControllerKaraoke::Revert, this));
+    connections.push_back(OPT_SUB("Audio/Auto/Commit", [ = ](agi::OptionValue const & opt) { auto_commit = opt.GetBool(); }));
 
-	keyframes_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
-	video_position_provider.AddMarkerMovedListener([=]{ AnnounceMarkerMoved(); });
+    keyframes_provider.AddMarkerMovedListener([ = ] { AnnounceMarkerMoved(); });
+    video_position_provider.AddMarkerMovedListener([ = ] { AnnounceMarkerMoved(); });
 
-	Revert();
+    Revert();
 }
 
-void AudioTimingControllerKaraoke::Next(NextMode mode) {
-	// Don't create new lines since it's almost never useful to k-time a line
-	// before dialogue timing it
-	if (mode != TIMING_UNIT)
-		cur_syl = markers.size();
+void AudioTimingControllerKaraoke::Next(NextMode mode)
+{
+    // Don't create new lines since it's almost never useful to k-time a line
+    // before dialogue timing it
+    if (mode != TIMING_UNIT)
+        cur_syl = markers.size();
 
-	++cur_syl;
-	if (cur_syl > markers.size()) {
-		--cur_syl;
-		c->selectionController->NextLine();
-	}
-	else {
-		AnnounceUpdatedPrimaryRange();
-		AnnounceUpdatedStyleRanges();
-	}
+    ++cur_syl;
+    if (cur_syl > markers.size()) {
+        --cur_syl;
+        c->selectionController->NextLine();
+    }
+    else {
+        AnnounceUpdatedPrimaryRange();
+        AnnounceUpdatedStyleRanges();
+    }
 
-	c->audioController->PlayPrimaryRange();
+    c->audioController->PlayPrimaryRange();
 }
 
-void AudioTimingControllerKaraoke::Prev() {
-	if (cur_syl == 0) {
-		AssDialogue *old_line = active_line;
-		c->selectionController->PrevLine();
-		if (old_line != active_line) {
-			cur_syl = markers.size();
-			AnnounceUpdatedPrimaryRange();
-			AnnounceUpdatedStyleRanges();
-		}
-	}
-	else {
-		--cur_syl;
-		AnnounceUpdatedPrimaryRange();
-		AnnounceUpdatedStyleRanges();
-	}
-
-	c->audioController->PlayPrimaryRange();
+void AudioTimingControllerKaraoke::Prev()
+{
+    if (cur_syl == 0) {
+        AssDialogue *old_line = active_line;
+        c->selectionController->PrevLine();
+        if (old_line != active_line) {
+            cur_syl = markers.size();
+            AnnounceUpdatedPrimaryRange();
+            AnnounceUpdatedStyleRanges();
+        }
+    }
+    else {
+        --cur_syl;
+        AnnounceUpdatedPrimaryRange();
+        AnnounceUpdatedStyleRanges();
+    }
+
+    c->audioController->PlayPrimaryRange();
 }
 
 void AudioTimingControllerKaraoke::GetRenderingStyles(AudioRenderingStyleRanges &ranges) const
 {
-	TimeRange sr = GetPrimaryPlaybackRange();
-	ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Primary);
-	ranges.AddRange(start_marker, end_marker, AudioStyle_Selected);
+    TimeRange sr = GetPrimaryPlaybackRange();
+    ranges.AddRange(sr.begin(), sr.end(), AudioStyle_Primary);
+    ranges.AddRange(start_marker, end_marker, AudioStyle_Selected);
 }
 
-TimeRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const {
-	return TimeRange(
-		cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
-		cur_syl < markers.size() ? markers[cur_syl] : end_marker);
+TimeRange AudioTimingControllerKaraoke::GetPrimaryPlaybackRange() const
+{
+    return TimeRange(
+               cur_syl > 0 ? markers[cur_syl - 1] : start_marker,
+               cur_syl < markers.size() ? markers[cur_syl] : end_marker);
 }
 
-TimeRange AudioTimingControllerKaraoke::GetActiveLineRange() const {
-	return TimeRange(start_marker, end_marker);
+TimeRange AudioTimingControllerKaraoke::GetActiveLineRange() const
+{
+    return TimeRange(start_marker, end_marker);
 }
 
-TimeRange AudioTimingControllerKaraoke::GetIdealVisibleTimeRange() const {
-	return GetActiveLineRange();
+TimeRange AudioTimingControllerKaraoke::GetIdealVisibleTimeRange() const
+{
+    return GetActiveLineRange();
 }
 
-void AudioTimingControllerKaraoke::GetMarkers(TimeRange const& range, AudioMarkerVector &out) const {
-	size_t i;
-	for (i = 0; i < markers.size() && markers[i] < range.begin(); ++i) ;
-	for (; i < markers.size() && markers[i] < range.end(); ++i)
-		out.push_back(&markers[i]);
+void AudioTimingControllerKaraoke::GetMarkers(TimeRange const &range, AudioMarkerVector &out) const
+{
+    size_t i;
+    for (i = 0; i < markers.size() && markers[i] < range.begin(); ++i) ;
+    for (; i < markers.size() && markers[i] < range.end(); ++i)
+        out.push_back(&markers[i]);
 
-	if (range.contains(start_marker)) out.push_back(&start_marker);
-	if (range.contains(end_marker)) out.push_back(&end_marker);
+    if (range.contains(start_marker)) out.push_back(&start_marker);
+    if (range.contains(end_marker)) out.push_back(&end_marker);
 
-	keyframes_provider.GetMarkers(range, out);
-	video_position_provider.GetMarkers(range, out);
+    keyframes_provider.GetMarkers(range, out);
+    video_position_provider.GetMarkers(range, out);
 }
 
-int AudioTimingControllerKaraoke::GetTapMarkerPosition() const {
-	assert(tap_marker_idx <= markers.size() + 1);
+int AudioTimingControllerKaraoke::GetTapMarkerPosition() const
+{
+    assert(tap_marker_idx <= markers.size() + 1);
 
-	if (tap_marker_idx == 0) {
-		return start_marker;
-	}
-	else if (tap_marker_idx < markers.size() + 1) {
-		return markers[tap_marker_idx-1];
-	}
-	else {
-		return end_marker;
-	}
+    if (tap_marker_idx == 0) {
+        return start_marker;
+    }
+    else if (tap_marker_idx < markers.size() + 1) {
+        return markers[tap_marker_idx - 1];
+    }
+    else {
+        return end_marker;
+    }
 }
 
-size_t AudioTimingControllerKaraoke::GetTapMarkerIndex() const {
-	assert(tap_marker_idx <= markers.size() + 1);
-	return tap_marker_idx;
+size_t AudioTimingControllerKaraoke::GetTapMarkerIndex() const
+{
+    assert(tap_marker_idx <= markers.size() + 1);
+    return tap_marker_idx;
 }
 
-void AudioTimingControllerKaraoke::DoCommit() {
-	active_line->Text = kara->GetText();
-	file_changed_slot.Block();
-	commit_id = c->ass->Commit(_("karaoke timing"), AssFile::COMMIT_DIAG_TEXT | AssFile::COMMIT_DIAG_TIME, commit_id, active_line);
-	file_changed_slot.Unblock();
-	pending_changes = false;
+void AudioTimingControllerKaraoke::DoCommit()
+{
+    active_line->Text = kara->GetText();
+    file_changed_slot.Block();
+    commit_id = c->ass->Commit(_("karaoke timing"), AssFile::COMMIT_DIAG_TEXT | AssFile::COMMIT_DIAG_TIME, commit_id, active_line);
+    file_changed_slot.Unblock();
+    pending_changes = false;
 }
 
-void AudioTimingControllerKaraoke::Commit() {
-	if (!auto_commit && pending_changes)
-		DoCommit();
+void AudioTimingControllerKaraoke::Commit()
+{
+    if (!auto_commit && pending_changes)
+        DoCommit();
 }
 
-void AudioTimingControllerKaraoke::Revert() {
-	active_line = c->selectionController->GetActiveLine();
+void AudioTimingControllerKaraoke::Revert()
+{
+    active_line = c->selectionController->GetActiveLine();
 
-	cur_syl = 0;
-	commit_id = -1;
-	pending_changes = false;
-	tap_marker_idx = 0;
+    cur_syl = 0;
+    commit_id = -1;
+    pending_changes = false;
+    tap_marker_idx = 0;
 
-	start_marker.Move(active_line->Start);
-	end_marker.Move(active_line->End);
+    start_marker.Move(active_line->Start);
+    end_marker.Move(active_line->End);
 
-	markers.clear();
-	labels.clear();
+    markers.clear();
+    labels.clear();
 
-	markers.reserve(kara->size());
-	labels.reserve(kara->size());
+    markers.reserve(kara->size());
+    labels.reserve(kara->size());
 
-	for (auto it = kara->begin(); it != kara->end(); ++it) {
-		if (it != kara->begin())
-			markers.emplace_back(it->start_time, &separator_pen, AudioMarker::Feet_None);
-		labels.push_back(AudioLabel{to_wx(it->text), TimeRange(it->start_time, it->start_time + it->duration)});
-	}
+    for (auto it = kara->begin(); it != kara->end(); ++it) {
+        if (it != kara->begin())
+            markers.emplace_back(it->start_time, &separator_pen, AudioMarker::Feet_None);
+        labels.push_back(AudioLabel{to_wx(it->text), TimeRange(it->start_time, it->start_time + it->duration)});
+    }
 
-	AnnounceUpdatedPrimaryRange();
-	AnnounceUpdatedStyleRanges();
-	AnnounceMarkerMoved();
-	AnnounceUpdatedTapMarker();
+    AnnounceUpdatedPrimaryRange();
+    AnnounceUpdatedStyleRanges();
+    AnnounceMarkerMoved();
+    AnnounceUpdatedTapMarker();
 }
 
-void AudioTimingControllerKaraoke::AddLeadIn() {
-	start_marker.Move(start_marker - OPT_GET("Audio/Lead/IN")->GetInt());
-	labels.front().range = TimeRange(start_marker, labels.front().range.end());
-	ApplyLead(cur_syl == 0);
+void AudioTimingControllerKaraoke::AddLeadIn()
+{
+    start_marker.Move(start_marker - OPT_GET("Audio/Lead/IN")->GetInt());
+    labels.front().range = TimeRange(start_marker, labels.front().range.end());
+    ApplyLead(cur_syl == 0);
 }
 
-void AudioTimingControllerKaraoke::AddLeadOut() {
-	end_marker.Move(end_marker + OPT_GET("Audio/Lead/OUT")->GetInt());
-	labels.back().range = TimeRange(labels.back().range.begin(), end_marker);
-	ApplyLead(cur_syl == markers.size());
+void AudioTimingControllerKaraoke::AddLeadOut()
+{
+    end_marker.Move(end_marker + OPT_GET("Audio/Lead/OUT")->GetInt());
+    labels.back().range = TimeRange(labels.back().range.begin(), end_marker);
+    ApplyLead(cur_syl == markers.size());
 }
 
-void AudioTimingControllerKaraoke::ApplyLead(bool announce_primary) {
-	active_line->Start = (int)start_marker;
-	active_line->End = (int)end_marker;
-	kara->SetLineTimes(start_marker, end_marker);
-	if (!announce_primary)
-		AnnounceUpdatedStyleRanges();
-	AnnounceChanges(announce_primary);
+void AudioTimingControllerKaraoke::ApplyLead(bool announce_primary)
+{
+    active_line->Start = (int)start_marker;
+    active_line->End = (int)end_marker;
+    kara->SetLineTimes(start_marker, end_marker);
+    if (!announce_primary)
+        AnnounceUpdatedStyleRanges();
+    AnnounceChanges(announce_primary);
 }
 
-void AudioTimingControllerKaraoke::ModifyLength(int delta, bool shift_following) {
-	if (cur_syl == markers.size()) return;
-
-	int cur, end, step;
-	if (delta < 0) {
-		cur = cur_syl;
-		end = shift_following ? markers.size() : cur_syl + 1;
-		step = 1;
-	}
-	else {
-		cur = shift_following ? markers.size() - 1 : cur_syl;
-		end = cur_syl - 1;
-		step = -1;
-	}
-
-	for (; cur != end; cur += step) {
-		MoveMarker(&markers[cur], markers[cur] + delta * 10);
-	}
-	AnnounceChanges(true);
-}
-
-void AudioTimingControllerKaraoke::ModifyStart(int delta) {
-	if (cur_syl == 0) return;
-	MoveMarker(&markers[cur_syl - 1], markers[cur_syl - 1] + delta * 10);
-	AnnounceChanges(true);
-}
-
-void AudioTimingControllerKaraoke::MoveTapMarker(int ms) {
-	// Fix rounding error
-	ms = (ms + 5) / 10 * 10;
-
-	// Get syllable this time falls within
-	const size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
-
-	// Tapping automatically moves all of the necessary markers for the tap
-	// marker to land at the current audio position. Intuitively, it "pushes" or
-	// "compresses" the markers blocking the tap marker's way so they all end up
-	// in the same position. The expectation is that the markers will reach their
-	// proper position once the user finishes tapping to the line
-	if (tap_marker_idx == 0) {
-		// Moving the start time of first syllable (i.e. start time of the line)
-		if (ms > end_marker) MoveEndMarker(ms);
-		if (syl > 0) CompressMarkers(syl-1, 0, ms);
-		MoveStartMarker(ms);
-	}
-	else if (tap_marker_idx < markers.size() + 1) {
-		// Moving the end time of a non-end syllable
-		if (ms < start_marker) MoveStartMarker(ms);
-		else if (ms > end_marker) MoveEndMarker(ms);
-		if (syl < tap_marker_idx) {
-			// Moving markers left
-			CompressMarkers(syl, tap_marker_idx-1, ms);
-		}
-		else {
-			// Moving markers right
-			CompressMarkers(syl-1, tap_marker_idx-1, ms);
-		}
-	}
-	else {
-		// Moving the end time of last syllable (i.e. end time of the line)
-		if (ms < start_marker) MoveStartMarker(ms);
-		if (syl < markers.size()) CompressMarkers(0, markers.size()-1, ms);
-		MoveEndMarker(ms);
-	}
-
-	AnnounceChanges(true);
-}
-
-bool AudioTimingControllerKaraoke::NextTapMarker() {
-	if (tap_marker_idx < markers.size() + 1) {
-		++tap_marker_idx;
-		AnnounceUpdatedTapMarker();
-		return true;
-	}
-	return false;
-}
-
-bool AudioTimingControllerKaraoke::IsNearbyMarker(int ms, int sensitivity, bool) const {
-	TimeRange range(ms - sensitivity, ms + sensitivity);
-	return any_of(markers.begin(), markers.end(), [&](KaraokeMarker const& km) {
-		return range.contains(km);
-	});
+void AudioTimingControllerKaraoke::ModifyLength(int delta, bool shift_following)
+{
+    if (cur_syl == markers.size()) return;
+
+    int cur, end, step;
+    if (delta < 0) {
+        cur = cur_syl;
+        end = shift_following ? markers.size() : cur_syl + 1;
+        step = 1;
+    }
+    else {
+        cur = shift_following ? markers.size() - 1 : cur_syl;
+        end = cur_syl - 1;
+        step = -1;
+    }
+
+    for (; cur != end; cur += step) {
+        MoveMarker(&markers[cur], markers[cur] + delta * 10);
+    }
+    AnnounceChanges(true);
+}
+
+void AudioTimingControllerKaraoke::ModifyStart(int delta)
+{
+    if (cur_syl == 0) return;
+    MoveMarker(&markers[cur_syl - 1], markers[cur_syl - 1] + delta * 10);
+    AnnounceChanges(true);
 }
 
-template<typename Out, typename In>
-static std::vector<Out *> copy_ptrs(In &vec, size_t start, size_t end) {
-	std::vector<Out *> ret;
-	ret.reserve(end - start);
-	for (; start < end; ++start)
-		ret.push_back(&vec[start]);
-	return ret;
+void AudioTimingControllerKaraoke::MoveTapMarker(int ms)
+{
+    // Fix rounding error
+    ms = (ms + 5) / 10 * 10;
+
+    // Get syllable this time falls within
+    const size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
+
+    // Tapping automatically moves all of the necessary markers for the tap
+    // marker to land at the current audio position. Intuitively, it "pushes" or
+    // "compresses" the markers blocking the tap marker's way so they all end up
+    // in the same position. The expectation is that the markers will reach their
+    // proper position once the user finishes tapping to the line
+    if (tap_marker_idx == 0) {
+        // Moving the start time of first syllable (i.e. start time of the line)
+        if (ms > end_marker) MoveEndMarker(ms);
+        if (syl > 0) CompressMarkers(syl - 1, 0, ms);
+        MoveStartMarker(ms);
+    }
+    else if (tap_marker_idx < markers.size() + 1) {
+        // Moving the end time of a non-end syllable
+        if (ms < start_marker) MoveStartMarker(ms);
+        else if (ms > end_marker) MoveEndMarker(ms);
+        if (syl < tap_marker_idx) {
+            // Moving markers left
+            CompressMarkers(syl, tap_marker_idx - 1, ms);
+        }
+        else {
+            // Moving markers right
+            CompressMarkers(syl - 1, tap_marker_idx - 1, ms);
+        }
+    }
+    else {
+        // Moving the end time of last syllable (i.e. end time of the line)
+        if (ms < start_marker) MoveStartMarker(ms);
+        if (syl < markers.size()) CompressMarkers(0, markers.size() - 1, ms);
+        MoveEndMarker(ms);
+    }
+
+    AnnounceChanges(true);
+}
+
+bool AudioTimingControllerKaraoke::NextTapMarker()
+{
+    if (tap_marker_idx < markers.size() + 1) {
+        ++tap_marker_idx;
+        AnnounceUpdatedTapMarker();
+        return true;
+    }
+    return false;
 }
 
-std::vector<AudioMarker*> AudioTimingControllerKaraoke::OnLeftClick(int ms, bool ctrl_down, bool, int sensitivity, int) {
-	TimeRange range(ms - sensitivity, ms + sensitivity);
+bool AudioTimingControllerKaraoke::IsNearbyMarker(int ms, int sensitivity, bool) const
+{
+    TimeRange range(ms - sensitivity, ms + sensitivity);
+    return any_of(markers.begin(), markers.end(), [&](KaraokeMarker const & km) {
+        return range.contains(km);
+    });
+}
 
-	size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
-	if (syl < markers.size() && range.contains(markers[syl]))
-		return copy_ptrs<AudioMarker>(markers, syl, ctrl_down ? markers.size() : syl + 1);
-	if (syl > 0 && range.contains(markers[syl - 1]))
-		return copy_ptrs<AudioMarker>(markers, syl - 1, ctrl_down ? markers.size() : syl);
+template<typename Out, typename In>
+static std::vector<Out *> copy_ptrs(In &vec, size_t start, size_t end)
+{
+    std::vector<Out *> ret;
+    ret.reserve(end - start);
+    for (; start < end; ++start)
+        ret.push_back(&vec[start]);
+    return ret;
+}
 
-	cur_syl = syl;
+std::vector<AudioMarker *> AudioTimingControllerKaraoke::OnLeftClick(int ms, bool ctrl_down, bool, int sensitivity, int)
+{
+    TimeRange range(ms - sensitivity, ms + sensitivity);
 
-	// Change tap marker
-	// Selecting a syllable moves the marker to the _end_ of that syllable, such
-	// that the next tap determines when that syllable ends. This behavior is
-	// more intuitive when coupled with AudioKaraoke's tap syllable highlight.
-	if (ms < start_marker.GetPosition()) {
-		tap_marker_idx = 0;
-	}
-	else {
-		tap_marker_idx = cur_syl + 1;
-	}
+    size_t syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
+    if (syl < markers.size() && range.contains(markers[syl]))
+        return copy_ptrs<AudioMarker>(markers, syl, ctrl_down ? markers.size() : syl + 1);
+    if (syl > 0 && range.contains(markers[syl - 1]))
+        return copy_ptrs<AudioMarker>(markers, syl - 1, ctrl_down ? markers.size() : syl);
 
-	AnnounceUpdatedPrimaryRange();
-	AnnounceUpdatedStyleRanges();
-	AnnounceUpdatedTapMarker();
+    cur_syl = syl;
 
-	return {};
-}
+    // Change tap marker
+    // Selecting a syllable moves the marker to the _end_ of that syllable, such
+    // that the next tap determines when that syllable ends. This behavior is
+    // more intuitive when coupled with AudioKaraoke's tap syllable highlight.
+    if (ms < start_marker.GetPosition()) {
+        tap_marker_idx = 0;
+    }
+    else {
+        tap_marker_idx = cur_syl + 1;
+    }
 
-std::vector<AudioMarker*> AudioTimingControllerKaraoke::OnRightClick(int ms, bool ctrl_down, int, int) {
-	if (ctrl_down) {
-		// Ctrl-right-click: play audio
-		c->audioController->PlayToEnd(ms);
-	}
-	else {
-		// Normal right-click: select new syllable and play range
-		cur_syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
-		AnnounceUpdatedPrimaryRange();
-		AnnounceUpdatedStyleRanges();
-		c->audioController->PlayPrimaryRange();
-	}
+    AnnounceUpdatedPrimaryRange();
+    AnnounceUpdatedStyleRanges();
+    AnnounceUpdatedTapMarker();
 
-	return {};
+    return {};
 }
 
-int AudioTimingControllerKaraoke::MoveMarker(KaraokeMarker *marker, int new_position) {
-	// No rearranging of syllables allowed
-	new_position = mid(
-		marker == &markers.front() ? start_marker.GetPosition() : (marker - 1)->GetPosition(),
-		new_position,
-		marker == &markers.back() ? end_marker.GetPosition() : (marker + 1)->GetPosition());
+std::vector<AudioMarker *> AudioTimingControllerKaraoke::OnRightClick(int ms, bool ctrl_down, int, int)
+{
+    if (ctrl_down) {
+        // Ctrl-right-click: play audio
+        c->audioController->PlayToEnd(ms);
+    }
+    else {
+        // Normal right-click: select new syllable and play range
+        cur_syl = distance(markers.begin(), lower_bound(markers.begin(), markers.end(), ms));
+        AnnounceUpdatedPrimaryRange();
+        AnnounceUpdatedStyleRanges();
+        c->audioController->PlayPrimaryRange();
+    }
+
+    return {};
+}
+
+int AudioTimingControllerKaraoke::MoveMarker(KaraokeMarker *marker, int new_position)
+{
+    // No rearranging of syllables allowed
+    new_position = mid(
+                       marker == &markers.front() ? start_marker.GetPosition() : (marker - 1)->GetPosition(),
+                       new_position,
+                       marker == &markers.back() ? end_marker.GetPosition() : (marker + 1)->GetPosition());
 
-	if (new_position == marker->GetPosition())
-		return -1;
+    if (new_position == marker->GetPosition())
+        return -1;
 
-	marker->Move(new_position);
+    marker->Move(new_position);
 
-	size_t syl = marker - &markers.front() + 1;
-	kara->SetStartTime(syl, new_position);
+    size_t syl = marker - &markers.front() + 1;
+    kara->SetStartTime(syl, new_position);
 
-	labels[syl - 1].range = TimeRange(labels[syl - 1].range.begin(), new_position);
-	labels[syl].range = TimeRange(new_position, labels[syl].range.end());
+    labels[syl - 1].range = TimeRange(labels[syl - 1].range.begin(), new_position);
+    labels[syl].range = TimeRange(new_position, labels[syl].range.end());
 
-	return syl;
+    return syl;
 }
 
-void AudioTimingControllerKaraoke::MoveStartMarker(int new_position) {
-	// No rearranging of syllables allowed
-	new_position = mid(
-		0,
-		new_position,
-		markers[0].GetPosition());
+void AudioTimingControllerKaraoke::MoveStartMarker(int new_position)
+{
+    // No rearranging of syllables allowed
+    new_position = mid(
+                       0,
+                       new_position,
+                       markers[0].GetPosition());
 
-	if (new_position == start_marker.GetPosition())
-		return;
+    if (new_position == start_marker.GetPosition())
+        return;
 
-	start_marker.Move(new_position);
+    start_marker.Move(new_position);
 
-	active_line->Start = (int)start_marker;
-	kara->SetLineTimes(start_marker, end_marker);
+    active_line->Start = (int)start_marker;
+    kara->SetLineTimes(start_marker, end_marker);
 
-	labels.front().range = TimeRange(start_marker, labels.front().range.end());
+    labels.front().range = TimeRange(start_marker, labels.front().range.end());
 }
 
-void AudioTimingControllerKaraoke::MoveEndMarker(int new_position) {
-	// No rearranging of syllables allowed
-	new_position = mid(
-		markers.back().GetPosition(),
-		new_position,
-		INT_MAX);
-
-	if (new_position == end_marker.GetPosition())
-		return;
-
-	end_marker.Move(new_position);
-
-	active_line->End = (int)end_marker;
-	kara->SetLineTimes(start_marker, end_marker);
+void AudioTimingControllerKaraoke::MoveEndMarker(int new_position)
+{
+    // No rearranging of syllables allowed
+    new_position = mid(
+                       markers.back().GetPosition(),
+                       new_position,
+                       INT_MAX);
 
-	labels.back().range = TimeRange(labels.back().range.begin(), end_marker);
-}
+    if (new_position == end_marker.GetPosition())
+        return;
 
-void AudioTimingControllerKaraoke::CompressMarkers(size_t from, size_t to, int new_position) {
-	int incr = (from < to ? 1 : -1);
-	size_t i = from;
-	for (;;) {
-		MoveMarker(&markers[i], new_position);
-		if (i == to) {
-			break;
-		}
-		else {
-			i += incr;
-		}
-	}
-}
+    end_marker.Move(new_position);
 
-void AudioTimingControllerKaraoke::AnnounceChanges(bool announce_primary) {
-	if (announce_primary) {
-		AnnounceUpdatedPrimaryRange();
-		AnnounceUpdatedStyleRanges();
-	}
-	AnnounceMarkerMoved();
-	AnnounceLabelChanged();
+    active_line->End = (int)end_marker;
+    kara->SetLineTimes(start_marker, end_marker);
 
-	if (auto_commit)
-		DoCommit();
-	else {
-		pending_changes = true;
-		commit_id = -1;
-	}
+    labels.back().range = TimeRange(labels.back().range.begin(), end_marker);
 }
 
-void AudioTimingControllerKaraoke::OnMarkerDrag(std::vector<AudioMarker*> const& m, int new_position, int) {
-	// Fix rounding error
-	new_position = (new_position + 5) / 10 * 10;
-	int old_position = m[0]->GetPosition();
-	int syl_int = MoveMarker(static_cast<KaraokeMarker *>(m[0]), new_position);
-	if (syl_int < 0) return;
+void AudioTimingControllerKaraoke::CompressMarkers(size_t from, size_t to, int new_position)
+{
+    int incr = (from < to ? 1 : -1);
+    size_t i = from;
+    for (;;) {
+        MoveMarker(&markers[i], new_position);
+        if (i == to) {
+            break;
+        }
+        else {
+            i += incr;
+        }
+    }
+}
+
+void AudioTimingControllerKaraoke::AnnounceChanges(bool announce_primary)
+{
+    if (announce_primary) {
+        AnnounceUpdatedPrimaryRange();
+        AnnounceUpdatedStyleRanges();
+    }
+    AnnounceMarkerMoved();
+    AnnounceLabelChanged();
+
+    if (auto_commit)
+        DoCommit();
+    else {
+        pending_changes = true;
+        commit_id = -1;
+    }
+}
+
+void AudioTimingControllerKaraoke::OnMarkerDrag(std::vector<AudioMarker *> const &m, int new_position, int)
+{
+    // Fix rounding error
+    new_position = (new_position + 5) / 10 * 10;
+    int old_position = m[0]->GetPosition();
+    int syl_int = MoveMarker(static_cast<KaraokeMarker *>(m[0]), new_position);
+    if (syl_int < 0) return;
     size_t syl = syl_int;
 
-	bool announce_primary = ((syl == cur_syl) || (syl == (cur_syl + 1)));
-	if (m.size() > 1) {
-		int delta = m[0]->GetPosition() - old_position;
-		for (AudioMarker *marker : m | boost::adaptors::sliced(1, m.size()))
-			MoveMarker(static_cast<KaraokeMarker *>(marker), marker->GetPosition() + delta);
-		announce_primary = true;
-	}
+    bool announce_primary = ((syl == cur_syl) || (syl == (cur_syl + 1)));
+    if (m.size() > 1) {
+        int delta = m[0]->GetPosition() - old_position;
+        for (AudioMarker *marker : m | boost::adaptors::sliced(1, m.size()))
+            MoveMarker(static_cast<KaraokeMarker *>(marker), marker->GetPosition() + delta);
+        announce_primary = true;
+    }
 
-	AnnounceChanges(announce_primary);
+    AnnounceChanges(announce_primary);
 }
 
-void AudioTimingControllerKaraoke::GetLabels(TimeRange const& range, std::vector<AudioLabel> &out) const {
-	copy(labels | boost::adaptors::filtered([&](AudioLabel const& l) {
-		return range.overlaps(l.range);
-	}), back_inserter(out));
+void AudioTimingControllerKaraoke::GetLabels(TimeRange const &range, std::vector<AudioLabel> &out) const
+{
+    copy(labels | boost::adaptors::filtered([&](AudioLabel const & l) {
+        return range.overlaps(l.range);
+    }), back_inserter(out));
 }
 
-std::string AudioTimingControllerKaraoke::GetCurrentSylText() const {
-	return kara->GetStrippedText(cur_syl);
+std::string AudioTimingControllerKaraoke::GetCurrentSylText() const
+{
+    return kara->GetStrippedText(cur_syl);
 }
 
-void AudioTimingControllerKaraoke::SetCurrentSylText(std::string new_text) {
-	kara->SetStrippedText(cur_syl, new_text);
-	AnnounceChanges(true);
+void AudioTimingControllerKaraoke::SetCurrentSylText(std::string new_text)
+{
+    kara->SetStrippedText(cur_syl, new_text);
+    AnnounceChanges(true);
 }
diff --git a/src/auto4_base.cpp b/src/auto4_base.cpp
index 388e1fa3deb91adc81a5a62a9a1fa727a5de1c8b..6c0c5056aba5cc41eee1e1d2812d9f89ecf2b4e8 100644
--- a/src/auto4_base.cpp
+++ b/src/auto4_base.cpp
@@ -61,447 +61,456 @@
 #endif
 
 namespace Automation4 {
-	bool CalculateTextExtents(AssStyle *style, std::string const& text, double &width, double &height, double &descent, double &extlead)
-	{
-		width = height = descent = extlead = 0;
+bool CalculateTextExtents(AssStyle *style, std::string const &text, double &width, double &height, double &descent, double &extlead)
+{
+    width = height = descent = extlead = 0;
 
-		double fontsize = style->fontsize * 64;
-		double spacing = style->spacing * 64;
+    double fontsize = style->fontsize * 64;
+    double spacing = style->spacing * 64;
 
 #ifdef WIN32
-		// This is almost copypasta from TextSub
-		auto dc = CreateCompatibleDC(nullptr);
-		if (!dc) return false;
-
-		SetMapMode(dc, MM_TEXT);
-
-		LOGFONTW lf = {0};
-		lf.lfHeight = (LONG)fontsize;
-		lf.lfWeight = style->bold ? FW_BOLD : FW_NORMAL;
-		lf.lfItalic = style->italic;
-		lf.lfUnderline = style->underline;
-		lf.lfStrikeOut = style->strikeout;
-		lf.lfCharSet = style->encoding;
-		lf.lfOutPrecision = OUT_TT_PRECIS;
-		lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
-		lf.lfQuality = ANTIALIASED_QUALITY;
-		lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
-		wcsncpy(lf.lfFaceName, agi::charset::ConvertW(style->font).c_str(), 31);
-
-		auto font = CreateFontIndirect(&lf);
-		if (!font) return false;
-
-		auto old_font = SelectObject(dc, font);
-
-		std::wstring wtext(agi::charset::ConvertW(text));
-		if (spacing != 0 ) {
-			width = 0;
-			for (auto c : wtext) {
-				SIZE sz;
-				GetTextExtentPoint32(dc, &c, 1, &sz);
-				width += sz.cx + spacing;
-				height = sz.cy;
-			}
-		}
-		else {
-			SIZE sz;
-			GetTextExtentPoint32(dc, &wtext[0], (int)wtext.size(), &sz);
-			width = sz.cx;
-			height = sz.cy;
-		}
-
-		TEXTMETRIC tm;
-		GetTextMetrics(dc, &tm);
-		descent = tm.tmDescent;
-		extlead = tm.tmExternalLeading;
-
-		SelectObject(dc, old_font);
-		DeleteObject(font);
-		DeleteObject(dc);
+    // This is almost copypasta from TextSub
+    auto dc = CreateCompatibleDC(nullptr);
+    if (!dc) return false;
+
+    SetMapMode(dc, MM_TEXT);
+
+    LOGFONTW lf = {0};
+    lf.lfHeight = (LONG)fontsize;
+    lf.lfWeight = style->bold ? FW_BOLD : FW_NORMAL;
+    lf.lfItalic = style->italic;
+    lf.lfUnderline = style->underline;
+    lf.lfStrikeOut = style->strikeout;
+    lf.lfCharSet = style->encoding;
+    lf.lfOutPrecision = OUT_TT_PRECIS;
+    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+    lf.lfQuality = ANTIALIASED_QUALITY;
+    lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+    wcsncpy(lf.lfFaceName, agi::charset::ConvertW(style->font).c_str(), 31);
+
+    auto font = CreateFontIndirect(&lf);
+    if (!font) return false;
+
+    auto old_font = SelectObject(dc, font);
+
+    std::wstring wtext(agi::charset::ConvertW(text));
+    if (spacing != 0 ) {
+        width = 0;
+        for (auto c : wtext) {
+            SIZE sz;
+            GetTextExtentPoint32(dc, &c, 1, &sz);
+            width += sz.cx + spacing;
+            height = sz.cy;
+        }
+    }
+    else {
+        SIZE sz;
+        GetTextExtentPoint32(dc, &wtext[0], (int)wtext.size(), &sz);
+        width = sz.cx;
+        height = sz.cy;
+    }
+
+    TEXTMETRIC tm;
+    GetTextMetrics(dc, &tm);
+    descent = tm.tmDescent;
+    extlead = tm.tmExternalLeading;
+
+    SelectObject(dc, old_font);
+    DeleteObject(font);
+    DeleteObject(dc);
 
 #else // not WIN32
-		wxMemoryDC thedc;
-
-		// fix fontsize to be 72 DPI
-		//fontsize = -FT_MulDiv((int)(fontsize+0.5), 72, thedc.GetPPI().y);
-
-		// now try to get a font!
-		// use the font list to get some caching... (chance is the script will need the same font very often)
-		// USING wxTheFontList SEEMS TO CAUSE BAD LEAKS!
-		//wxFont *thefont = wxTheFontList->FindOrCreateFont(
-		wxFont thefont(
-			(int)fontsize,
-			wxFONTFAMILY_DEFAULT,
-			style->italic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
-			style->bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
-			style->underline,
-			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 (auto const& wc : wtext) {
-				int a, b, c, d;
-				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;
-				extlead = d > extlead ? d*scaling : extlead;
-			}
-		} 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(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;
-		}
+    wxMemoryDC thedc;
+
+    // fix fontsize to be 72 DPI
+    //fontsize = -FT_MulDiv((int)(fontsize+0.5), 72, thedc.GetPPI().y);
+
+    // now try to get a font!
+    // use the font list to get some caching... (chance is the script will need the same font very often)
+    // USING wxTheFontList SEEMS TO CAUSE BAD LEAKS!
+    //wxFont *thefont = wxTheFontList->FindOrCreateFont(
+    wxFont thefont(
+        (int)fontsize,
+        wxFONTFAMILY_DEFAULT,
+        style->italic ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
+        style->bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
+        style->underline,
+        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 (auto const &wc : wtext) {
+            int a, b, c, d;
+            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;
+            extlead = d > extlead ? d * scaling : extlead;
+        }
+    }
+    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(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
 
-		// Compensate for scaling
-		width = style->scalex / 100 * width / 64;
-		height = style->scaley / 100 * height / 64;
-		descent = style->scaley / 100 * descent / 64;
-		extlead = style->scaley / 100 * extlead / 64;
-
-		return true;
-	}
-
-	ExportFilter::ExportFilter(std::string const& name, std::string const& description, int priority)
-	: AssExportFilter(name, description, priority)
-	{
-	}
-
-	std::string ExportFilter::GetScriptSettingsIdentifier()
-	{
-		return inline_string_encode(GetName());
-	}
-
-	wxWindow* ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) {
-		config_dialog = GenerateConfigDialog(parent, c);
-
-		if (config_dialog) {
-			std::string const& val = c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()];
-			if (!val.empty())
-				config_dialog->Unserialise(val);
-			return config_dialog->CreateWindow(parent);
-		}
-
-		return nullptr;
-	}
-
-	void ExportFilter::LoadSettings(bool is_default, agi::Context *c) {
-		if (config_dialog)
-			c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()] = config_dialog->Serialise();
-	}
-
-	// ProgressSink
-	ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr)
-	: impl(impl)
-	, bsr(bsr)
-	, trace_level(OPT_GET("Automation/Trace Level")->GetInt())
-	{
-	}
-
-	void ProgressSink::ShowDialog(ScriptDialog *config_dialog)
-	{
-		agi::dispatch::Main().Sync([=] {
-			wxDialog w; // container dialog box
-			w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
-			w.Create(bsr->GetParentWindow(), -1, to_wx(bsr->GetTitle()));
-			auto 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
-			w.SetSizerAndFit(s);
-			w.CenterOnParent();
-			w.ShowModal();
-		});
-	}
-
-	int ProgressSink::ShowDialog(wxDialog *dialog)
-	{
-		int ret = 0;
-		agi::dispatch::Main().Sync([&] { ret = dialog->ShowModal(); });
-		return ret;
-	}
-
-	BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, std::string const& title)
-	: impl(new DialogProgress(parent, to_wx(title)))
-	{
-	}
-
-	BackgroundScriptRunner::~BackgroundScriptRunner()
-	{
-	}
-
-	void BackgroundScriptRunner::Run(std::function<void (ProgressSink*)> task)
-	{
-		impl->Run([&](agi::ProgressSink *ps) {
-			ProgressSink aps(ps, this);
-			task(&aps);
-		});
-	}
-
-	wxWindow *BackgroundScriptRunner::GetParentWindow() const
-	{
-		return impl.get();
-	}
-
-	std::string BackgroundScriptRunner::GetTitle() const
-	{
-		return from_wx(impl->GetTitle());
-	}
-
-	// Script
-	Script::Script(agi::fs::path const& filename)
-	: filename(filename)
-	{
-		include_path.emplace_back(filename.parent_path());
-
-		std::string include_paths = OPT_GET("Path/Automation/Include")->GetString();
-		for (auto tok : agi::Split(include_paths, '|')) {
-			auto path = config::path->Decode(agi::str(tok));
-			if (path.is_absolute() && agi::fs::DirectoryExists(path))
-				include_path.emplace_back(std::move(path));
-		}
-	}
-
-	// ScriptManager
-	void ScriptManager::Add(std::unique_ptr<Script> script)
-	{
-		if (find(scripts.begin(), scripts.end(), script) == scripts.end())
-			scripts.emplace_back(std::move(script));
-
-		ScriptsChanged();
-	}
-
-	void ScriptManager::Remove(Script *script)
-	{
-		auto i = find_if(scripts.begin(), scripts.end(), [&](std::unique_ptr<Script> const& s) { return s.get() == script; });
-		if (i != scripts.end())
-			scripts.erase(i);
-
-		ScriptsChanged();
-	}
-
-	void ScriptManager::RemoveAll()
-	{
-		scripts.clear();
-		ScriptsChanged();
-	}
-
-	void ScriptManager::Reload(Script *script)
-	{
-		script->Reload();
-		ScriptsChanged();
-	}
-
-	const std::vector<cmd::Command*>& ScriptManager::GetMacros()
-	{
-		macros.clear();
-		for (auto& script : scripts) {
-			std::vector<cmd::Command*> sfs = script->GetMacros();
-			copy(sfs.begin(), sfs.end(), back_inserter(macros));
-		}
-		return macros;
-	}
-
-	// AutoloadScriptManager
-	AutoloadScriptManager::AutoloadScriptManager(std::string path)
-	: path(std::move(path))
-	{
-		Reload();
-	}
-
-	void AutoloadScriptManager::Reload()
-	{
-		scripts.clear();
-
-		std::vector<std::future<std::unique_ptr<Script>>> script_futures;
-
-		for (auto tok : agi::Split(path, '|')) {
-			auto dirname = config::path->Decode(agi::str(tok));
-			if (!agi::fs::DirectoryExists(dirname)) continue;
-
-			for (auto filename : agi::fs::DirectoryIterator(dirname, "*.*"))
-				script_futures.emplace_back(std::async(std::launch::async, [=] {
-					return ScriptFactory::CreateFromFile(dirname/filename, false, false);
-				}));
-		}
-
-		int error_count = 0;
-		for (auto& future : script_futures) {
-			auto s = future.get();
-			if (s) {
-				if (!s->GetLoadedState()) ++error_count;
-				scripts.emplace_back(std::move(s));
-			}
-		}
-
-		if (error_count == 1) {
-			wxLogWarning("A script in the Automation autoload directory failed to load.\nPlease review the errors, fix them and use the Rescan Autoload Dir button in Automation Manager to load the scripts again.");
-		}
-		else if (error_count > 1) {
-			wxLogWarning("Multiple scripts in the Automation autoload directory failed to load.\nPlease review the errors, fix them and use the Rescan Autoload Dir button in Automation Manager to load the scripts again.");
-		}
-
-		ScriptsChanged();
-	}
-
-	LocalScriptManager::LocalScriptManager(agi::Context *c)
-	: context(c)
-	, file_open_connection(c->subsController->AddFileOpenListener(&LocalScriptManager::Reload, this))
-	{
-		AddScriptChangeListener(&LocalScriptManager::SaveLoadedList, this);
-	}
-
-	void LocalScriptManager::Reload()
-	{
-		bool was_empty = scripts.empty();
-		scripts.clear();
-
-		auto const& local_scripts = context->ass->Properties.automation_scripts;
-		if (local_scripts.empty()) {
-			if (!was_empty)
-				ScriptsChanged();
-			return;
-		}
-
-		auto autobasefn(OPT_GET("Path/Automation/Base")->GetString());
-
-		for (auto tok : agi::Split(local_scripts, '|')) {
-			tok = boost::trim_copy(tok);
-			if (boost::size(tok) == 0) continue;
-			char first_char = tok[0];
-			std::string trimmed(begin(tok) + 1, end(tok));
-
-			agi::fs::path basepath;
-			if (first_char == '~') {
-				basepath = context->subsController->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, to_wx(trimmed));
-				continue;
-			}
-			auto sfname = basepath/trimmed;
-			if (agi::fs::FileExists(sfname))
-				scripts.emplace_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, to_wx(trimmed), basepath.wstring(), sfname.wstring());
-			}
-		}
-
-		ScriptsChanged();
-	}
-
-	void LocalScriptManager::SaveLoadedList()
-	{
-		// Store Automation script data
-		// Algorithm:
-		// 1. If script filename has Automation Base Path as a prefix, the path is relative to that (ie. "$")
-		// 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 ("~")
-		std::string scripts_string;
-		agi::fs::path autobasefn(OPT_GET("Path/Automation/Base")->GetString());
-
-		for (auto& script : GetScripts()) {
-			if (!scripts_string.empty())
-				scripts_string += "|";
-
-			auto scriptfn(script->GetFilename().string());
-			auto autobase_rel = context->path->MakeRelative(scriptfn, autobasefn);
-			auto assfile_rel = context->path->MakeRelative(scriptfn, "?script");
-
-			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 = "/" + script->GetFilename().generic_string();
-			}
-
-			scripts_string += scriptfn;
-		}
-		context->ass->Properties.automation_scripts = std::move(scripts_string);
-	}
-
-	// ScriptFactory
-	ScriptFactory::ScriptFactory(std::string engine_name, std::string filename_pattern)
-	: engine_name(std::move(engine_name))
-	, filename_pattern(std::move(filename_pattern))
-	{
-	}
-
-	void ScriptFactory::Register(std::unique_ptr<ScriptFactory> factory)
-	{
-		if (find(Factories().begin(), Factories().end(), factory) != Factories().end())
-			throw agi::InternalError("Automation 4: Attempt to register the same script factory multiple times. This should never happen.");
-
-		Factories().emplace_back(std::move(factory));
-	}
-
-	std::unique_ptr<Script> ScriptFactory::CreateFromFile(agi::fs::path const& filename, bool complain_about_unrecognised, bool create_unknown)
-	{
-		for (auto& factory : Factories()) {
-			auto s = factory->Produce(filename);
-			if (s) {
-				if (!s->GetLoadedState()) {
-					wxLogError(_("Failed to load Automation script '%s':\n%s"), filename.wstring(), s->GetDescription());
-				}
-				return s;
-			}
-		}
-
-		if (complain_about_unrecognised) {
-			wxLogError(_("The file was not recognised as an Automation script: %s"), filename.wstring());
-		}
-
-		return create_unknown ? agi::make_unique<UnknownScript>(filename) : nullptr;
-	}
-
-	std::vector<std::unique_ptr<ScriptFactory>>& ScriptFactory::Factories()
-	{
-		static std::vector<std::unique_ptr<ScriptFactory>> factories;
-		return factories;
-	}
-
-	const std::vector<std::unique_ptr<ScriptFactory>>& ScriptFactory::GetFactories()
-	{
-		return Factories();
-	}
-
-	std::string ScriptFactory::GetWildcardStr()
-	{
-		std::string fnfilter, catchall;
-		for (auto& fact : Factories()) {
-			if (fact->GetEngineName().empty() || fact->GetFilenamePattern().empty())
-				continue;
-
-			std::string filter(fact->GetFilenamePattern());
-			boost::replace_all(filter, ",", ";");
-			fnfilter += agi::format("%s scripts (%s)|%s|", fact->GetEngineName(), fact->GetFilenamePattern(), filter);
-			catchall += filter + ";";
-		}
-		fnfilter += from_wx(_("All Files")) + " (*.*)|*.*";
-
-		if (!catchall.empty())
-			catchall.pop_back();
-
-		if (Factories().size() > 1)
-			fnfilter = from_wx(_("All Supported Formats")) + "|" + catchall + "|" + fnfilter;
-
-		return fnfilter;
-	}
-
-	std::string UnknownScript::GetDescription() const {
-		return from_wx(_("File was not recognized as a script"));
-	}
+    // Compensate for scaling
+    width = style->scalex / 100 * width / 64;
+    height = style->scaley / 100 * height / 64;
+    descent = style->scaley / 100 * descent / 64;
+    extlead = style->scaley / 100 * extlead / 64;
+
+    return true;
+}
+
+ExportFilter::ExportFilter(std::string const &name, std::string const &description, int priority)
+    : AssExportFilter(name, description, priority)
+{
+}
+
+std::string ExportFilter::GetScriptSettingsIdentifier()
+{
+    return inline_string_encode(GetName());
+}
+
+wxWindow *ExportFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c)
+{
+    config_dialog = GenerateConfigDialog(parent, c);
+
+    if (config_dialog) {
+        std::string const &val = c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()];
+        if (!val.empty())
+            config_dialog->Unserialise(val);
+        return config_dialog->CreateWindow(parent);
+    }
+
+    return nullptr;
+}
+
+void ExportFilter::LoadSettings(bool is_default, agi::Context *c)
+{
+    if (config_dialog)
+        c->ass->Properties.automation_settings[GetScriptSettingsIdentifier()] = config_dialog->Serialise();
+}
+
+// ProgressSink
+ProgressSink::ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr)
+    : impl(impl)
+    , bsr(bsr)
+    , trace_level(OPT_GET("Automation/Trace Level")->GetInt())
+{
+}
+
+void ProgressSink::ShowDialog(ScriptDialog *config_dialog)
+{
+    agi::dispatch::Main().Sync([ = ] {
+        wxDialog w; // container dialog box
+        w.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
+        w.Create(bsr->GetParentWindow(), -1, to_wx(bsr->GetTitle()));
+        auto 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
+        w.SetSizerAndFit(s);
+        w.CenterOnParent();
+        w.ShowModal();
+    });
+}
+
+int ProgressSink::ShowDialog(wxDialog *dialog)
+{
+    int ret = 0;
+    agi::dispatch::Main().Sync([&] { ret = dialog->ShowModal(); });
+    return ret;
+}
+
+BackgroundScriptRunner::BackgroundScriptRunner(wxWindow *parent, std::string const &title)
+    : impl(new DialogProgress(parent, to_wx(title)))
+{
+}
+
+BackgroundScriptRunner::~BackgroundScriptRunner()
+{
+}
+
+void BackgroundScriptRunner::Run(std::function<void (ProgressSink *)> task)
+{
+    impl->Run([&](agi::ProgressSink * ps) {
+        ProgressSink aps(ps, this);
+        task(&aps);
+    });
+}
+
+wxWindow *BackgroundScriptRunner::GetParentWindow() const
+{
+    return impl.get();
+}
+
+std::string BackgroundScriptRunner::GetTitle() const
+{
+    return from_wx(impl->GetTitle());
+}
+
+// Script
+Script::Script(agi::fs::path const &filename)
+    : filename(filename)
+{
+    include_path.emplace_back(filename.parent_path());
+
+    std::string include_paths = OPT_GET("Path/Automation/Include")->GetString();
+    for (auto tok : agi::Split(include_paths, '|')) {
+        auto path = config::path->Decode(agi::str(tok));
+        if (path.is_absolute() && agi::fs::DirectoryExists(path))
+            include_path.emplace_back(std::move(path));
+    }
+}
+
+// ScriptManager
+void ScriptManager::Add(std::unique_ptr<Script> script)
+{
+    if (find(scripts.begin(), scripts.end(), script) == scripts.end())
+        scripts.emplace_back(std::move(script));
+
+    ScriptsChanged();
+}
+
+void ScriptManager::Remove(Script *script)
+{
+    auto i = find_if(scripts.begin(), scripts.end(), [&](std::unique_ptr<Script> const & s) { return s.get() == script; });
+    if (i != scripts.end())
+        scripts.erase(i);
+
+    ScriptsChanged();
+}
+
+void ScriptManager::RemoveAll()
+{
+    scripts.clear();
+    ScriptsChanged();
+}
+
+void ScriptManager::Reload(Script *script)
+{
+    script->Reload();
+    ScriptsChanged();
+}
+
+const std::vector<cmd::Command *> &ScriptManager::GetMacros()
+{
+    macros.clear();
+    for (auto &script : scripts) {
+        std::vector<cmd::Command *> sfs = script->GetMacros();
+        copy(sfs.begin(), sfs.end(), back_inserter(macros));
+    }
+    return macros;
+}
+
+// AutoloadScriptManager
+AutoloadScriptManager::AutoloadScriptManager(std::string path)
+    : path(std::move(path))
+{
+    Reload();
+}
+
+void AutoloadScriptManager::Reload()
+{
+    scripts.clear();
+
+    std::vector<std::future<std::unique_ptr<Script>>> script_futures;
+
+    for (auto tok : agi::Split(path, '|')) {
+        auto dirname = config::path->Decode(agi::str(tok));
+        if (!agi::fs::DirectoryExists(dirname)) continue;
+
+        for (auto filename : agi::fs::DirectoryIterator(dirname, "*.*"))
+            script_futures.emplace_back(std::async(std::launch::async, [ = ] {
+            return ScriptFactory::CreateFromFile(dirname / filename, false, false);
+        }));
+    }
+
+    int error_count = 0;
+    for (auto &future : script_futures) {
+        auto s = future.get();
+        if (s) {
+            if (!s->GetLoadedState()) ++error_count;
+            scripts.emplace_back(std::move(s));
+        }
+    }
+
+    if (error_count == 1) {
+        wxLogWarning("A script in the Automation autoload directory failed to load.\nPlease review the errors, fix them and use the Rescan Autoload Dir button in Automation Manager to load the scripts again.");
+    }
+    else if (error_count > 1) {
+        wxLogWarning("Multiple scripts in the Automation autoload directory failed to load.\nPlease review the errors, fix them and use the Rescan Autoload Dir button in Automation Manager to load the scripts again.");
+    }
+
+    ScriptsChanged();
+}
+
+LocalScriptManager::LocalScriptManager(agi::Context *c)
+    : context(c)
+    , file_open_connection(c->subsController->AddFileOpenListener(&LocalScriptManager::Reload, this))
+{
+    AddScriptChangeListener(&LocalScriptManager::SaveLoadedList, this);
+}
+
+void LocalScriptManager::Reload()
+{
+    bool was_empty = scripts.empty();
+    scripts.clear();
+
+    auto const &local_scripts = context->ass->Properties.automation_scripts;
+    if (local_scripts.empty()) {
+        if (!was_empty)
+            ScriptsChanged();
+        return;
+    }
+
+    auto autobasefn(OPT_GET("Path/Automation/Base")->GetString());
+
+    for (auto tok : agi::Split(local_scripts, '|')) {
+        tok = boost::trim_copy(tok);
+        if (boost::size(tok) == 0) continue;
+        char first_char = tok[0];
+        std::string trimmed(begin(tok) + 1, end(tok));
+
+        agi::fs::path basepath;
+        if (first_char == '~') {
+            basepath = context->subsController->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, to_wx(trimmed));
+            continue;
+        }
+        auto sfname = basepath / trimmed;
+        if (agi::fs::FileExists(sfname))
+            scripts.emplace_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, to_wx(trimmed), basepath.wstring(), sfname.wstring());
+        }
+    }
+
+    ScriptsChanged();
+}
+
+void LocalScriptManager::SaveLoadedList()
+{
+    // Store Automation script data
+    // Algorithm:
+    // 1. If script filename has Automation Base Path as a prefix, the path is relative to that (ie. "$")
+    // 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 ("~")
+    std::string scripts_string;
+    agi::fs::path autobasefn(OPT_GET("Path/Automation/Base")->GetString());
+
+    for (auto &script : GetScripts()) {
+        if (!scripts_string.empty())
+            scripts_string += "|";
+
+        auto scriptfn(script->GetFilename().string());
+        auto autobase_rel = context->path->MakeRelative(scriptfn, autobasefn);
+        auto assfile_rel = context->path->MakeRelative(scriptfn, "?script");
+
+        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 = "/" + script->GetFilename().generic_string();
+        }
+
+        scripts_string += scriptfn;
+    }
+    context->ass->Properties.automation_scripts = std::move(scripts_string);
+}
+
+// ScriptFactory
+ScriptFactory::ScriptFactory(std::string engine_name, std::string filename_pattern)
+    : engine_name(std::move(engine_name))
+    , filename_pattern(std::move(filename_pattern))
+{
+}
+
+void ScriptFactory::Register(std::unique_ptr<ScriptFactory> factory)
+{
+    if (find(Factories().begin(), Factories().end(), factory) != Factories().end())
+        throw agi::InternalError("Automation 4: Attempt to register the same script factory multiple times. This should never happen.");
+
+    Factories().emplace_back(std::move(factory));
+}
+
+std::unique_ptr<Script> ScriptFactory::CreateFromFile(agi::fs::path const &filename, bool complain_about_unrecognised, bool create_unknown)
+{
+    for (auto &factory : Factories()) {
+        auto s = factory->Produce(filename);
+        if (s) {
+            if (!s->GetLoadedState()) {
+                wxLogError(_("Failed to load Automation script '%s':\n%s"), filename.wstring(), s->GetDescription());
+            }
+            return s;
+        }
+    }
+
+    if (complain_about_unrecognised) {
+        wxLogError(_("The file was not recognised as an Automation script: %s"), filename.wstring());
+    }
+
+    return create_unknown ? agi::make_unique<UnknownScript>(filename) : nullptr;
+}
+
+std::vector<std::unique_ptr<ScriptFactory>> &ScriptFactory::Factories()
+{
+    static std::vector<std::unique_ptr<ScriptFactory>> factories;
+    return factories;
+}
+
+const std::vector<std::unique_ptr<ScriptFactory>> &ScriptFactory::GetFactories()
+{
+    return Factories();
+}
+
+std::string ScriptFactory::GetWildcardStr()
+{
+    std::string fnfilter, catchall;
+    for (auto &fact : Factories()) {
+        if (fact->GetEngineName().empty() || fact->GetFilenamePattern().empty())
+            continue;
+
+        std::string filter(fact->GetFilenamePattern());
+        boost::replace_all(filter, ",", ";");
+        fnfilter += agi::format("%s scripts (%s)|%s|", fact->GetEngineName(), fact->GetFilenamePattern(), filter);
+        catchall += filter + ";";
+    }
+    fnfilter += from_wx(_("All Files")) + " (*.*)|*.*";
+
+    if (!catchall.empty())
+        catchall.pop_back();
+
+    if (Factories().size() > 1)
+        fnfilter = from_wx(_("All Supported Formats")) + "|" + catchall + "|" + fnfilter;
+
+    return fnfilter;
+}
+
+std::string UnknownScript::GetDescription() const
+{
+    return from_wx(_("File was not recognized as a script"));
+}
 }
diff --git a/src/auto4_base.h b/src/auto4_base.h
index 5f645ccd0036befcbe54d4ad61f6a88f5d439ba7..c832371e1b3ab3b64d7367d2dc601fe42354f1bb 100644
--- a/src/auto4_base.h
+++ b/src/auto4_base.h
@@ -54,238 +54,238 @@ namespace agi { struct Context; }
 namespace cmd { class Command; }
 
 namespace Automation4 {
-	DEFINE_EXCEPTION(AutomationError, agi::Exception);
-	DEFINE_EXCEPTION(ScriptLoadError, AutomationError);
-	DEFINE_EXCEPTION(MacroRunError, AutomationError);
+DEFINE_EXCEPTION(AutomationError, agi::Exception);
+DEFINE_EXCEPTION(ScriptLoadError, AutomationError);
+DEFINE_EXCEPTION(MacroRunError, AutomationError);
 
-	// Calculate the extents of a text string given a style
-	bool CalculateTextExtents(AssStyle *style, std::string const& text, double &width, double &height, double &descent, double &extlead);
+// Calculate the extents of a text string given a style
+bool CalculateTextExtents(AssStyle *style, std::string const &text, double &width, double &height, double &descent, double &extlead);
 
-	class ScriptDialog;
-
-	class ExportFilter : public AssExportFilter {
-		std::unique_ptr<ScriptDialog> config_dialog;
-
-		/// subclasses should implement this, producing a new ScriptDialog
-		virtual std::unique_ptr<ScriptDialog> GenerateConfigDialog(wxWindow *parent, agi::Context *c) = 0;
-
-	protected:
-		std::string GetScriptSettingsIdentifier();
-
-	public:
-		ExportFilter(std::string const& name, std::string const& description, int priority);
-
-		wxWindow* GetConfigDialogWindow(wxWindow *parent, agi::Context *c) override;
-		void LoadSettings(bool is_default, agi::Context *c) override;
-
-		// Subclasses must implement ProcessSubs from AssExportFilter
-	};
-
-	/// A "dialog" which actually generates a non-top-level window that is then
-	/// either inserted into a dialog or into the export filter configuration
-	/// panel
-	class ScriptDialog {
-	public:
-		virtual ~ScriptDialog() = default;
-
-		/// Create a window with the given parent
-		virtual wxWindow *CreateWindow(wxWindow *parent) = 0;
-
-		/// Serialize the values of the controls in this dialog to a string
-		/// suitable for storage in the subtitle script
-		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(std::string const& serialised) { }
-	};
-
-	class ProgressSink;
-
-	class BackgroundScriptRunner {
-		std::unique_ptr<DialogProgress> impl;
-
-	public:
-		wxWindow *GetParentWindow() const;
-		std::string GetTitle() const;
-
-		void Run(std::function<void(ProgressSink*)> task);
-
-		BackgroundScriptRunner(wxWindow *parent, std::string const& title);
-		~BackgroundScriptRunner();
-	};
-
-	/// A wrapper around agi::ProgressSink which adds the ability to open
-	/// dialogs on the GUI thread
-	class ProgressSink final : public agi::ProgressSink {
-		agi::ProgressSink *impl;
-		BackgroundScriptRunner *bsr;
-		int trace_level;
-	public:
-		void SetIndeterminate() override { impl->SetIndeterminate(); }
-		void SetTitle(std::string const& title) override { impl->SetTitle(title); }
-		void SetMessage(std::string const& msg) override { impl->SetMessage(msg); }
-		void SetProgress(int64_t cur, int64_t max) override { impl->SetProgress(cur, max); }
-		void Log(std::string const& str) override { impl->Log(str); }
-		bool IsCancelled() override { return impl->IsCancelled(); }
-
-		/// Show the passed dialog on the GUI thread, blocking the calling
-		/// thread until it closes
-		void ShowDialog(ScriptDialog *config_dialog);
-		int ShowDialog(wxDialog *dialog);
-		wxWindow *GetParentWindow() const { return bsr->GetParentWindow(); }
-
-		/// Get the current automation trace level
-		int GetTraceLevel() const { return trace_level; }
-
-		ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr);
-	};
-
-	class Script {
-		agi::fs::path filename;
-
-	protected:
-		/// The automation include path, consisting of the user-specified paths
-		/// along with the script's path
-		std::vector<agi::fs::path> include_path;
-		Script(agi::fs::path const& filename);
-
-	public:
-		virtual ~Script() = default;
-
-		/// Reload this script
-		virtual void Reload() = 0;
-
-		/// The script's file name with path
-		agi::fs::path GetFilename() const { return filename; }
-		/// The script's file name without path
-		agi::fs::path GetPrettyFilename() const { return filename.filename(); }
-		/// The script's name. Not required to be unique.
-		virtual std::string GetName() const=0;
-		/// A short description of the script
-		virtual std::string GetDescription() const=0;
-		/// The author of the script
-		virtual std::string GetAuthor() const=0;
-		/// A version string that should not be used for anything but display
-		virtual std::string GetVersion() const=0;
-		/// Did the script load correctly?
-		virtual bool GetLoadedState() const=0;
-
-		/// Get a list of commands provided by this script
-		virtual std::vector<cmd::Command*> GetMacros() const=0;
-		/// Get a list of export filters provided by this script
-		virtual std::vector<ExportFilter*> GetFilters() const=0;
-	};
-
-	/// A manager of loaded automation scripts
-	class ScriptManager {
-	protected:
-		std::vector<std::unique_ptr<Script>> scripts;
-		std::vector<cmd::Command*> macros;
-
-		agi::signal::Signal<> ScriptsChanged;
-
-	public:
-		/// Deletes all scripts managed
-		virtual ~ScriptManager() = default;
-		/// Add a script to the manager.
-		void Add(std::unique_ptr<Script> script);
-		/// Remove a script from the manager, and delete the Script object.
-		void Remove(Script *script);
-		/// Deletes all scripts managed
-		void RemoveAll();
-		/// Reload all scripts managed
-		virtual void Reload() = 0;
-		/// Reload a single managed script
-		virtual void Reload(Script *script);
-
-		/// Get all managed scripts (both loaded and invalid)
-		const std::vector<std::unique_ptr<Script>>& GetScripts() const { return scripts; }
-
-		const std::vector<cmd::Command*>& GetMacros();
-		// No need to have getters for the other kinds of features, I think.
-		// They automatically register themselves in the relevant places.
-
-		DEFINE_SIGNAL_ADDERS(ScriptsChanged, AddScriptChangeListener)
-	};
-
-	/// Manager for scripts specified by a subtitle file
-	class LocalScriptManager final : public ScriptManager {
-		agi::Context *context;
-		agi::signal::Connection file_open_connection;
-
-		void SaveLoadedList();
-	public:
-		LocalScriptManager(agi::Context *context);
-		void Reload() override;
-	};
-
-	/// Manager for scripts in the autoload directory
-	class AutoloadScriptManager final : public ScriptManager {
-		std::string path;
-	public:
-		AutoloadScriptManager(std::string path);
-		void Reload() override;
-	};
-
-	/// Both a base class for script factories and a manager of registered
-	/// script factories
-	class ScriptFactory {
-		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
-		/// script should be returned which returns false from IsLoaded and
-		/// an appropriate error message from GetDescription.
-		///
-		/// This is private as it should only ever be called through
-		/// CreateFromFile
-		virtual std::unique_ptr<Script> Produce(agi::fs::path const& filename) const = 0;
-
-		static std::vector<std::unique_ptr<ScriptFactory>>& Factories();
-
-	protected:
-		ScriptFactory(std::string engine_name, std::string filename_pattern);
-
-	public:
-		virtual ~ScriptFactory() = default;
-
-		/// Name of this automation engine
-		const std::string& GetEngineName() const { return engine_name; }
-		/// Extension which this engine supports
-		const std::string& GetFilenamePattern() const { return filename_pattern; }
-
-		/// Register an automation engine.
-		static void Register(std::unique_ptr<ScriptFactory> factory);
-
-		/// Get the full wildcard string for all loaded engines
-		static std::string GetWildcardStr();
-
-		/// Load a script from a file
-		/// @param filename Script to load
-		/// @param complain_about_unrecognised Should an error be displayed for files that aren't automation scripts?
-		/// @param create_unknown Create a placeholder rather than returning nullptr if no script engine supports the file
-		static std::unique_ptr<Script> CreateFromFile(agi::fs::path const& filename, bool complain_about_unrecognised, bool create_unknown=true);
-
-		static const std::vector<std::unique_ptr<ScriptFactory>>& GetFactories();
-	};
-
-	/// A script which represents a file not recognized by any registered
-	/// automation engines
-	class UnknownScript final : public Script {
-	public:
-		UnknownScript(agi::fs::path const& filename) : Script(filename) { }
-
-		void Reload() override { }
-
-		std::string GetName() const override { return GetFilename().stem().string(); }
-		std::string GetDescription() const override;
-		std::string GetAuthor() const override { return ""; }
-		std::string GetVersion() const override { return ""; }
-		bool GetLoadedState() const override { return false; }
-
-		std::vector<cmd::Command*> GetMacros() const override { return {}; }
-		std::vector<ExportFilter*> GetFilters() const override { return {}; }
-	};
+class ScriptDialog;
+
+class ExportFilter : public AssExportFilter {
+    std::unique_ptr<ScriptDialog> config_dialog;
+
+    /// subclasses should implement this, producing a new ScriptDialog
+    virtual std::unique_ptr<ScriptDialog> GenerateConfigDialog(wxWindow *parent, agi::Context *c) = 0;
+
+protected:
+    std::string GetScriptSettingsIdentifier();
+
+public:
+    ExportFilter(std::string const &name, std::string const &description, int priority);
+
+    wxWindow *GetConfigDialogWindow(wxWindow *parent, agi::Context *c) override;
+    void LoadSettings(bool is_default, agi::Context *c) override;
+
+    // Subclasses must implement ProcessSubs from AssExportFilter
+};
+
+/// A "dialog" which actually generates a non-top-level window that is then
+/// either inserted into a dialog or into the export filter configuration
+/// panel
+class ScriptDialog {
+public:
+    virtual ~ScriptDialog() = default;
+
+    /// Create a window with the given parent
+    virtual wxWindow *CreateWindow(wxWindow *parent) = 0;
+
+    /// Serialize the values of the controls in this dialog to a string
+    /// suitable for storage in the subtitle script
+    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(std::string const &serialised) { }
+};
+
+class ProgressSink;
+
+class BackgroundScriptRunner {
+    std::unique_ptr<DialogProgress> impl;
+
+public:
+    wxWindow *GetParentWindow() const;
+    std::string GetTitle() const;
+
+    void Run(std::function<void(ProgressSink *)> task);
+
+    BackgroundScriptRunner(wxWindow *parent, std::string const &title);
+    ~BackgroundScriptRunner();
+};
+
+/// A wrapper around agi::ProgressSink which adds the ability to open
+/// dialogs on the GUI thread
+class ProgressSink final : public agi::ProgressSink {
+    agi::ProgressSink *impl;
+    BackgroundScriptRunner *bsr;
+    int trace_level;
+public:
+    void SetIndeterminate() override { impl->SetIndeterminate(); }
+    void SetTitle(std::string const &title) override { impl->SetTitle(title); }
+    void SetMessage(std::string const &msg) override { impl->SetMessage(msg); }
+    void SetProgress(int64_t cur, int64_t max) override { impl->SetProgress(cur, max); }
+    void Log(std::string const &str) override { impl->Log(str); }
+    bool IsCancelled() override { return impl->IsCancelled(); }
+
+    /// Show the passed dialog on the GUI thread, blocking the calling
+    /// thread until it closes
+    void ShowDialog(ScriptDialog *config_dialog);
+    int ShowDialog(wxDialog *dialog);
+    wxWindow *GetParentWindow() const { return bsr->GetParentWindow(); }
+
+    /// Get the current automation trace level
+    int GetTraceLevel() const { return trace_level; }
+
+    ProgressSink(agi::ProgressSink *impl, BackgroundScriptRunner *bsr);
+};
+
+class Script {
+    agi::fs::path filename;
+
+protected:
+    /// The automation include path, consisting of the user-specified paths
+    /// along with the script's path
+    std::vector<agi::fs::path> include_path;
+    Script(agi::fs::path const &filename);
+
+public:
+    virtual ~Script() = default;
+
+    /// Reload this script
+    virtual void Reload() = 0;
+
+    /// The script's file name with path
+    agi::fs::path GetFilename() const { return filename; }
+    /// The script's file name without path
+    agi::fs::path GetPrettyFilename() const { return filename.filename(); }
+    /// The script's name. Not required to be unique.
+    virtual std::string GetName() const = 0;
+    /// A short description of the script
+    virtual std::string GetDescription() const = 0;
+    /// The author of the script
+    virtual std::string GetAuthor() const = 0;
+    /// A version string that should not be used for anything but display
+    virtual std::string GetVersion() const = 0;
+    /// Did the script load correctly?
+    virtual bool GetLoadedState() const = 0;
+
+    /// Get a list of commands provided by this script
+    virtual std::vector<cmd::Command *> GetMacros() const = 0;
+    /// Get a list of export filters provided by this script
+    virtual std::vector<ExportFilter *> GetFilters() const = 0;
+};
+
+/// A manager of loaded automation scripts
+class ScriptManager {
+protected:
+    std::vector<std::unique_ptr<Script>> scripts;
+    std::vector<cmd::Command *> macros;
+
+    agi::signal::Signal<> ScriptsChanged;
+
+public:
+    /// Deletes all scripts managed
+    virtual ~ScriptManager() = default;
+    /// Add a script to the manager.
+    void Add(std::unique_ptr<Script> script);
+    /// Remove a script from the manager, and delete the Script object.
+    void Remove(Script *script);
+    /// Deletes all scripts managed
+    void RemoveAll();
+    /// Reload all scripts managed
+    virtual void Reload() = 0;
+    /// Reload a single managed script
+    virtual void Reload(Script *script);
+
+    /// Get all managed scripts (both loaded and invalid)
+    const std::vector<std::unique_ptr<Script>> &GetScripts() const { return scripts; }
+
+    const std::vector<cmd::Command *> &GetMacros();
+    // No need to have getters for the other kinds of features, I think.
+    // They automatically register themselves in the relevant places.
+
+    DEFINE_SIGNAL_ADDERS(ScriptsChanged, AddScriptChangeListener)
+};
+
+/// Manager for scripts specified by a subtitle file
+class LocalScriptManager final : public ScriptManager {
+    agi::Context *context;
+    agi::signal::Connection file_open_connection;
+
+    void SaveLoadedList();
+public:
+    LocalScriptManager(agi::Context *context);
+    void Reload() override;
+};
+
+/// Manager for scripts in the autoload directory
+class AutoloadScriptManager final : public ScriptManager {
+    std::string path;
+public:
+    AutoloadScriptManager(std::string path);
+    void Reload() override;
+};
+
+/// Both a base class for script factories and a manager of registered
+/// script factories
+class ScriptFactory {
+    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
+    /// script should be returned which returns false from IsLoaded and
+    /// an appropriate error message from GetDescription.
+    ///
+    /// This is private as it should only ever be called through
+    /// CreateFromFile
+    virtual std::unique_ptr<Script> Produce(agi::fs::path const &filename) const = 0;
+
+    static std::vector<std::unique_ptr<ScriptFactory>> &Factories();
+
+protected:
+    ScriptFactory(std::string engine_name, std::string filename_pattern);
+
+public:
+    virtual ~ScriptFactory() = default;
+
+    /// Name of this automation engine
+    const std::string &GetEngineName() const { return engine_name; }
+    /// Extension which this engine supports
+    const std::string &GetFilenamePattern() const { return filename_pattern; }
+
+    /// Register an automation engine.
+    static void Register(std::unique_ptr<ScriptFactory> factory);
+
+    /// Get the full wildcard string for all loaded engines
+    static std::string GetWildcardStr();
+
+    /// Load a script from a file
+    /// @param filename Script to load
+    /// @param complain_about_unrecognised Should an error be displayed for files that aren't automation scripts?
+    /// @param create_unknown Create a placeholder rather than returning nullptr if no script engine supports the file
+    static std::unique_ptr<Script> CreateFromFile(agi::fs::path const &filename, bool complain_about_unrecognised, bool create_unknown = true);
+
+    static const std::vector<std::unique_ptr<ScriptFactory>> &GetFactories();
+};
+
+/// A script which represents a file not recognized by any registered
+/// automation engines
+class UnknownScript final : public Script {
+public:
+    UnknownScript(agi::fs::path const &filename) : Script(filename) { }
+
+    void Reload() override { }
+
+    std::string GetName() const override { return GetFilename().stem().string(); }
+    std::string GetDescription() const override;
+    std::string GetAuthor() const override { return ""; }
+    std::string GetVersion() const override { return ""; }
+    bool GetLoadedState() const override { return false; }
+
+    std::vector<cmd::Command *> GetMacros() const override { return {}; }
+    std::vector<ExportFilter *> GetFilters() const override { return {}; }
+};
 }
diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp
index 59c1e10af3c9844a67e5f13de66687ae90d4aa25..33b0e8e595c3ed05e1ec464876a1e2300f2468e3 100644
--- a/src/auto4_lua.cpp
+++ b/src/auto4_lua.cpp
@@ -77,965 +77,969 @@ using namespace agi::lua;
 using namespace Automation4;
 
 namespace {
-	wxString get_wxstring(lua_State *L, int idx)
-	{
-		return wxString::FromUTF8(lua_tostring(L, idx));
-	}
-
-	wxString check_wxstring(lua_State *L, int idx)
-	{
-		return to_wx(check_string(L, idx));
-	}
-
-	void set_context(lua_State *L, const agi::Context *c)
-	{
-		// Explicit cast is needed to discard the const
-		push_value(L, (void *)c);
-		lua_setfield(L, LUA_REGISTRYINDEX, "project_context");
-	}
-
-	const agi::Context *get_context(lua_State *L)
-	{
-		lua_getfield(L, LUA_REGISTRYINDEX, "project_context");
-		if (!lua_islightuserdata(L, -1)) {
-			lua_pop(L, 1);
-			return nullptr;
-		}
-		const agi::Context * c = static_cast<const agi::Context *>(lua_touserdata(L, -1));
-		lua_pop(L, 1);
-		return c;
-	}
-
-	int get_file_name(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		if (c && !c->subsController->Filename().empty())
-			push_value(L, c->subsController->Filename().filename());
-		else
-			lua_pushnil(L);
-		return 1;
-	}
-
-	int get_translation(lua_State *L)
-	{
-		wxString str(check_wxstring(L, 1));
-		push_value(L, _(str).utf8_str());
-		return 1;
-	}
-
-	const char *clipboard_get()
-	{
-		std::string data = GetClipboard();
-		if (data.empty())
-			return nullptr;
-		return strndup(data);
-	}
-
-	bool clipboard_set(const char *str)
-	{
-		bool succeeded = false;
+wxString get_wxstring(lua_State *L, int idx)
+{
+    return wxString::FromUTF8(lua_tostring(L, idx));
+}
+
+wxString check_wxstring(lua_State *L, int idx)
+{
+    return to_wx(check_string(L, idx));
+}
+
+void set_context(lua_State *L, const agi::Context *c)
+{
+    // Explicit cast is needed to discard the const
+    push_value(L, (void *)c);
+    lua_setfield(L, LUA_REGISTRYINDEX, "project_context");
+}
+
+const agi::Context *get_context(lua_State *L)
+{
+    lua_getfield(L, LUA_REGISTRYINDEX, "project_context");
+    if (!lua_islightuserdata(L, -1)) {
+        lua_pop(L, 1);
+        return nullptr;
+    }
+    const agi::Context *c = static_cast<const agi::Context *>(lua_touserdata(L, -1));
+    lua_pop(L, 1);
+    return c;
+}
+
+int get_file_name(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    if (c && !c->subsController->Filename().empty())
+        push_value(L, c->subsController->Filename().filename());
+    else
+        lua_pushnil(L);
+    return 1;
+}
+
+int get_translation(lua_State *L)
+{
+    wxString str(check_wxstring(L, 1));
+    push_value(L, _(str).utf8_str());
+    return 1;
+}
+
+const char *clipboard_get()
+{
+    std::string data = GetClipboard();
+    if (data.empty())
+        return nullptr;
+    return strndup(data);
+}
+
+bool clipboard_set(const char *str)
+{
+    bool succeeded = false;
 
 #if wxUSE_OLE
-		// OLE needs to be initialized on each thread that wants to write to
-		// the clipboard, which wx does not handle automatically
-		wxClipboard cb;
+    // OLE needs to be initialized on each thread that wants to write to
+    // the clipboard, which wx does not handle automatically
+    wxClipboard cb;
 #else
-		wxClipboard &cb = *wxTheClipboard;
+    wxClipboard &cb = *wxTheClipboard;
 #endif
-		if (cb.Open()) {
-			succeeded = cb.SetData(new wxTextDataObject(wxString::FromUTF8(str)));
-			cb.Close();
-			cb.Flush();
-		}
-
-		return succeeded;
-	}
-
-	int clipboard_init(lua_State *L)
-	{
-		agi::lua::register_lib_table(L, {}, "get", clipboard_get, "set", clipboard_set);
-		return 1;
-	}
-
-	int frame_from_ms(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		int ms = lua_tointeger(L, -1);
-		lua_pop(L, 1);
-		if (c && c->project->Timecodes().IsLoaded())
-			push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START));
-		else
-			lua_pushnil(L);
-
-		return 1;
-	}
-
-	int ms_from_frame(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		int frame = lua_tointeger(L, -1);
-		lua_pop(L, 1);
-		if (c && c->project->Timecodes().IsLoaded())
-			push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START));
-		else
-			lua_pushnil(L);
-		return 1;
-	}
-
-	int video_size(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		if (c && c->project->VideoProvider()) {
-			auto provider = c->project->VideoProvider();
-			push_value(L, provider->GetWidth());
-			push_value(L, provider->GetHeight());
-			push_value(L, c->videoController->GetAspectRatioValue());
-			push_value(L, (int)c->videoController->GetAspectRatioType());
-			return 4;
-		}
-		else {
-			lua_pushnil(L);
-			return 1;
-		}
-	}
-
-	int get_keyframes(lua_State *L)
-	{
-		if (const agi::Context *c = get_context(L))
-			push_value(L, c->project->Keyframes());
-		else
-			lua_pushnil(L);
-		return 1;
-	}
-
-	int decode_path(lua_State *L)
-	{
-		std::string path = check_string(L, 1);
-		lua_pop(L, 1);
-		if (const agi::Context *c = get_context(L))
-			push_value(L, c->path->Decode(path));
-		else
-			push_value(L, config::path->Decode(path));
-		return 1;
-	}
-
-	int cancel_script(lua_State *L)
-	{
-		lua_pushnil(L);
-		throw error_tag();
-	}
-
-	int lua_text_textents(lua_State *L)
-	{
-		argcheck(L, !!lua_istable(L, 1), 1, "");
-		argcheck(L, !!lua_isstring(L, 2), 2, "");
-
-		// have to check that it looks like a style table before actually converting
-		// if it's a dialogue table then an active AssFile object is required
-		{
-			lua_getfield(L, 1, "class");
-			std::string actual_class{lua_tostring(L, -1)};
-			boost::to_lower(actual_class);
-			if (actual_class != "style")
-				return error(L, "Not a style entry");
-			lua_pop(L, 1);
-		}
-
-		lua_pushvalue(L, 1);
-		std::unique_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
-		lua_pop(L, 1);
-        auto& et_ref = *et;
-		if (typeid(et_ref) != typeid(AssStyle))
-			return error(L, "Not a style entry");
-
-		double width, height, descent, extlead;
-		if (!Automation4::CalculateTextExtents(static_cast<AssStyle*>(et.get()),
-				check_string(L, 2), width, height, descent, extlead))
-			return error(L, "Some internal error occurred calculating text_extents");
-
-		push_value(L, width);
-		push_value(L, height);
-		push_value(L, descent);
-		push_value(L, extlead);
-		return 4;
-	}
-
-	int lua_get_audio_selection(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		if (!c || !c->audioController || !c->audioController->GetTimingController()) {
-			lua_pushnil(L);
-			return 1;
-		}
-		const TimeRange range = c->audioController->GetTimingController()->GetActiveLineRange();
-		push_value(L, range.begin());
-		push_value(L, range.end());
-		return 2;
-	}
-
-	int lua_set_status_text(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		if (!c || !c->frame) {
-			lua_pushnil(L);
-			return 1;
-		}
-		std::string text = check_string(L, 1);
-		lua_pop(L, 1);
-		agi::dispatch::Main().Async([=] { c->frame->StatusTimeout(to_wx(text)); });
-		return 0;
-	}
-
-	int project_properties(lua_State *L)
-	{
-		const agi::Context *c = get_context(L);
-		if (!c)
-			lua_pushnil(L);
-		else {
-			lua_createtable(L, 0, 14);
+    if (cb.Open()) {
+        succeeded = cb.SetData(new wxTextDataObject(wxString::FromUTF8(str)));
+        cb.Close();
+        cb.Flush();
+    }
+
+    return succeeded;
+}
+
+int clipboard_init(lua_State *L)
+{
+    agi::lua::register_lib_table(L, {}, "get", clipboard_get, "set", clipboard_set);
+    return 1;
+}
+
+int frame_from_ms(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    int ms = lua_tointeger(L, -1);
+    lua_pop(L, 1);
+    if (c && c->project->Timecodes().IsLoaded())
+        push_value(L, c->videoController->FrameAtTime(ms, agi::vfr::START));
+    else
+        lua_pushnil(L);
+
+    return 1;
+}
+
+int ms_from_frame(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    int frame = lua_tointeger(L, -1);
+    lua_pop(L, 1);
+    if (c && c->project->Timecodes().IsLoaded())
+        push_value(L, c->videoController->TimeAtFrame(frame, agi::vfr::START));
+    else
+        lua_pushnil(L);
+    return 1;
+}
+
+int video_size(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    if (c && c->project->VideoProvider()) {
+        auto provider = c->project->VideoProvider();
+        push_value(L, provider->GetWidth());
+        push_value(L, provider->GetHeight());
+        push_value(L, c->videoController->GetAspectRatioValue());
+        push_value(L, (int)c->videoController->GetAspectRatioType());
+        return 4;
+    }
+    else {
+        lua_pushnil(L);
+        return 1;
+    }
+}
+
+int get_keyframes(lua_State *L)
+{
+    if (const agi::Context *c = get_context(L))
+        push_value(L, c->project->Keyframes());
+    else
+        lua_pushnil(L);
+    return 1;
+}
+
+int decode_path(lua_State *L)
+{
+    std::string path = check_string(L, 1);
+    lua_pop(L, 1);
+    if (const agi::Context *c = get_context(L))
+        push_value(L, c->path->Decode(path));
+    else
+        push_value(L, config::path->Decode(path));
+    return 1;
+}
+
+int cancel_script(lua_State *L)
+{
+    lua_pushnil(L);
+    throw error_tag();
+}
+
+int lua_text_textents(lua_State *L)
+{
+    argcheck(L, !!lua_istable(L, 1), 1, "");
+    argcheck(L, !!lua_isstring(L, 2), 2, "");
+
+    // have to check that it looks like a style table before actually converting
+    // if it's a dialogue table then an active AssFile object is required
+    {
+        lua_getfield(L, 1, "class");
+        std::string actual_class{lua_tostring(L, -1)};
+        boost::to_lower(actual_class);
+        if (actual_class != "style")
+            return error(L, "Not a style entry");
+        lua_pop(L, 1);
+    }
+
+    lua_pushvalue(L, 1);
+    std::unique_ptr<AssEntry> et(Automation4::LuaAssFile::LuaToAssEntry(L));
+    lua_pop(L, 1);
+    auto &et_ref = *et;
+    if (typeid(et_ref) != typeid(AssStyle))
+        return error(L, "Not a style entry");
+
+    double width, height, descent, extlead;
+    if (!Automation4::CalculateTextExtents(static_cast<AssStyle *>(et.get()),
+                                           check_string(L, 2), width, height, descent, extlead))
+        return error(L, "Some internal error occurred calculating text_extents");
+
+    push_value(L, width);
+    push_value(L, height);
+    push_value(L, descent);
+    push_value(L, extlead);
+    return 4;
+}
+
+int lua_get_audio_selection(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    if (!c || !c->audioController || !c->audioController->GetTimingController()) {
+        lua_pushnil(L);
+        return 1;
+    }
+    const TimeRange range = c->audioController->GetTimingController()->GetActiveLineRange();
+    push_value(L, range.begin());
+    push_value(L, range.end());
+    return 2;
+}
+
+int lua_set_status_text(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    if (!c || !c->frame) {
+        lua_pushnil(L);
+        return 1;
+    }
+    std::string text = check_string(L, 1);
+    lua_pop(L, 1);
+    agi::dispatch::Main().Async([ = ] { c->frame->StatusTimeout(to_wx(text)); });
+    return 0;
+}
+
+int project_properties(lua_State *L)
+{
+    const agi::Context *c = get_context(L);
+    if (!c)
+        lua_pushnil(L);
+    else {
+        lua_createtable(L, 0, 14);
 #define PUSH_FIELD(name) set_field(L, #name, c->ass->Properties.name)
-			PUSH_FIELD(automation_scripts);
-			PUSH_FIELD(export_filters);
-			PUSH_FIELD(export_encoding);
-			PUSH_FIELD(style_storage);
-			PUSH_FIELD(video_zoom);
-			PUSH_FIELD(ar_value);
-			PUSH_FIELD(scroll_position);
-			PUSH_FIELD(active_row);
-			PUSH_FIELD(ar_mode);
-			PUSH_FIELD(video_position);
+        PUSH_FIELD(automation_scripts);
+        PUSH_FIELD(export_filters);
+        PUSH_FIELD(export_encoding);
+        PUSH_FIELD(style_storage);
+        PUSH_FIELD(video_zoom);
+        PUSH_FIELD(ar_value);
+        PUSH_FIELD(scroll_position);
+        PUSH_FIELD(active_row);
+        PUSH_FIELD(ar_mode);
+        PUSH_FIELD(video_position);
 #undef PUSH_FIELD
-			set_field(L, "audio_file", c->path->MakeAbsolute(c->ass->Properties.audio_file, "?script"));
-			set_field(L, "video_file", c->path->MakeAbsolute(c->ass->Properties.video_file, "?script"));
-			set_field(L, "timecodes_file", c->path->MakeAbsolute(c->ass->Properties.timecodes_file, "?script"));
-			set_field(L, "keyframes_file", c->path->MakeAbsolute(c->ass->Properties.keyframes_file, "?script"));
-		}
-		return 1;
-	}
-
-	class LuaFeature {
-		int myid = 0;
-	protected:
-		lua_State *L;
-
-		void RegisterFeature();
-		void UnregisterFeature();
-
-		void GetFeatureFunction(const char *function) const;
-
-		LuaFeature(lua_State *L) : L(L) { }
-	};
-
-	/// Run a lua function on a background thread
-	/// @param L Lua state
-	/// @param nargs Number of arguments the function takes
-	/// @param nresults Number of values the function returns
-	/// @param title Title to use for the progress dialog
-	/// @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, std::string const& title, wxWindow *parent, bool can_open_config);
-
-	class LuaCommand final : public cmd::Command, private LuaFeature {
-		std::string cmd_name;
-		wxString display;
-		wxString help;
-		int cmd_type;
-
-	public:
-		LuaCommand(lua_State *L);
-		~LuaCommand();
-
-		const char* name() const override { return cmd_name.c_str(); }
-		wxString StrMenu(const agi::Context *) const override { return display; }
-		wxString StrDisplay(const agi::Context *) const override { return display; }
-		wxString StrHelp() const override { return help; }
-
-		int Type() const override { return cmd_type; }
-
-		void operator()(agi::Context *c) override;
-		bool Validate(const agi::Context *c) override;
-		virtual bool IsActive(const agi::Context *c) override;
-
-		static int LuaRegister(lua_State *L);
-	};
-
-	class LuaExportFilter final : public ExportFilter, private LuaFeature {
-		bool has_config;
-		LuaDialog *config_dialog;
-
-	protected:
-		std::unique_ptr<ScriptDialog> GenerateConfigDialog(wxWindow *parent, agi::Context *c) override;
-
-	public:
-		LuaExportFilter(lua_State *L);
-		static int LuaRegister(lua_State *L);
-
-		void ProcessSubs(AssFile *subs, wxWindow *export_dialog) override;
-	};
-	class LuaScript final : public Script {
-		lua_State *L = nullptr;
-
-		std::string name;
-		std::string description;
-		std::string author;
-		std::string version;
-
-		std::vector<cmd::Command*> macros;
-		std::vector<std::unique_ptr<ExportFilter>> filters;
-
-		/// load script and create internal structures etc.
-		void Create();
-		/// destroy internal structures, unreg features and delete environment
-		void Destroy();
-
-		static int LuaInclude(lua_State *L);
-
-	public:
-		LuaScript(agi::fs::path const& filename);
-		~LuaScript() { Destroy(); }
-
-		void RegisterCommand(LuaCommand *command);
-		void UnregisterCommand(LuaCommand *command);
-		void RegisterFilter(LuaExportFilter *filter);
-
-		static LuaScript* GetScriptObject(lua_State *L);
-
-		// Script implementation
-		void Reload() override { Create(); }
-
-		std::string GetName() const override { return name; }
-		std::string GetDescription() const override { return description; }
-		std::string GetAuthor() const override { return author; }
-		std::string GetVersion() const override { return version; }
-		bool GetLoadedState() const override { return L != nullptr; }
-
-		std::vector<cmd::Command*> GetMacros() const override { return macros; }
-		std::vector<ExportFilter*> GetFilters() const override;
-	};
-
-	LuaScript::LuaScript(agi::fs::path const& filename)
-	: Script(filename)
-	{
-		Create();
-	}
-
-	void LuaScript::Create()
-	{
-		Destroy();
-
-		name = GetPrettyFilename().string();
-
-		// create lua environment
-		L = luaL_newstate();
-		if (!L) {
-			description = "Could not initialize Lua state";
-			return;
-		}
-
-		bool loaded = false;
-		BOOST_SCOPE_EXIT_ALL(&) { if (!loaded) Destroy(); };
-		LuaStackcheck stackcheck(L);
-
-		// register standard libs
-		preload_modules(L);
-		stackcheck.check_stack(0);
-
-		// dofile and loadfile are replaced with include
-		lua_pushnil(L);
-		lua_setglobal(L, "dofile");
-		lua_pushnil(L);
-		lua_setglobal(L, "loadfile");
-		push_value(L, exception_wrapper<LuaInclude>);
-		lua_setglobal(L, "include");
-
-		// Replace the default lua module loader with our unicode compatible
-		// one and set the module search path
-		if (!Install(L, include_path)) {
-			description = get_string_or_default(L, 1);
-			lua_pop(L, 1);
-			return;
-		}
-		stackcheck.check_stack(0);
-
-		// prepare stuff in the registry
-
-		// store the script's filename
-		push_value(L, GetFilename().stem());
-		lua_setfield(L, LUA_REGISTRYINDEX, "filename");
-		stackcheck.check_stack(0);
-
-		// reference to the script object
-		push_value(L, this);
-		lua_setfield(L, LUA_REGISTRYINDEX, "aegisub");
-		stackcheck.check_stack(0);
-
-		// make "aegisub" table
-		lua_pushstring(L, "aegisub");
-		lua_createtable(L, 0, 13);
-
-		set_field<LuaCommand::LuaRegister>(L, "register_macro");
-		set_field<LuaExportFilter::LuaRegister>(L, "register_filter");
-		set_field<lua_text_textents>(L, "text_extents");
-		set_field<frame_from_ms>(L, "frame_from_ms");
-		set_field<ms_from_frame>(L, "ms_from_frame");
-		set_field<video_size>(L, "video_size");
-		set_field<get_keyframes>(L, "keyframes");
-		set_field<decode_path>(L, "decode_path");
-		set_field<cancel_script>(L, "cancel");
-		set_field(L, "lua_automation_version", 4);
-		set_field<clipboard_init>(L, "__init_clipboard");
-		set_field<get_file_name>(L, "file_name");
-		set_field<get_translation>(L, "gettext");
-		set_field<project_properties>(L, "project_properties");
-		set_field<lua_get_audio_selection>(L, "get_audio_selection");
-		set_field<lua_set_status_text>(L, "set_status_text");
-
-		// store aegisub table to globals
-		lua_settable(L, LUA_GLOBALSINDEX);
-		stackcheck.check_stack(0);
-
-		// load user script
-		if (!LoadFile(L, GetFilename())) {
-			description = get_string_or_default(L, 1);
-			lua_pop(L, 1);
-			return;
-		}
-		stackcheck.check_stack(1);
-
-		// Insert our error handler under the user's script
-		lua_pushcclosure(L, add_stack_trace, 0);
-		lua_insert(L, -2);
-
-		// and execute it
-		// this is where features are registered
-		if (lua_pcall(L, 0, 0, -2)) {
-			// error occurred, assumed to be on top of Lua stack
-			description = agi::format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename().string(), get_string_or_default(L, -1));
-			lua_pop(L, 2); // error + error handler
-			return;
-		}
-		lua_pop(L, 1); // error handler
-		stackcheck.check_stack(0);
-
-		lua_getglobal(L, "version");
-		if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) {
-			lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
-			description = "Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported.";
-			return;
-		}
-
-		name = get_global_string(L, "script_name");
-		description = get_global_string(L, "script_description");
-		author = get_global_string(L, "script_author");
-		version = get_global_string(L, "script_version");
-
-		if (name.empty())
-			name = GetPrettyFilename().string();
-
-		lua_pop(L, 1);
-		// if we got this far, the script should be ready
-		loaded = true;
-	}
-
-	void LuaScript::Destroy()
-	{
-		// Assume the script object is clean if there's no Lua state
-		if (!L) return;
-
-		// loops backwards because commands remove themselves from macros when
-		// they're unregistered
-		for (int i = macros.size() - 1; i >= 0; --i)
-			cmd::unreg(macros[i]->name());
-
-		filters.clear();
-
-		lua_close(L);
-		L = nullptr;
-	}
-
-	std::vector<ExportFilter*> LuaScript::GetFilters() const
-	{
-		std::vector<ExportFilter *> ret;
-		ret.reserve(filters.size());
-		for (auto& filter : filters) ret.push_back(filter.get());
-		return ret;
-	}
-
-	void LuaScript::RegisterCommand(LuaCommand *command)
-	{
-		for (auto macro : macros) {
-			if (macro->name() == command->name()) {
-				error(L, "A macro named '%s' is already defined in script '%s'",
-					command->StrDisplay(nullptr).utf8_str().data(), name.c_str());
-			}
-		}
-		macros.push_back(command);
-	}
-
-	void LuaScript::UnregisterCommand(LuaCommand *command)
-	{
-		macros.erase(remove(macros.begin(), macros.end(), command), macros.end());
-	}
-
-	void LuaScript::RegisterFilter(LuaExportFilter *filter)
-	{
-		filters.emplace_back(filter);
-	}
-
-	LuaScript* LuaScript::GetScriptObject(lua_State *L)
-	{
-		lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
-		void *ptr = lua_touserdata(L, -1);
-		lua_pop(L, 1);
-		return (LuaScript*)ptr;
-	}
-
-
-	int LuaScript::LuaInclude(lua_State *L)
-	{
-		const LuaScript *s = GetScriptObject(L);
-
-		const std::string filename(check_string(L, 1));
-		agi::fs::path filepath;
-
-		// 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 (!agi::fs::FileExists(filepath))
-			return error(L, "Lua include not found: %s", filename.c_str());
-
-		if (!LoadFile(L, filepath))
-			return error(L, "Error loading Lua include \"%s\":\n%s", filename.c_str(), check_string(L, 1).c_str());
-
-		int pretop = lua_gettop(L) - 1; // don't count the function value itself
-		lua_call(L, 0, LUA_MULTRET);
-		return lua_gettop(L) - pretop;
-	}
-
-	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);
-		bsr.Run([&](ProgressSink *ps) {
-			LuaProgressSink lps(L, ps, can_open_config);
-
-			// Insert our error handler under the function to call
-			lua_pushcclosure(L, add_stack_trace, 0);
-			lua_insert(L, -nargs - 2);
-
-			if (lua_pcall(L, nargs, nresults, -nargs - 2)) {
-				if (!lua_isnil(L, -1)) {
-					// if the call failed, log the error here
-					ps->Log("\n\nLua reported a runtime error:\n");
-					ps->Log(get_string_or_default(L, -1));
-				}
-				lua_pop(L, 2);
-				failed = true;
-			}
-			else
-				lua_remove(L, -nresults - 1);
-
-			lua_gc(L, LUA_GCCOLLECT, 0);
-		});
-		if (failed)
-			throw agi::UserCancelException("Script threw an error");
-	}
-
-	// LuaFeature
-	void LuaFeature::RegisterFeature()
-	{
-		myid = luaL_ref(L, LUA_REGISTRYINDEX);
-	}
-
-	void LuaFeature::UnregisterFeature()
-	{
-		luaL_unref(L, LUA_REGISTRYINDEX, myid);
-	}
-
-	void LuaFeature::GetFeatureFunction(const char *function) const
-	{
-		// get this feature's function pointers
-		lua_rawgeti(L, LUA_REGISTRYINDEX, myid);
-		// get pointer for validation function
-		push_value(L, function);
-		lua_rawget(L, -2);
-		// remove the function table
-		lua_remove(L, -2);
-		assert(lua_isfunction(L, -1));
-	}
-
-	// LuaFeatureMacro
-	int LuaCommand::LuaRegister(lua_State *L)
-	{
-		static std::mutex mutex;
-		auto command = agi::make_unique<LuaCommand>(L);
-		{
-			std::lock_guard<std::mutex> lock(mutex);
-			cmd::reg(std::move(command));
-		}
-		return 0;
-	}
-
-	LuaCommand::LuaCommand(lua_State *L)
-	: LuaFeature(L)
-	, display(check_wxstring(L, 1))
-	, help(get_wxstring(L, 2))
-	, cmd_type(cmd::COMMAND_NORMAL)
-	{
-		lua_getfield(L, LUA_REGISTRYINDEX, "filename");
-		cmd_name = agi::format("automation/lua/%s/%s", check_string(L, -1), check_string(L, 1));
-
-		if (!lua_isfunction(L, 3))
-			error(L, "The macro processing function must be a function");
-
-		if (lua_isfunction(L, 4))
-			cmd_type |= cmd::COMMAND_VALIDATE;
-
-		if (lua_isfunction(L, 5))
-			cmd_type |= cmd::COMMAND_TOGGLE;
-
-		// new table for containing the functions for this feature
-		lua_createtable(L, 0, 3);
-
-		// store processing function
-		push_value(L, "run");
-		lua_pushvalue(L, 3);
-		lua_rawset(L, -3);
-
-		// store validation function
-		push_value(L, "validate");
-		lua_pushvalue(L, 4);
-		lua_rawset(L, -3);
-
-		// store active function
-		push_value(L, "isactive");
-		lua_pushvalue(L, 5);
-		lua_rawset(L, -3);
-
-		// store the table in the registry
-		RegisterFeature();
-
-		LuaScript::GetScriptObject(L)->RegisterCommand(this);
-	}
-
-	LuaCommand::~LuaCommand()
-	{
-		UnregisterFeature();
-		LuaScript::GetScriptObject(L)->UnregisterCommand(this);
-	}
-
-	static std::vector<int> selected_rows(const agi::Context *c)
-	{
-		auto const& sel = c->selectionController->GetSelectedSet();
-		int offset = c->ass->Info.size() + c->ass->Styles.size();
-		std::vector<int> rows;
-		rows.reserve(sel.size());
-		for (auto line : sel)
-			rows.push_back(line->Row + offset + 1);
-		sort(begin(rows), end(rows));
-		return rows;
-	}
-
-	bool LuaCommand::Validate(const agi::Context *c)
-	{
-		if (!(cmd_type & cmd::COMMAND_VALIDATE)) return true;
-
-		set_context(L, c);
-
-		// Error handler goes under the function to call
-		lua_pushcclosure(L, add_stack_trace, 0);
-
-		GetFeatureFunction("validate");
-		auto subsobj = new LuaAssFile(L, c->ass.get());
-
-		push_value(L, selected_rows(c));
-		if (auto active_line = c->selectionController->GetActiveLine())
-			push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
-		else
-			lua_pushnil(L);
-
-		int err = lua_pcall(L, 3, 2, -5 /* three args, function, error handler */);
-		subsobj->ProcessingComplete();
-
-		if (err) {
-			wxLogWarning("Runtime error in Lua macro validation function:\n%s", get_wxstring(L, -1));
-			lua_pop(L, 2);
-			return false;
-		}
-
-		bool result = !!lua_toboolean(L, -2);
-
-		wxString new_help_string(get_wxstring(L, -1));
-		if (new_help_string.size()) {
-			help = new_help_string;
-			cmd_type |= cmd::COMMAND_DYNAMIC_HELP;
-		}
-
-		lua_pop(L, 3); // two return values and error handler
-
-		return result;
-	}
-
-	void LuaCommand::operator()(agi::Context *c)
-	{
-		LuaStackcheck stackcheck(L);
-		set_context(L, c);
-		stackcheck.check_stack(0);
-
-		GetFeatureFunction("run");
-		auto subsobj = new LuaAssFile(L, c->ass.get(), true, true);
-
-		int original_offset = c->ass->Info.size() + c->ass->Styles.size() + 1;
-		auto original_sel = selected_rows(c);
-		int original_active = 0;
-		if (auto active_line = c->selectionController->GetActiveLine())
-			original_active = active_line->Row + original_offset;
-
-		push_value(L, original_sel);
-		push_value(L, original_active);
-
-		try {
-			LuaThreadedCall(L, 3, 2, from_wx(StrDisplay(c)), c->parent, true);
-		}
-		catch (agi::UserCancelException const&) {
-			subsobj->Cancel();
-			stackcheck.check_stack(0);
-			return;
-		}
-
-		auto lines = subsobj->ProcessingComplete(StrDisplay(c));
-
-		AssDialogue *active_line = nullptr;
-		int active_idx = original_active;
-
-		// Check for a new active row
-		if (lua_isnumber(L, -1)) {
-			active_idx = lua_tointeger(L, -1);
-			if (active_idx < 1 || active_idx > (int)lines.size()) {
-				wxLogError("Active row %d is out of bounds (must be 1-%u)", active_idx, lines.size());
-				active_idx = original_active;
-			}
-		}
-
-		stackcheck.check_stack(2);
-		lua_pop(L, 1);
-
-		// top of stack will be selected lines array, if any was returned
-		if (lua_istable(L, -1)) {
-			std::set<AssDialogue*> sel;
-			lua_for_each(L, [&] {
-				if (!lua_isnumber(L, -1))
-					return;
-				int cur = lua_tointeger(L, -1);
-				if (cur < 1 || cur > (int)lines.size()) {
-					wxLogError("Selected row %d is out of bounds (must be 1-%u)", cur, lines.size());
-					throw LuaForEachBreak();
-				}
-
-                auto &prev_line = lines[cur - 1][0];
-				if (typeid(prev_line) != typeid(AssDialogue)) {
-					wxLogError("Selected row %d is not a dialogue line", cur);
-					throw LuaForEachBreak();
-				}
-
-				auto diag = static_cast<AssDialogue*>(lines[cur - 1]);
-				sel.insert(diag);
-				if (!active_line || active_idx == cur)
-					active_line = diag;
-			});
-
-			AssDialogue *new_active = c->selectionController->GetActiveLine();
-			if (active_line && (active_idx > 0 || !sel.count(new_active)))
-				new_active = active_line;
-			if (sel.empty())
-				sel.insert(new_active);
-			c->selectionController->SetSelectionAndActive(std::move(sel), new_active);
-		}
-		else {
-			lua_pop(L, 1);
-
-			Selection new_sel;
-			AssDialogue *new_active = nullptr;
-
-			int prev = original_offset;
-			auto it = c->ass->Events.begin();
-			for (int row : original_sel) {
-				while (row > prev && it != c->ass->Events.end()) {
-					++prev;
-					++it;
-				}
-				if (it == c->ass->Events.end()) break;
-				new_sel.insert(&*it);
-				if (row == original_active)
-					new_active = &*it;
-			}
-
-			if (new_sel.empty() && !c->ass->Events.empty())
-				new_sel.insert(&c->ass->Events.front());
-			if (!new_sel.count(new_active))
-				new_active = *new_sel.begin();
-			c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
-		}
-
-		stackcheck.check_stack(0);
-	}
-
-	bool LuaCommand::IsActive(const agi::Context *c)
-	{
-		if (!(cmd_type & cmd::COMMAND_TOGGLE)) return false;
-
-		LuaStackcheck stackcheck(L);
-
-		set_context(L, c);
-		stackcheck.check_stack(0);
-
-		GetFeatureFunction("isactive");
-		auto subsobj = new LuaAssFile(L, c->ass.get());
-		push_value(L, selected_rows(c));
-		if (auto active_line = c->selectionController->GetActiveLine())
-			push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
-
-		int err = lua_pcall(L, 3, 1, 0);
-		subsobj->ProcessingComplete();
-
-		bool result = false;
-		if (err)
-			wxLogWarning("Runtime error in Lua macro IsActive function:\n%s", get_wxstring(L, -1));
-		else
-			result = !!lua_toboolean(L, -1);
-
-		// clean up stack (result or error message)
-		stackcheck.check_stack(1);
-		lua_pop(L, 1);
-
-		return result;
-	}
-
-	// LuaFeatureFilter
-	LuaExportFilter::LuaExportFilter(lua_State *L)
-	: ExportFilter(check_string(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3))
-	, LuaFeature(L)
-	{
-		if (!lua_isfunction(L, 4))
-			error(L, "The filter processing function must be a function");
-
-		// new table for containing the functions for this feature
-		lua_createtable(L, 0, 2);
-
-		// store processing function
-		push_value(L, "run");
-		lua_pushvalue(L, 4);
-		lua_rawset(L, -3);
-
-		// store config function
-		push_value(L, "config");
-		lua_pushvalue(L, 5);
-		has_config = lua_isfunction(L, -1);
-		lua_rawset(L, -3);
-
-		// store the table in the registry
-		RegisterFeature();
-
-		LuaScript::GetScriptObject(L)->RegisterFilter(this);
-	}
-
-	int LuaExportFilter::LuaRegister(lua_State *L)
-	{
-		static std::mutex mutex;
-		auto filter = agi::make_unique<LuaExportFilter>(L);
-		{
-			std::lock_guard<std::mutex> lock(mutex);
-			AssExportFilterChain::Register(std::move(filter));
-		}
-		return 0;
-	}
-
-	void LuaExportFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
-	{
-		LuaStackcheck stackcheck(L);
-
-		GetFeatureFunction("run");
-		stackcheck.check_stack(1);
-
-		// The entire point of an export filter is to modify the file, but
-		// setting undo points makes no sense
-		auto subsobj = new LuaAssFile(L, subs, true);
-		assert(lua_isuserdata(L, -1));
-		stackcheck.check_stack(2);
-
-		// config
-		if (has_config && config_dialog) {
-			int results_produced = config_dialog->LuaReadBack(L);
-			assert(results_produced == 1);
-			(void) results_produced;	// avoid warning on release builds
-			// TODO, write back stored options here
-		} else {
-			// no config so put an empty table instead
-			lua_newtable(L);
-		}
-		assert(lua_istable(L, -1));
-		stackcheck.check_stack(3);
-
-		try {
-			LuaThreadedCall(L, 2, 0, GetName(), export_dialog, false);
-			stackcheck.check_stack(0);
-			subsobj->ProcessingComplete();
-		}
-		catch (agi::UserCancelException const&) {
-			subsobj->Cancel();
-			throw;
-		}
-	}
-
-	std::unique_ptr<ScriptDialog> LuaExportFilter::GenerateConfigDialog(wxWindow *parent, agi::Context *c)
-	{
-		if (!has_config)
-			return nullptr;
-
-		set_context(L, c);
-
-		GetFeatureFunction("config");
-
-		// prepare function call
-		auto subsobj = new LuaAssFile(L, c->ass.get());
-		// stored options
-		lua_newtable(L); // TODO, nothing for now
-
-		// do call
-		int err = lua_pcall(L, 2, 1, 0);
-		subsobj->ProcessingComplete();
-
-		if (err) {
-			wxLogWarning("Runtime error in Lua config dialog function:\n%s", get_wxstring(L, -1));
-			lua_pop(L, 1); // remove error message
-		} else {
-			// Create config dialogue from table on top of stack
-			config_dialog = new LuaDialog(L, false);
-		}
-
-		return std::unique_ptr<ScriptDialog>{config_dialog};
-	}
+        set_field(L, "audio_file", c->path->MakeAbsolute(c->ass->Properties.audio_file, "?script"));
+        set_field(L, "video_file", c->path->MakeAbsolute(c->ass->Properties.video_file, "?script"));
+        set_field(L, "timecodes_file", c->path->MakeAbsolute(c->ass->Properties.timecodes_file, "?script"));
+        set_field(L, "keyframes_file", c->path->MakeAbsolute(c->ass->Properties.keyframes_file, "?script"));
+    }
+    return 1;
+}
+
+class LuaFeature {
+    int myid = 0;
+protected:
+    lua_State *L;
+
+    void RegisterFeature();
+    void UnregisterFeature();
+
+    void GetFeatureFunction(const char *function) const;
+
+    LuaFeature(lua_State *L) : L(L) { }
+};
+
+/// Run a lua function on a background thread
+/// @param L Lua state
+/// @param nargs Number of arguments the function takes
+/// @param nresults Number of values the function returns
+/// @param title Title to use for the progress dialog
+/// @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, std::string const &title, wxWindow *parent, bool can_open_config);
+
+class LuaCommand final : public cmd::Command, private LuaFeature {
+    std::string cmd_name;
+    wxString display;
+    wxString help;
+    int cmd_type;
+
+public:
+    LuaCommand(lua_State *L);
+    ~LuaCommand();
+
+    const char *name() const override { return cmd_name.c_str(); }
+    wxString StrMenu(const agi::Context *) const override { return display; }
+    wxString StrDisplay(const agi::Context *) const override { return display; }
+    wxString StrHelp() const override { return help; }
+
+    int Type() const override { return cmd_type; }
+
+    void operator()(agi::Context *c) override;
+    bool Validate(const agi::Context *c) override;
+    virtual bool IsActive(const agi::Context *c) override;
+
+    static int LuaRegister(lua_State *L);
+};
+
+class LuaExportFilter final : public ExportFilter, private LuaFeature {
+    bool has_config;
+    LuaDialog *config_dialog;
+
+protected:
+    std::unique_ptr<ScriptDialog> GenerateConfigDialog(wxWindow *parent, agi::Context *c) override;
+
+public:
+    LuaExportFilter(lua_State *L);
+    static int LuaRegister(lua_State *L);
+
+    void ProcessSubs(AssFile *subs, wxWindow *export_dialog) override;
+};
+class LuaScript final : public Script {
+    lua_State *L = nullptr;
+
+    std::string name;
+    std::string description;
+    std::string author;
+    std::string version;
+
+    std::vector<cmd::Command *> macros;
+    std::vector<std::unique_ptr<ExportFilter>> filters;
+
+    /// load script and create internal structures etc.
+    void Create();
+    /// destroy internal structures, unreg features and delete environment
+    void Destroy();
+
+    static int LuaInclude(lua_State *L);
+
+public:
+    LuaScript(agi::fs::path const &filename);
+    ~LuaScript() { Destroy(); }
+
+    void RegisterCommand(LuaCommand *command);
+    void UnregisterCommand(LuaCommand *command);
+    void RegisterFilter(LuaExportFilter *filter);
+
+    static LuaScript *GetScriptObject(lua_State *L);
+
+    // Script implementation
+    void Reload() override { Create(); }
+
+    std::string GetName() const override { return name; }
+    std::string GetDescription() const override { return description; }
+    std::string GetAuthor() const override { return author; }
+    std::string GetVersion() const override { return version; }
+    bool GetLoadedState() const override { return L != nullptr; }
+
+    std::vector<cmd::Command *> GetMacros() const override { return macros; }
+    std::vector<ExportFilter *> GetFilters() const override;
+};
+
+LuaScript::LuaScript(agi::fs::path const &filename)
+    : Script(filename)
+{
+    Create();
+}
+
+void LuaScript::Create()
+{
+    Destroy();
+
+    name = GetPrettyFilename().string();
+
+    // create lua environment
+    L = luaL_newstate();
+    if (!L) {
+        description = "Could not initialize Lua state";
+        return;
+    }
+
+    bool loaded = false;
+    BOOST_SCOPE_EXIT_ALL(&) { if (!loaded) Destroy(); };
+    LuaStackcheck stackcheck(L);
+
+    // register standard libs
+    preload_modules(L);
+    stackcheck.check_stack(0);
+
+    // dofile and loadfile are replaced with include
+    lua_pushnil(L);
+    lua_setglobal(L, "dofile");
+    lua_pushnil(L);
+    lua_setglobal(L, "loadfile");
+    push_value(L, exception_wrapper<LuaInclude>);
+    lua_setglobal(L, "include");
+
+    // Replace the default lua module loader with our unicode compatible
+    // one and set the module search path
+    if (!Install(L, include_path)) {
+        description = get_string_or_default(L, 1);
+        lua_pop(L, 1);
+        return;
+    }
+    stackcheck.check_stack(0);
+
+    // prepare stuff in the registry
+
+    // store the script's filename
+    push_value(L, GetFilename().stem());
+    lua_setfield(L, LUA_REGISTRYINDEX, "filename");
+    stackcheck.check_stack(0);
+
+    // reference to the script object
+    push_value(L, this);
+    lua_setfield(L, LUA_REGISTRYINDEX, "aegisub");
+    stackcheck.check_stack(0);
+
+    // make "aegisub" table
+    lua_pushstring(L, "aegisub");
+    lua_createtable(L, 0, 13);
+
+    set_field<LuaCommand::LuaRegister>(L, "register_macro");
+    set_field<LuaExportFilter::LuaRegister>(L, "register_filter");
+    set_field<lua_text_textents>(L, "text_extents");
+    set_field<frame_from_ms>(L, "frame_from_ms");
+    set_field<ms_from_frame>(L, "ms_from_frame");
+    set_field<video_size>(L, "video_size");
+    set_field<get_keyframes>(L, "keyframes");
+    set_field<decode_path>(L, "decode_path");
+    set_field<cancel_script>(L, "cancel");
+    set_field(L, "lua_automation_version", 4);
+    set_field<clipboard_init>(L, "__init_clipboard");
+    set_field<get_file_name>(L, "file_name");
+    set_field<get_translation>(L, "gettext");
+    set_field<project_properties>(L, "project_properties");
+    set_field<lua_get_audio_selection>(L, "get_audio_selection");
+    set_field<lua_set_status_text>(L, "set_status_text");
+
+    // store aegisub table to globals
+    lua_settable(L, LUA_GLOBALSINDEX);
+    stackcheck.check_stack(0);
+
+    // load user script
+    if (!LoadFile(L, GetFilename())) {
+        description = get_string_or_default(L, 1);
+        lua_pop(L, 1);
+        return;
+    }
+    stackcheck.check_stack(1);
+
+    // Insert our error handler under the user's script
+    lua_pushcclosure(L, add_stack_trace, 0);
+    lua_insert(L, -2);
+
+    // and execute it
+    // this is where features are registered
+    if (lua_pcall(L, 0, 0, -2)) {
+        // error occurred, assumed to be on top of Lua stack
+        description = agi::format("Error initialising Lua script \"%s\":\n\n%s", GetPrettyFilename().string(), get_string_or_default(L, -1));
+        lua_pop(L, 2); // error + error handler
+        return;
+    }
+    lua_pop(L, 1); // error handler
+    stackcheck.check_stack(0);
+
+    lua_getglobal(L, "version");
+    if (lua_isnumber(L, -1) && lua_tointeger(L, -1) == 3) {
+        lua_pop(L, 1); // just to avoid tripping the stackcheck in debug
+        description = "Attempted to load an Automation 3 script as an Automation 4 Lua script. Automation 3 is no longer supported.";
+        return;
+    }
+
+    name = get_global_string(L, "script_name");
+    description = get_global_string(L, "script_description");
+    author = get_global_string(L, "script_author");
+    version = get_global_string(L, "script_version");
+
+    if (name.empty())
+        name = GetPrettyFilename().string();
+
+    lua_pop(L, 1);
+    // if we got this far, the script should be ready
+    loaded = true;
+}
+
+void LuaScript::Destroy()
+{
+    // Assume the script object is clean if there's no Lua state
+    if (!L) return;
+
+    // loops backwards because commands remove themselves from macros when
+    // they're unregistered
+    for (int i = macros.size() - 1; i >= 0; --i)
+        cmd::unreg(macros[i]->name());
+
+    filters.clear();
+
+    lua_close(L);
+    L = nullptr;
+}
+
+std::vector<ExportFilter *> LuaScript::GetFilters() const
+{
+    std::vector<ExportFilter *> ret;
+    ret.reserve(filters.size());
+    for (auto &filter : filters) ret.push_back(filter.get());
+    return ret;
+}
+
+void LuaScript::RegisterCommand(LuaCommand *command)
+{
+    for (auto macro : macros) {
+        if (macro->name() == command->name()) {
+            error(L, "A macro named '%s' is already defined in script '%s'",
+                  command->StrDisplay(nullptr).utf8_str().data(), name.c_str());
+        }
+    }
+    macros.push_back(command);
+}
+
+void LuaScript::UnregisterCommand(LuaCommand *command)
+{
+    macros.erase(remove(macros.begin(), macros.end(), command), macros.end());
+}
+
+void LuaScript::RegisterFilter(LuaExportFilter *filter)
+{
+    filters.emplace_back(filter);
+}
+
+LuaScript *LuaScript::GetScriptObject(lua_State *L)
+{
+    lua_getfield(L, LUA_REGISTRYINDEX, "aegisub");
+    void *ptr = lua_touserdata(L, -1);
+    lua_pop(L, 1);
+    return (LuaScript *)ptr;
+}
+
+
+int LuaScript::LuaInclude(lua_State *L)
+{
+    const LuaScript *s = GetScriptObject(L);
+
+    const std::string filename(check_string(L, 1));
+    agi::fs::path filepath;
+
+    // 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 (!agi::fs::FileExists(filepath))
+        return error(L, "Lua include not found: %s", filename.c_str());
+
+    if (!LoadFile(L, filepath))
+        return error(L, "Error loading Lua include \"%s\":\n%s", filename.c_str(), check_string(L, 1).c_str());
+
+    int pretop = lua_gettop(L) - 1; // don't count the function value itself
+    lua_call(L, 0, LUA_MULTRET);
+    return lua_gettop(L) - pretop;
+}
+
+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);
+    bsr.Run([&](ProgressSink * ps) {
+        LuaProgressSink lps(L, ps, can_open_config);
+
+        // Insert our error handler under the function to call
+        lua_pushcclosure(L, add_stack_trace, 0);
+        lua_insert(L, -nargs - 2);
+
+        if (lua_pcall(L, nargs, nresults, -nargs - 2)) {
+            if (!lua_isnil(L, -1)) {
+                // if the call failed, log the error here
+                ps->Log("\n\nLua reported a runtime error:\n");
+                ps->Log(get_string_or_default(L, -1));
+            }
+            lua_pop(L, 2);
+            failed = true;
+        }
+        else
+            lua_remove(L, -nresults - 1);
+
+        lua_gc(L, LUA_GCCOLLECT, 0);
+    });
+    if (failed)
+        throw agi::UserCancelException("Script threw an error");
+}
+
+// LuaFeature
+void LuaFeature::RegisterFeature()
+{
+    myid = luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+void LuaFeature::UnregisterFeature()
+{
+    luaL_unref(L, LUA_REGISTRYINDEX, myid);
+}
+
+void LuaFeature::GetFeatureFunction(const char *function) const
+{
+    // get this feature's function pointers
+    lua_rawgeti(L, LUA_REGISTRYINDEX, myid);
+    // get pointer for validation function
+    push_value(L, function);
+    lua_rawget(L, -2);
+    // remove the function table
+    lua_remove(L, -2);
+    assert(lua_isfunction(L, -1));
+}
+
+// LuaFeatureMacro
+int LuaCommand::LuaRegister(lua_State *L)
+{
+    static std::mutex mutex;
+    auto command = agi::make_unique<LuaCommand>(L);
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        cmd::reg(std::move(command));
+    }
+    return 0;
+}
+
+LuaCommand::LuaCommand(lua_State *L)
+    : LuaFeature(L)
+    , display(check_wxstring(L, 1))
+    , help(get_wxstring(L, 2))
+    , cmd_type(cmd::COMMAND_NORMAL)
+{
+    lua_getfield(L, LUA_REGISTRYINDEX, "filename");
+    cmd_name = agi::format("automation/lua/%s/%s", check_string(L, -1), check_string(L, 1));
+
+    if (!lua_isfunction(L, 3))
+        error(L, "The macro processing function must be a function");
+
+    if (lua_isfunction(L, 4))
+        cmd_type |= cmd::COMMAND_VALIDATE;
+
+    if (lua_isfunction(L, 5))
+        cmd_type |= cmd::COMMAND_TOGGLE;
+
+    // new table for containing the functions for this feature
+    lua_createtable(L, 0, 3);
+
+    // store processing function
+    push_value(L, "run");
+    lua_pushvalue(L, 3);
+    lua_rawset(L, -3);
+
+    // store validation function
+    push_value(L, "validate");
+    lua_pushvalue(L, 4);
+    lua_rawset(L, -3);
+
+    // store active function
+    push_value(L, "isactive");
+    lua_pushvalue(L, 5);
+    lua_rawset(L, -3);
+
+    // store the table in the registry
+    RegisterFeature();
+
+    LuaScript::GetScriptObject(L)->RegisterCommand(this);
+}
+
+LuaCommand::~LuaCommand()
+{
+    UnregisterFeature();
+    LuaScript::GetScriptObject(L)->UnregisterCommand(this);
+}
+
+static std::vector<int> selected_rows(const agi::Context *c)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    int offset = c->ass->Info.size() + c->ass->Styles.size();
+    std::vector<int> rows;
+    rows.reserve(sel.size());
+    for (auto line : sel)
+        rows.push_back(line->Row + offset + 1);
+    sort(begin(rows), end(rows));
+    return rows;
+}
+
+bool LuaCommand::Validate(const agi::Context *c)
+{
+    if (!(cmd_type & cmd::COMMAND_VALIDATE)) return true;
+
+    set_context(L, c);
+
+    // Error handler goes under the function to call
+    lua_pushcclosure(L, add_stack_trace, 0);
+
+    GetFeatureFunction("validate");
+    auto subsobj = new LuaAssFile(L, c->ass.get());
+
+    push_value(L, selected_rows(c));
+    if (auto active_line = c->selectionController->GetActiveLine())
+        push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
+    else
+        lua_pushnil(L);
+
+    int err = lua_pcall(L, 3, 2, -5 /* three args, function, error handler */);
+    subsobj->ProcessingComplete();
+
+    if (err) {
+        wxLogWarning("Runtime error in Lua macro validation function:\n%s", get_wxstring(L, -1));
+        lua_pop(L, 2);
+        return false;
+    }
+
+    bool result = !!lua_toboolean(L, -2);
+
+    wxString new_help_string(get_wxstring(L, -1));
+    if (new_help_string.size()) {
+        help = new_help_string;
+        cmd_type |= cmd::COMMAND_DYNAMIC_HELP;
+    }
+
+    lua_pop(L, 3); // two return values and error handler
+
+    return result;
+}
+
+void LuaCommand::operator()(agi::Context *c)
+{
+    LuaStackcheck stackcheck(L);
+    set_context(L, c);
+    stackcheck.check_stack(0);
+
+    GetFeatureFunction("run");
+    auto subsobj = new LuaAssFile(L, c->ass.get(), true, true);
+
+    int original_offset = c->ass->Info.size() + c->ass->Styles.size() + 1;
+    auto original_sel = selected_rows(c);
+    int original_active = 0;
+    if (auto active_line = c->selectionController->GetActiveLine())
+        original_active = active_line->Row + original_offset;
+
+    push_value(L, original_sel);
+    push_value(L, original_active);
+
+    try {
+        LuaThreadedCall(L, 3, 2, from_wx(StrDisplay(c)), c->parent, true);
+    }
+    catch (agi::UserCancelException const &) {
+        subsobj->Cancel();
+        stackcheck.check_stack(0);
+        return;
+    }
+
+    auto lines = subsobj->ProcessingComplete(StrDisplay(c));
+
+    AssDialogue *active_line = nullptr;
+    int active_idx = original_active;
+
+    // Check for a new active row
+    if (lua_isnumber(L, -1)) {
+        active_idx = lua_tointeger(L, -1);
+        if (active_idx < 1 || active_idx > (int)lines.size()) {
+            wxLogError("Active row %d is out of bounds (must be 1-%u)", active_idx, lines.size());
+            active_idx = original_active;
+        }
+    }
+
+    stackcheck.check_stack(2);
+    lua_pop(L, 1);
+
+    // top of stack will be selected lines array, if any was returned
+    if (lua_istable(L, -1)) {
+        std::set<AssDialogue *> sel;
+        lua_for_each(L, [&] {
+            if (!lua_isnumber(L, -1))
+                return;
+            int cur = lua_tointeger(L, -1);
+            if (cur < 1 || cur > (int)lines.size())
+            {
+                wxLogError("Selected row %d is out of bounds (must be 1-%u)", cur, lines.size());
+                throw LuaForEachBreak();
+            }
+
+            auto &prev_line = lines[cur - 1][0];
+            if (typeid(prev_line) != typeid(AssDialogue))
+            {
+                wxLogError("Selected row %d is not a dialogue line", cur);
+                throw LuaForEachBreak();
+            }
+
+            auto diag = static_cast<AssDialogue *>(lines[cur - 1]);
+            sel.insert(diag);
+            if (!active_line || active_idx == cur)
+                active_line = diag;
+        });
+
+        AssDialogue *new_active = c->selectionController->GetActiveLine();
+        if (active_line && (active_idx > 0 || !sel.count(new_active)))
+            new_active = active_line;
+        if (sel.empty())
+            sel.insert(new_active);
+        c->selectionController->SetSelectionAndActive(std::move(sel), new_active);
+    }
+    else {
+        lua_pop(L, 1);
+
+        Selection new_sel;
+        AssDialogue *new_active = nullptr;
+
+        int prev = original_offset;
+        auto it = c->ass->Events.begin();
+        for (int row : original_sel) {
+            while (row > prev && it != c->ass->Events.end()) {
+                ++prev;
+                ++it;
+            }
+            if (it == c->ass->Events.end()) break;
+            new_sel.insert(&*it);
+            if (row == original_active)
+                new_active = &*it;
+        }
+
+        if (new_sel.empty() && !c->ass->Events.empty())
+            new_sel.insert(&c->ass->Events.front());
+        if (!new_sel.count(new_active))
+            new_active = *new_sel.begin();
+        c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
+    }
+
+    stackcheck.check_stack(0);
+}
+
+bool LuaCommand::IsActive(const agi::Context *c)
+{
+    if (!(cmd_type & cmd::COMMAND_TOGGLE)) return false;
+
+    LuaStackcheck stackcheck(L);
+
+    set_context(L, c);
+    stackcheck.check_stack(0);
+
+    GetFeatureFunction("isactive");
+    auto subsobj = new LuaAssFile(L, c->ass.get());
+    push_value(L, selected_rows(c));
+    if (auto active_line = c->selectionController->GetActiveLine())
+        push_value(L, active_line->Row + c->ass->Info.size() + c->ass->Styles.size() + 1);
+
+    int err = lua_pcall(L, 3, 1, 0);
+    subsobj->ProcessingComplete();
+
+    bool result = false;
+    if (err)
+        wxLogWarning("Runtime error in Lua macro IsActive function:\n%s", get_wxstring(L, -1));
+    else
+        result = !!lua_toboolean(L, -1);
+
+    // clean up stack (result or error message)
+    stackcheck.check_stack(1);
+    lua_pop(L, 1);
+
+    return result;
+}
+
+// LuaFeatureFilter
+LuaExportFilter::LuaExportFilter(lua_State *L)
+    : ExportFilter(check_string(L, 1), lua_tostring(L, 2), lua_tointeger(L, 3))
+    , LuaFeature(L)
+{
+    if (!lua_isfunction(L, 4))
+        error(L, "The filter processing function must be a function");
+
+    // new table for containing the functions for this feature
+    lua_createtable(L, 0, 2);
+
+    // store processing function
+    push_value(L, "run");
+    lua_pushvalue(L, 4);
+    lua_rawset(L, -3);
+
+    // store config function
+    push_value(L, "config");
+    lua_pushvalue(L, 5);
+    has_config = lua_isfunction(L, -1);
+    lua_rawset(L, -3);
+
+    // store the table in the registry
+    RegisterFeature();
+
+    LuaScript::GetScriptObject(L)->RegisterFilter(this);
+}
+
+int LuaExportFilter::LuaRegister(lua_State *L)
+{
+    static std::mutex mutex;
+    auto filter = agi::make_unique<LuaExportFilter>(L);
+    {
+        std::lock_guard<std::mutex> lock(mutex);
+        AssExportFilterChain::Register(std::move(filter));
+    }
+    return 0;
+}
+
+void LuaExportFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
+{
+    LuaStackcheck stackcheck(L);
+
+    GetFeatureFunction("run");
+    stackcheck.check_stack(1);
+
+    // The entire point of an export filter is to modify the file, but
+    // setting undo points makes no sense
+    auto subsobj = new LuaAssFile(L, subs, true);
+    assert(lua_isuserdata(L, -1));
+    stackcheck.check_stack(2);
+
+    // config
+    if (has_config && config_dialog) {
+        int results_produced = config_dialog->LuaReadBack(L);
+        assert(results_produced == 1);
+        (void) results_produced;	// avoid warning on release builds
+        // TODO, write back stored options here
+    }
+    else {
+        // no config so put an empty table instead
+        lua_newtable(L);
+    }
+    assert(lua_istable(L, -1));
+    stackcheck.check_stack(3);
+
+    try {
+        LuaThreadedCall(L, 2, 0, GetName(), export_dialog, false);
+        stackcheck.check_stack(0);
+        subsobj->ProcessingComplete();
+    }
+    catch (agi::UserCancelException const &) {
+        subsobj->Cancel();
+        throw;
+    }
+}
+
+std::unique_ptr<ScriptDialog> LuaExportFilter::GenerateConfigDialog(wxWindow *parent, agi::Context *c)
+{
+    if (!has_config)
+        return nullptr;
+
+    set_context(L, c);
+
+    GetFeatureFunction("config");
+
+    // prepare function call
+    auto subsobj = new LuaAssFile(L, c->ass.get());
+    // stored options
+    lua_newtable(L); // TODO, nothing for now
+
+    // do call
+    int err = lua_pcall(L, 2, 1, 0);
+    subsobj->ProcessingComplete();
+
+    if (err) {
+        wxLogWarning("Runtime error in Lua config dialog function:\n%s", get_wxstring(L, -1));
+        lua_pop(L, 1); // remove error message
+    }
+    else {
+        // Create config dialogue from table on top of stack
+        config_dialog = new LuaDialog(L, false);
+    }
+
+    return std::unique_ptr<ScriptDialog> {config_dialog};
+}
 }
 
 namespace Automation4 {
-	LuaScriptFactory::LuaScriptFactory()
-	: ScriptFactory("Lua", "*.lua,*.moon")
-	{
-	}
-
-	std::unique_ptr<Script> LuaScriptFactory::Produce(agi::fs::path const& filename) const
-	{
-		if (agi::fs::HasExtension(filename, "lua") || agi::fs::HasExtension(filename, "moon"))
-			return agi::make_unique<LuaScript>(filename);
-		return nullptr;
-	}
+LuaScriptFactory::LuaScriptFactory()
+    : ScriptFactory("Lua", "*.lua,*.moon")
+{
+}
+
+std::unique_ptr<Script> LuaScriptFactory::Produce(agi::fs::path const &filename) const
+{
+    if (agi::fs::HasExtension(filename, "lua") || agi::fs::HasExtension(filename, "moon"))
+        return agi::make_unique<LuaScript>(filename);
+    return nullptr;
+}
 }
diff --git a/src/auto4_lua.h b/src/auto4_lua.h
index 46068173efdc93acaf63ed2a950c5066a7a2031f..91e4ff175ba26b2a59eb5bc8d14e497d75afe2b4 100644
--- a/src/auto4_lua.h
+++ b/src/auto4_lua.h
@@ -39,183 +39,183 @@ class wxWindow;
 struct lua_State;
 
 namespace Automation4 {
-	/// @class LuaAssFile
-	/// @brief Object wrapping an AssFile object for modification through Lua
-	class LuaAssFile {
-		struct PendingCommit {
-			wxString mesage;
-			int modification_type;
-			std::vector<AssEntry*> lines;
-		};
-
-		/// Pointer to file being modified
-		AssFile *ass;
-
-		/// Lua state the object exists in
-		lua_State *L;
-
-		/// Is the feature this object is created for read-only?
-		bool can_modify;
-		/// Is the feature allowed to set undo points?
-		bool can_set_undo;
-		/// throws an error if modification is disallowed
-		void CheckAllowModify();
-		/// throws an error if the line index is out of bounds
-		void CheckBounds(int idx);
-
-		/// How ass file been modified by the script since the last commit
-		int modification_type = 0;
-
-		/// Reference count used to avoid deleting this until both lua and the
-		/// calling C++ code are done with it
-		int references = 2;
-
-		/// Set of subtitle lines being modified; initially a shallow copy of ass->Line
-		std::vector<AssEntry*> lines;
-		bool script_info_copied = false;
-
-		/// Commits to apply once processing completes successfully
-		std::deque<PendingCommit> pending_commits;
-		/// Lines to delete once processing complete successfully
-		std::vector<std::unique_ptr<AssEntry>> lines_to_delete;
-
-		/// Create copies of all of the lines in the script info section if it
-		/// hasn't already happened. This is done lazily, since it only needs
-		/// to happen when the user modifies the headers in some way, which
-		/// most runs of a script will not do.
-		void InitScriptInfoIfNeeded();
-		/// Add the line at the given index to the list of lines to be deleted
-		/// when the script completes, unless it's an AssInfo, since those are
-		/// owned by the container.
-		void QueueLineForDeletion(size_t idx);
-		/// Set the line at the index to the given value
-		void AssignLine(size_t idx, std::unique_ptr<AssEntry> e);
-		void InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e);
-
-		int ObjectIndexRead(lua_State *L);
-		void ObjectIndexWrite(lua_State *L);
-		int ObjectGetLen(lua_State *L);
-		void ObjectDelete(lua_State *L);
-		void ObjectDeleteRange(lua_State *L);
-		void ObjectAppend(lua_State *L);
-		void ObjectInsert(lua_State *L);
-		void ObjectGarbageCollect(lua_State *L);
-		int ObjectIPairs(lua_State *L);
-		int IterNext(lua_State *L);
-
-		int LuaParseKaraokeData(lua_State *L);
-		int LuaGetScriptResolution(lua_State *L);
-
-		void LuaSetUndoPoint(lua_State *L);
-
-		// LuaAssFile can only be deleted by the reference count hitting zero
-		~LuaAssFile();
-	public:
-		static LuaAssFile *GetObjPointer(lua_State *L, int idx, bool allow_expired);
-
-		/// makes a Lua representation of AssEntry and places on the top of the stack
-		void AssEntryToLua(lua_State *L, size_t idx);
-		/// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
-		static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L, AssFile *ass=nullptr);
-
-		/// @brief Signal that the script using this file is now done running
-		/// @param set_undo If there's any uncommitted changes to the file,
-		///                 they will be automatically committed with this
-		///                 description
-		std::vector<AssEntry *> ProcessingComplete(wxString const& undo_description = wxString());
-
-		/// End processing without applying any changes made
-		void Cancel();
-
-		/// Constructor
-		/// @param L lua state
-		/// @param ass File to wrap
-		/// @param can_modify Is modifying the file allowed?
-		/// @param can_set_undo Is setting undo points allowed?
-		LuaAssFile(lua_State *L, AssFile *ass, bool can_modify = false, bool can_set_undo = false);
-	};
-
-	class LuaProgressSink {
-		lua_State *L;
-
-		static int LuaSetProgress(lua_State *L);
-		static int LuaSetTask(lua_State *L);
-		static int LuaSetTitle(lua_State *L);
-		static int LuaGetCancelled(lua_State *L);
-		static int LuaDebugOut(lua_State *L);
-		static int LuaDisplayDialog(lua_State *L);
-		static int LuaDisplayOpenDialog(lua_State *L);
-		static int LuaDisplaySaveDialog(lua_State *L);
-
-	public:
-		LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog = true);
-		~LuaProgressSink();
-
-		static ProgressSink* GetObjPointer(lua_State *L, int idx);
-	};
-
-	/// Base class for controls in dialogs
-	class LuaDialogControl {
-	public:
-		/// Name of this control in the output table
-		std::string name;
-
-		/// Tooltip of this control
-		std::string hint;
-
-		int x, y, width, height;
-
-		/// Create the associated wxControl
-		virtual wxControl *Create(wxWindow *parent) = 0;
-
-		/// Get the default flags to use when inserting this control into a sizer
-		virtual int GetSizerFlags() const { return wxEXPAND; }
-
-		/// Push the current value of the control onto the lua stack. Must not
-		/// touch the GUI as this may be called on a background thread.
-		virtual void LuaReadBack(lua_State *L) = 0;
-
-		/// Does this control have any user-changeable data that can be serialized?
-		virtual bool CanSerialiseValue() const { return false; }
-
-		/// Serialize the control's current value so that it can be stored
-		/// in the script
-		virtual std::string SerialiseValue() const { return ""; }
-
-		/// Restore the control's value from a saved value in the script
-		virtual void UnserialiseValue(const std::string &serialised) { }
-
-		LuaDialogControl(lua_State *L);
-
-		/// Virtual destructor so this can safely be inherited from
-		virtual ~LuaDialogControl() = default;
-	};
-
-	/// A lua-generated dialog or panel in the export options dialog
-	class LuaDialog final : public ScriptDialog {
-		/// Controls in this dialog
-		std::vector<std::unique_ptr<LuaDialogControl>> controls;
-		/// The names and IDs of buttons in this dialog if non-default ones were used
-		std::vector<std::pair<int, std::string>> buttons;
-
-		/// Does the dialog contain any buttons
-		bool use_buttons;
+/// @class LuaAssFile
+/// @brief Object wrapping an AssFile object for modification through Lua
+class LuaAssFile {
+    struct PendingCommit {
+        wxString mesage;
+        int modification_type;
+        std::vector<AssEntry *> lines;
+    };
+
+    /// Pointer to file being modified
+    AssFile *ass;
+
+    /// Lua state the object exists in
+    lua_State *L;
+
+    /// Is the feature this object is created for read-only?
+    bool can_modify;
+    /// Is the feature allowed to set undo points?
+    bool can_set_undo;
+    /// throws an error if modification is disallowed
+    void CheckAllowModify();
+    /// throws an error if the line index is out of bounds
+    void CheckBounds(int idx);
+
+    /// How ass file been modified by the script since the last commit
+    int modification_type = 0;
+
+    /// Reference count used to avoid deleting this until both lua and the
+    /// calling C++ code are done with it
+    int references = 2;
+
+    /// Set of subtitle lines being modified; initially a shallow copy of ass->Line
+    std::vector<AssEntry *> lines;
+    bool script_info_copied = false;
+
+    /// Commits to apply once processing completes successfully
+    std::deque<PendingCommit> pending_commits;
+    /// Lines to delete once processing complete successfully
+    std::vector<std::unique_ptr<AssEntry>> lines_to_delete;
+
+    /// Create copies of all of the lines in the script info section if it
+    /// hasn't already happened. This is done lazily, since it only needs
+    /// to happen when the user modifies the headers in some way, which
+    /// most runs of a script will not do.
+    void InitScriptInfoIfNeeded();
+    /// Add the line at the given index to the list of lines to be deleted
+    /// when the script completes, unless it's an AssInfo, since those are
+    /// owned by the container.
+    void QueueLineForDeletion(size_t idx);
+    /// Set the line at the index to the given value
+    void AssignLine(size_t idx, std::unique_ptr<AssEntry> e);
+    void InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e);
+
+    int ObjectIndexRead(lua_State *L);
+    void ObjectIndexWrite(lua_State *L);
+    int ObjectGetLen(lua_State *L);
+    void ObjectDelete(lua_State *L);
+    void ObjectDeleteRange(lua_State *L);
+    void ObjectAppend(lua_State *L);
+    void ObjectInsert(lua_State *L);
+    void ObjectGarbageCollect(lua_State *L);
+    int ObjectIPairs(lua_State *L);
+    int IterNext(lua_State *L);
+
+    int LuaParseKaraokeData(lua_State *L);
+    int LuaGetScriptResolution(lua_State *L);
+
+    void LuaSetUndoPoint(lua_State *L);
+
+    // LuaAssFile can only be deleted by the reference count hitting zero
+    ~LuaAssFile();
+public:
+    static LuaAssFile *GetObjPointer(lua_State *L, int idx, bool allow_expired);
+
+    /// makes a Lua representation of AssEntry and places on the top of the stack
+    void AssEntryToLua(lua_State *L, size_t idx);
+    /// assumes a Lua representation of AssEntry on the top of the stack, and creates an AssEntry object of it
+    static std::unique_ptr<AssEntry> LuaToAssEntry(lua_State *L, AssFile *ass = nullptr);
+
+    /// @brief Signal that the script using this file is now done running
+    /// @param set_undo If there's any uncommitted changes to the file,
+    ///                 they will be automatically committed with this
+    ///                 description
+    std::vector<AssEntry *> ProcessingComplete(wxString const &undo_description = wxString());
+
+    /// End processing without applying any changes made
+    void Cancel();
+
+    /// Constructor
+    /// @param L lua state
+    /// @param ass File to wrap
+    /// @param can_modify Is modifying the file allowed?
+    /// @param can_set_undo Is setting undo points allowed?
+    LuaAssFile(lua_State *L, AssFile *ass, bool can_modify = false, bool can_set_undo = false);
+};
+
+class LuaProgressSink {
+    lua_State *L;
+
+    static int LuaSetProgress(lua_State *L);
+    static int LuaSetTask(lua_State *L);
+    static int LuaSetTitle(lua_State *L);
+    static int LuaGetCancelled(lua_State *L);
+    static int LuaDebugOut(lua_State *L);
+    static int LuaDisplayDialog(lua_State *L);
+    static int LuaDisplayOpenDialog(lua_State *L);
+    static int LuaDisplaySaveDialog(lua_State *L);
+
+public:
+    LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog = true);
+    ~LuaProgressSink();
+
+    static ProgressSink *GetObjPointer(lua_State *L, int idx);
+};
+
+/// Base class for controls in dialogs
+class LuaDialogControl {
+public:
+    /// Name of this control in the output table
+    std::string name;
+
+    /// Tooltip of this control
+    std::string hint;
+
+    int x, y, width, height;
+
+    /// Create the associated wxControl
+    virtual wxControl *Create(wxWindow *parent) = 0;
+
+    /// Get the default flags to use when inserting this control into a sizer
+    virtual int GetSizerFlags() const { return wxEXPAND; }
+
+    /// Push the current value of the control onto the lua stack. Must not
+    /// touch the GUI as this may be called on a background thread.
+    virtual void LuaReadBack(lua_State *L) = 0;
+
+    /// Does this control have any user-changeable data that can be serialized?
+    virtual bool CanSerialiseValue() const { return false; }
+
+    /// Serialize the control's current value so that it can be stored
+    /// in the script
+    virtual std::string SerialiseValue() const { return ""; }
+
+    /// Restore the control's value from a saved value in the script
+    virtual void UnserialiseValue(const std::string &serialised) { }
+
+    LuaDialogControl(lua_State *L);
+
+    /// Virtual destructor so this can safely be inherited from
+    virtual ~LuaDialogControl() = default;
+};
+
+/// A lua-generated dialog or panel in the export options dialog
+class LuaDialog final : public ScriptDialog {
+    /// Controls in this dialog
+    std::vector<std::unique_ptr<LuaDialogControl>> controls;
+    /// The names and IDs of buttons in this dialog if non-default ones were used
+    std::vector<std::pair<int, std::string>> buttons;
+
+    /// Does the dialog contain any buttons
+    bool use_buttons;
 
-		/// Id of the button pushed (once a button has been pushed)
-		int button_pushed = -1;
+    /// Id of the button pushed (once a button has been pushed)
+    int button_pushed = -1;
 
-		wxWindow *window = nullptr;
+    wxWindow *window = nullptr;
 
-	public:
-		LuaDialog(lua_State *L, bool include_buttons);
-
-		/// Push the values of the controls in this dialog onto the lua stack
-		/// in a single table
-		int LuaReadBack(lua_State *L);
+public:
+    LuaDialog(lua_State *L, bool include_buttons);
+
+    /// Push the values of the controls in this dialog onto the lua stack
+    /// in a single table
+    int LuaReadBack(lua_State *L);
 
-		// ScriptDialog implementation
-		wxWindow* CreateWindow(wxWindow *parent) override;
-		std::string Serialise() override;
-		void Unserialise(const std::string &serialised) override;
-	};
+    // ScriptDialog implementation
+    wxWindow *CreateWindow(wxWindow *parent) override;
+    std::string Serialise() override;
+    void Unserialise(const std::string &serialised) override;
+};
 }
diff --git a/src/auto4_lua_assfile.cpp b/src/auto4_lua_assfile.cpp
index 91e66d932697da943408208ac6476b385b2a233b..e1675db701b5807f19512092348ac76ee1d9d0d8 100644
--- a/src/auto4_lua_assfile.cpp
+++ b/src/auto4_lua_assfile.cpp
@@ -52,726 +52,725 @@
 #include <memory>
 
 namespace {
-	using namespace agi::lua;
-
-	DEFINE_EXCEPTION(BadField, Automation4::MacroRunError);
-	BadField bad_field(const char *expected_type, const char *name, const char *line_clasee)
-	{
-		return BadField(std::string("Invalid or missing field '") + name + "' in '" + line_clasee + "' class subtitle line (expected " + expected_type + ")");
-	}
-
-	std::string get_string_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);
-		std::string ret(lua_tostring(L, -1));
-		lua_pop(L, 1);
-		return ret;
-	}
-
-	double get_double_field(lua_State *L, const char *name, const char *line_class)
-	{
-		lua_getfield(L, -1, name);
-		if (!lua_isnumber(L, -1))
-			throw bad_field("number", name, line_class);
-		double ret = lua_tonumber(L, -1);
-		lua_pop(L, 1);
-		return ret;
-	}
-
-	int get_int_field(lua_State *L, const char *name, const char *line_class)
-	{
-		lua_getfield(L, -1, name);
-		if (!lua_isnumber(L, -1))
-			throw bad_field("number", name, line_class);
-		int ret = lua_tointeger(L, -1);
-		lua_pop(L, 1);
-		return ret;
-	}
-
-	bool get_bool_field(lua_State *L, const char *name, const char *line_class)
-	{
-		lua_getfield(L, -1, name);
-		if (!lua_isboolean(L, -1))
-			throw bad_field("boolean", name, line_class);
-		bool ret = !!lua_toboolean(L, -1);
-		lua_pop(L, 1);
-		return ret;
-	}
-
-	using namespace Automation4;
-	template<int (LuaAssFile::*closure)(lua_State *)>
-	int closure_wrapper(lua_State *L)
-	{
-		return (LuaAssFile::GetObjPointer(L, lua_upvalueindex(1), false)->*closure)(L);
-	}
-
-	template<void (LuaAssFile::*closure)(lua_State *), bool allow_expired>
-	int closure_wrapper_v(lua_State *L)
-	{
-		(LuaAssFile::GetObjPointer(L, lua_upvalueindex(1), allow_expired)->*closure)(L);
-		return 0;
-	}
-
-	int modification_mask(AssEntry *e)
-	{
-		if (!e) return AssFile::COMMIT_SCRIPTINFO;
-		switch (e->Group()) {
-			case AssEntryGroup::DIALOGUE: return AssFile::COMMIT_DIAG_ADDREM;
-			case AssEntryGroup::STYLE:    return AssFile::COMMIT_STYLES;
-			default:                      return AssFile::COMMIT_SCRIPTINFO;
-		}
-	}
-
-	template<typename T, typename U>
-	const T *check_cast_constptr(const U *value) {
-		return typeid(const T) == typeid(*value) ? static_cast<const T *>(value) : nullptr;
-	}
+using namespace agi::lua;
+
+DEFINE_EXCEPTION(BadField, Automation4::MacroRunError);
+BadField bad_field(const char *expected_type, const char *name, const char *line_clasee)
+{
+    return BadField(std::string("Invalid or missing field '") + name + "' in '" + line_clasee + "' class subtitle line (expected " + expected_type + ")");
+}
+
+std::string get_string_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);
+    std::string ret(lua_tostring(L, -1));
+    lua_pop(L, 1);
+    return ret;
+}
+
+double get_double_field(lua_State *L, const char *name, const char *line_class)
+{
+    lua_getfield(L, -1, name);
+    if (!lua_isnumber(L, -1))
+        throw bad_field("number", name, line_class);
+    double ret = lua_tonumber(L, -1);
+    lua_pop(L, 1);
+    return ret;
+}
+
+int get_int_field(lua_State *L, const char *name, const char *line_class)
+{
+    lua_getfield(L, -1, name);
+    if (!lua_isnumber(L, -1))
+        throw bad_field("number", name, line_class);
+    int ret = lua_tointeger(L, -1);
+    lua_pop(L, 1);
+    return ret;
+}
+
+bool get_bool_field(lua_State *L, const char *name, const char *line_class)
+{
+    lua_getfield(L, -1, name);
+    if (!lua_isboolean(L, -1))
+        throw bad_field("boolean", name, line_class);
+    bool ret = !!lua_toboolean(L, -1);
+    lua_pop(L, 1);
+    return ret;
+}
+
+using namespace Automation4;
+template<int (LuaAssFile::*closure)(lua_State *)>
+int closure_wrapper(lua_State *L)
+{
+    return (LuaAssFile::GetObjPointer(L, lua_upvalueindex(1), false)->*closure)(L);
+}
+
+template<void (LuaAssFile::*closure)(lua_State *), bool allow_expired>
+int closure_wrapper_v(lua_State *L)
+{
+    (LuaAssFile::GetObjPointer(L, lua_upvalueindex(1), allow_expired)->*closure)(L);
+    return 0;
+}
+
+int modification_mask(AssEntry *e)
+{
+    if (!e) return AssFile::COMMIT_SCRIPTINFO;
+    switch (e->Group()) {
+    case AssEntryGroup::DIALOGUE: return AssFile::COMMIT_DIAG_ADDREM;
+    case AssEntryGroup::STYLE:    return AssFile::COMMIT_STYLES;
+    default:                      return AssFile::COMMIT_SCRIPTINFO;
+    }
+}
+
+template<typename T, typename U>
+const T *check_cast_constptr(const U *value)
+{
+    return typeid(const T) == typeid(*value) ? static_cast<const T *>(value) : nullptr;
+}
 }
 
 namespace Automation4 {
-	LuaAssFile::~LuaAssFile() { }
+LuaAssFile::~LuaAssFile() { }
+
+void LuaAssFile::CheckAllowModify()
+{
+    if (!can_modify)
+        error(L, "Attempt to modify subtitles in read-only feature context.");
+}
+
+void LuaAssFile::CheckBounds(int idx)
+{
+    if (idx <= 0 || idx > (int)lines.size())
+        error(L, "Requested out-of-range line from subtitle file: %d", idx);
+}
+
+void LuaAssFile::AssEntryToLua(lua_State *L, size_t idx)
+{
+    lua_newtable(L);
+
+    const AssEntry *e = lines[idx];
+    if (!e)
+        e = &ass->Info[idx];
+
+    set_field(L, "section", e->GroupHeader());
+
+    if (auto info = check_cast_constptr<AssInfo>(e)) {
+        set_field(L, "raw", info->GetEntryData());
+        set_field(L, "key", info->Key());
+        set_field(L, "value", info->Value());
+        set_field(L, "class", "info");
+    }
+    else if (auto dia = check_cast_constptr<AssDialogue>(e)) {
+        set_field(L, "raw", dia->GetEntryData());
+        set_field(L, "comment", dia->Comment);
+
+        set_field(L, "layer", dia->Layer);
+
+        set_field(L, "start_time", dia->Start);
+        set_field(L, "end_time", dia->End);
+
+        set_field(L, "style", dia->Style);
+        set_field(L, "actor", dia->Actor);
+        set_field(L, "effect", dia->Effect);
+
+        set_field(L, "margin_l", dia->Margin[0]);
+        set_field(L, "margin_r", dia->Margin[1]);
+        set_field(L, "margin_t", dia->Margin[2]);
+        set_field(L, "margin_b", dia->Margin[2]);
+
+        set_field(L, "text", dia->Text);
+
+        // create extradata table
+        lua_newtable(L);
+        for (auto const &ed : ass->GetExtradata(dia->ExtradataIds)) {
+            push_value(L, ed.key);
+            push_value(L, ed.value);
+            lua_settable(L, -3);
+        }
+        lua_setfield(L, -2, "extra");
+
+        set_field(L, "class", "dialogue");
+    }
+    else if (auto sty = check_cast_constptr<AssStyle>(e)) {
+        set_field(L, "raw", sty->GetEntryData());
+        set_field(L, "name", sty->name);
+
+        set_field(L, "fontname", sty->font);
+        set_field(L, "fontsize", sty->fontsize);
+
+        set_field(L, "color1", sty->primary.GetAssStyleFormatted() + "&");
+        set_field(L, "color2", sty->secondary.GetAssStyleFormatted() + "&");
+        set_field(L, "color3", sty->outline.GetAssStyleFormatted() + "&");
+        set_field(L, "color4", sty->shadow.GetAssStyleFormatted() + "&");
+
+        set_field(L, "bold", sty->bold);
+        set_field(L, "italic", sty->italic);
+        set_field(L, "underline", sty->underline);
+        set_field(L, "strikeout", sty->strikeout);
+
+        set_field(L, "scale_x", sty->scalex);
+        set_field(L, "scale_y", sty->scaley);
+
+        set_field(L, "spacing", sty->spacing);
+
+        set_field(L, "angle", sty->angle);
+
+        set_field(L, "borderstyle", sty->borderstyle);
+        set_field(L, "outline", sty->outline_w);
+        set_field(L, "shadow", sty->shadow_w);
+
+        set_field(L, "align", sty->alignment);
+
+        set_field(L, "margin_l", sty->Margin[0]);
+        set_field(L, "margin_r", sty->Margin[1]);
+        set_field(L, "margin_t", sty->Margin[2]);
+        set_field(L, "margin_b", sty->Margin[2]);
+
+        set_field(L, "encoding", sty->encoding);
+
+        set_field(L, "relative_to", 2);// From STS.h: "0: window, 1: video, 2: undefined (~window)"
+
+        set_field(L, "class", "style");
+    }
+    else {
+        assert(false);
+    }
+}
+
+std::unique_ptr<AssEntry> LuaAssFile::LuaToAssEntry(lua_State *L, AssFile *ass)
+{
+    // assume an assentry table is on the top of the stack
+    // convert it to a real AssEntry object, and pop the table from the stack
+
+    if (!lua_istable(L, -1))
+        error(L, "Can't convert a non-table value to AssEntry");
+
+    lua_getfield(L, -1, "class");
+    if (!lua_isstring(L, -1))
+        error(L, "Table lacks 'class' field, can't convert to AssEntry");
+
+    std::string lclass(lua_tostring(L, -1));
+    boost::to_lower(lclass);
+    lua_pop(L, 1);
+
+    std::unique_ptr<AssEntry> result;
+    if (lclass == "info")
+        result = agi::make_unique<AssInfo>(get_string_field(L, "key", "info"), get_string_field(L, "value", "info"));
+    else if (lclass == "style") {
+        auto sty = new AssStyle;
+        result.reset(sty);
+        sty->name = get_string_field(L, "name", "style");
+        sty->font = get_string_field(L, "fontname", "style");
+        sty->fontsize = get_double_field(L, "fontsize", "style");
+        sty->primary = get_string_field(L, "color1", "style");
+        sty->secondary = get_string_field(L, "color2", "style");
+        sty->outline = get_string_field(L, "color3", "style");
+        sty->shadow = get_string_field(L, "color4", "style");
+        sty->bold = get_bool_field(L, "bold", "style");
+        sty->italic = get_bool_field(L, "italic", "style");
+        sty->underline = get_bool_field(L, "underline", "style");
+        sty->strikeout = get_bool_field(L, "strikeout", "style");
+        sty->scalex = get_double_field(L, "scale_x", "style");
+        sty->scaley = get_double_field(L, "scale_y", "style");
+        sty->spacing = get_double_field(L, "spacing", "style");
+        sty->angle = get_double_field(L, "angle", "style");
+        sty->borderstyle = get_int_field(L, "borderstyle", "style");
+        sty->outline_w = get_double_field(L, "outline", "style");
+        sty->shadow_w = get_double_field(L, "shadow", "style");
+        sty->alignment = get_int_field(L, "align", "style");
+        sty->Margin[0] = get_int_field(L, "margin_l", "style");
+        sty->Margin[1] = get_int_field(L, "margin_r", "style");
+        sty->Margin[2] = get_int_field(L, "margin_t", "style");
+        sty->encoding = get_int_field(L, "encoding", "style");
+        sty->UpdateData();
+    }
+    else if (lclass == "dialogue") {
+        assert(ass != 0); // since we need AssFile::AddExtradata
+        auto dia = new AssDialogue;
+        result.reset(dia);
+
+        dia->Comment = get_bool_field(L, "comment", "dialogue");
+        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_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_string_field(L, "effect", "dialogue");
+        dia->Text = get_string_field(L, "text", "dialogue");
+
+        std::vector<uint32_t> new_ids;
+
+        lua_getfield(L, -1, "extra");
+        auto type = lua_type(L, -1);
+        if (type == LUA_TTABLE) {
+            lua_for_each(L, [&] {
+                if (lua_type(L, -2) != LUA_TSTRING) return;
+                new_ids.push_back(ass->AddExtradata(
+                                      get_string_or_default(L, -2),
+                                      get_string_or_default(L, -1)));
+            });
+            std::sort(begin(new_ids), end(new_ids));
+            dia->ExtradataIds = std::move(new_ids);
+        }
+        else if (type != LUA_TNIL) {
+            error(L, "dialogue extradata must be a table");
+        }
+    }
+    else {
+        error(L, "Found line with unknown class: %s", lclass.c_str());
+        return nullptr;
+    }
+
+    return result;
+}
 
-	void LuaAssFile::CheckAllowModify()
-	{
-		if (!can_modify)
-			error(L, "Attempt to modify subtitles in read-only feature context.");
-	}
-
-	void LuaAssFile::CheckBounds(int idx)
-	{
-		if (idx <= 0 || idx > (int)lines.size())
-			error(L, "Requested out-of-range line from subtitle file: %d", idx);
-	}
-
-	void LuaAssFile::AssEntryToLua(lua_State *L, size_t idx)
-	{
-		lua_newtable(L);
-
-		const AssEntry *e = lines[idx];
-		if (!e)
-			e = &ass->Info[idx];
-
-		set_field(L, "section", e->GroupHeader());
-
-		if (auto info = check_cast_constptr<AssInfo>(e)) {
-			set_field(L, "raw", info->GetEntryData());
-			set_field(L, "key", info->Key());
-			set_field(L, "value", info->Value());
-			set_field(L, "class", "info");
-		}
-		else if (auto dia = check_cast_constptr<AssDialogue>(e)) {
-			set_field(L, "raw", dia->GetEntryData());
-			set_field(L, "comment", dia->Comment);
-
-			set_field(L, "layer", dia->Layer);
-
-			set_field(L, "start_time", dia->Start);
-			set_field(L, "end_time", dia->End);
-
-			set_field(L, "style", dia->Style);
-			set_field(L, "actor", dia->Actor);
-			set_field(L, "effect", dia->Effect);
-
-			set_field(L, "margin_l", dia->Margin[0]);
-			set_field(L, "margin_r", dia->Margin[1]);
-			set_field(L, "margin_t", dia->Margin[2]);
-			set_field(L, "margin_b", dia->Margin[2]);
-
-			set_field(L, "text", dia->Text);
-
-			// create extradata table
-			lua_newtable(L);
-			for (auto const& ed : ass->GetExtradata(dia->ExtradataIds)) {
-				push_value(L, ed.key);
-				push_value(L, ed.value);
-				lua_settable(L, -3);
-			}
-			lua_setfield(L, -2, "extra");
-
-			set_field(L, "class", "dialogue");
-		}
-		else if (auto sty = check_cast_constptr<AssStyle>(e)) {
-			set_field(L, "raw", sty->GetEntryData());
-			set_field(L, "name", sty->name);
-
-			set_field(L, "fontname", sty->font);
-			set_field(L, "fontsize", sty->fontsize);
-
-			set_field(L, "color1", sty->primary.GetAssStyleFormatted() + "&");
-			set_field(L, "color2", sty->secondary.GetAssStyleFormatted() + "&");
-			set_field(L, "color3", sty->outline.GetAssStyleFormatted() + "&");
-			set_field(L, "color4", sty->shadow.GetAssStyleFormatted() + "&");
-
-			set_field(L, "bold", sty->bold);
-			set_field(L, "italic", sty->italic);
-			set_field(L, "underline", sty->underline);
-			set_field(L, "strikeout", sty->strikeout);
-
-			set_field(L, "scale_x", sty->scalex);
-			set_field(L, "scale_y", sty->scaley);
-
-			set_field(L, "spacing", sty->spacing);
-
-			set_field(L, "angle", sty->angle);
-
-			set_field(L, "borderstyle", sty->borderstyle);
-			set_field(L, "outline", sty->outline_w);
-			set_field(L, "shadow", sty->shadow_w);
-
-			set_field(L, "align", sty->alignment);
-
-			set_field(L, "margin_l", sty->Margin[0]);
-			set_field(L, "margin_r", sty->Margin[1]);
-			set_field(L, "margin_t", sty->Margin[2]);
-			set_field(L, "margin_b", sty->Margin[2]);
-
-			set_field(L, "encoding", sty->encoding);
-
-			set_field(L, "relative_to", 2);// From STS.h: "0: window, 1: video, 2: undefined (~window)"
-
-			set_field(L, "class", "style");
-		}
-		else {
-			assert(false);
-		}
-	}
-
-	std::unique_ptr<AssEntry> LuaAssFile::LuaToAssEntry(lua_State *L, AssFile *ass)
-	{
-		// assume an assentry table is on the top of the stack
-		// convert it to a real AssEntry object, and pop the table from the stack
-
-		if (!lua_istable(L, -1))
-			error(L, "Can't convert a non-table value to AssEntry");
-
-		lua_getfield(L, -1, "class");
-		if (!lua_isstring(L, -1))
-			error(L, "Table lacks 'class' field, can't convert to AssEntry");
-
-		std::string lclass(lua_tostring(L, -1));
-		boost::to_lower(lclass);
-		lua_pop(L, 1);
-
-		std::unique_ptr<AssEntry> result;
-		if (lclass == "info")
-			result = agi::make_unique<AssInfo>(get_string_field(L, "key", "info"), get_string_field(L, "value", "info"));
-		else if (lclass == "style") {
-			auto sty = new AssStyle;
-			result.reset(sty);
-			sty->name = get_string_field(L, "name", "style");
-			sty->font = get_string_field(L, "fontname", "style");
-			sty->fontsize = get_double_field(L, "fontsize", "style");
-			sty->primary = get_string_field(L, "color1", "style");
-			sty->secondary = get_string_field(L, "color2", "style");
-			sty->outline = get_string_field(L, "color3", "style");
-			sty->shadow = get_string_field(L, "color4", "style");
-			sty->bold = get_bool_field(L, "bold", "style");
-			sty->italic = get_bool_field(L, "italic", "style");
-			sty->underline = get_bool_field(L, "underline", "style");
-			sty->strikeout = get_bool_field(L, "strikeout", "style");
-			sty->scalex = get_double_field(L, "scale_x", "style");
-			sty->scaley = get_double_field(L, "scale_y", "style");
-			sty->spacing = get_double_field(L, "spacing", "style");
-			sty->angle = get_double_field(L, "angle", "style");
-			sty->borderstyle = get_int_field(L, "borderstyle", "style");
-			sty->outline_w = get_double_field(L, "outline", "style");
-			sty->shadow_w = get_double_field(L, "shadow", "style");
-			sty->alignment = get_int_field(L, "align", "style");
-			sty->Margin[0] = get_int_field(L, "margin_l", "style");
-			sty->Margin[1] = get_int_field(L, "margin_r", "style");
-			sty->Margin[2] = get_int_field(L, "margin_t", "style");
-			sty->encoding = get_int_field(L, "encoding", "style");
-			sty->UpdateData();
-		}
-		else if (lclass == "dialogue") {
-			assert(ass != 0); // since we need AssFile::AddExtradata
-			auto dia = new AssDialogue;
-			result.reset(dia);
-
-			dia->Comment = get_bool_field(L, "comment", "dialogue");
-			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_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_string_field(L, "effect", "dialogue");
-			dia->Text = get_string_field(L, "text", "dialogue");
-
-			std::vector<uint32_t> new_ids;
-
-			lua_getfield(L, -1, "extra");
-			auto type = lua_type(L, -1);
-			if (type == LUA_TTABLE) {
-				lua_for_each(L, [&] {
-					if (lua_type(L, -2) != LUA_TSTRING) return;
-					new_ids.push_back(ass->AddExtradata(
-						get_string_or_default(L, -2),
-						get_string_or_default(L, -1)));
-				});
-				std::sort(begin(new_ids), end(new_ids));
-				dia->ExtradataIds = std::move(new_ids);
-			}
-			else if (type != LUA_TNIL) {
-				error(L, "dialogue extradata must be a table");
-			}
-		}
-		else {
-			error(L, "Found line with unknown class: %s", lclass.c_str());
-			return nullptr;
-		}
-
-		return result;
-	}
-
-	int LuaAssFile::ObjectIndexRead(lua_State *L)
-	{
-		switch (lua_type(L, 2)) {
-			case LUA_TNUMBER:
-			{
-				// read an indexed AssEntry
-				int idx = lua_tointeger(L, 2);
-				CheckBounds(idx);
-				AssEntryToLua(L, idx - 1);
-				return 1;
-			}
-
-			case LUA_TSTRING:
-			{
-				// either return n or a function doing further stuff
-				const char *idx = lua_tostring(L, 2);
-
-				if (strcmp(idx, "n") == 0) {
-					// get number of items
-					lua_pushnumber(L, lines.size());
-					return 1;
-				}
-
-				lua_pushvalue(L, 1);
-				if (strcmp(idx, "delete") == 0)
-					lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectDelete, false>, 1);
-				else if (strcmp(idx, "deleterange") == 0)
-					lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectDeleteRange, false>, 1);
-				else if (strcmp(idx, "insert") == 0)
-					lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectInsert, false>, 1);
-				else if (strcmp(idx, "append") == 0)
-					lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectAppend, false>, 1);
-				else if (strcmp(idx, "script_resolution") == 0)
-					lua_pushcclosure(L, closure_wrapper<&LuaAssFile::LuaGetScriptResolution>, 1);
-				else {
-					// idiot
-					lua_pop(L, 1);
-					return error(L, "Invalid indexing in Subtitle File object: '%s'", idx);
-				}
-
-				return 1;
-			}
-
-			default:
-				// crap, user is stupid!
-				return error(L, "Attempt to index a Subtitle File object with value of type '%s'.", lua_typename(L, lua_type(L, 2)));
-		}
-
-		assert(false);
-		return 0;
-	}
-
-	void LuaAssFile::InitScriptInfoIfNeeded()
-	{
-		if (script_info_copied) return;
-		size_t i = 0;
-		for (auto const& info : ass->Info) {
-			// Just in case an insane user inserted non-info lines into the
-			// script info section...
-			while (lines[i]) ++i;
-			lines_to_delete.emplace_back(agi::make_unique<AssInfo>(info));
-			lines[i++] = lines_to_delete.back().get();
-		}
-		script_info_copied = true;
-	}
-
-	void LuaAssFile::QueueLineForDeletion(size_t idx)
-	{
-		if (!lines[idx] || lines[idx]->Group() == AssEntryGroup::INFO)
-			InitScriptInfoIfNeeded();
-		else
-			lines_to_delete.emplace_back(lines[idx]);
-	}
-
-	void LuaAssFile::AssignLine(size_t idx, std::unique_ptr<AssEntry> e)
-	{
-		auto group = e->Group();
-		if (group == AssEntryGroup::INFO)
-			InitScriptInfoIfNeeded();
-		lines[idx] = e.get();
-		if (group == AssEntryGroup::INFO)
-			lines_to_delete.emplace_back(std::move(e));
-		else
-			e.release();
-	}
-
-	void LuaAssFile::InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e)
-	{
-		auto group = e->Group();
-		if (group == AssEntryGroup::INFO)
-			InitScriptInfoIfNeeded();
-		vec.insert(vec.begin() + idx, e.get());
-		if (group == AssEntryGroup::INFO)
-			lines_to_delete.emplace_back(std::move(e));
-		else
-			e.release();
-	}
-
-	void LuaAssFile::ObjectIndexWrite(lua_State *L)
-	{
-		// instead of implementing everything twice, just call the other modification-functions from here
-		// after modifying the stack to match their expectations
-
-		CheckAllowModify();
-
-		int n = check_int(L, 2);
-		if (n < 0) {
-			// insert line so new index is n
-			lua_remove(L, 1);
-			lua_pushinteger(L, -n);
-			lua_replace(L, 1);
-			ObjectInsert(L);
-		}
-		else if (n == 0) {
-			// append line to list
-			lua_remove(L, 1);
-			lua_remove(L, 1);
-			ObjectAppend(L);
-		}
-		else {
-			// replace line at index n or delete
-			if (!lua_isnil(L, 3)) {
-				// insert
-				CheckBounds(n);
-
-				auto e = LuaToAssEntry(L, ass);
-				modification_type |= modification_mask(e.get());
-				QueueLineForDeletion(n - 1);
-				AssignLine(n - 1, std::move(e));
-			}
-			else {
-				// delete
-				lua_remove(L, 1);
-				lua_remove(L, 1);
-				ObjectDelete(L);
-			}
-		}
-	}
-
-	int LuaAssFile::ObjectGetLen(lua_State *L)
-	{
-		lua_pushnumber(L, lines.size());
-		return 1;
-	}
-
-	void LuaAssFile::ObjectDelete(lua_State *L)
-	{
-		CheckAllowModify();
-
-		// get number of items to delete
-		int itemcount = lua_gettop(L);
-		if (itemcount == 0) return;
-
-		std::vector<size_t> ids;
-		if (itemcount == 1 && lua_istable(L, 1)) {
-			lua_pushvalue(L, 1);
-			lua_for_each(L, [&] {
-				size_t n = check_uint(L, -1);
-				argcheck(L, n > 0 && n <= lines.size(), 1, "Out of range line index");
-				ids.push_back(n - 1);
-			});
-		}
-		else {
-			ids.reserve(itemcount);
-			while (itemcount > 0) {
-				size_t n = check_uint(L, -1);
-				argcheck(L, n > 0 && n <= lines.size(), itemcount, "Out of range line index");
-				ids.push_back(n - 1);
-				--itemcount;
-			}
-		}
-
-		sort(ids.begin(), ids.end());
-
-		size_t id_idx = 0, out = 0;
-		for (size_t i = 0; i < lines.size(); ++i) {
-			if (id_idx < ids.size() && ids[id_idx] == i) {
-				modification_type |= modification_mask(lines[i]);
-				QueueLineForDeletion(i);
-				++id_idx;
-			}
-			else {
-				lines[out++] = lines[i];
-			}
-		}
-
-		lines.erase(lines.begin() + out, lines.end());
-	}
-
-	void LuaAssFile::ObjectDeleteRange(lua_State *L)
-	{
-		CheckAllowModify();
-
-		size_t a = std::max<size_t>(check_uint(L, 1), 1) - 1;
-		size_t b = std::min<size_t>(check_uint(L, 2), lines.size());
-
-		if (a >= b) return;
-
-		for (size_t i = a; i < b; ++i) {
-			modification_type |= modification_mask(lines[i]);
-			QueueLineForDeletion(i);
-		}
-
-		lines.erase(lines.begin() + a, lines.begin() + b);
-	}
-
-	void LuaAssFile::ObjectAppend(lua_State *L)
-	{
-		CheckAllowModify();
-
-		int n = lua_gettop(L);
-
-		for (int i = 1; i <= n; i++) {
-			lua_pushvalue(L, i);
-			auto e = LuaToAssEntry(L, ass);
-			modification_type |= modification_mask(e.get());
-
-			if (lines.empty()) {
-				InsertLine(lines, 0, std::move(e));
-				continue;
-			}
-
-			// Find the appropriate place to put it
-			auto group = e->Group();
-			for (size_t i = lines.size(); i > 0; --i) {
-				auto cur_group = lines[i - 1] ? lines[i - 1]->Group() : AssEntryGroup::INFO;
-				if (cur_group == group) {
-					InsertLine(lines, i, std::move(e));
-					break;
-				}
-			}
-
-			// No lines of this type exist already, so just append it to the end
-			if (e) InsertLine(lines, lines.size(), std::move(e));
-		}
-	}
-
-	void LuaAssFile::ObjectInsert(lua_State *L)
-	{
-		CheckAllowModify();
-
-		size_t before = check_uint(L, 1);
-
-		// + 1 to allow appending at the end of the file
-		argcheck(L, before > 0 && before <= lines.size() + 1, 1,
-			"Out of range line index");
-
-		if (before == lines.size() + 1) {
-			lua_remove(L, 1);
-			ObjectAppend(L);
-			return;
-		}
-
-		int n = lua_gettop(L);
-		std::vector<AssEntry *> new_entries;
-		new_entries.reserve(n - 1);
-		for (int i = 2; i <= n; i++) {
-			lua_pushvalue(L, i);
-			auto e = LuaToAssEntry(L, ass);
-			modification_type |= modification_mask(e.get());
-			InsertLine(new_entries, i - 2, std::move(e));
-			lua_pop(L, 1);
-		}
-		lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end());
-	}
-
-	void LuaAssFile::ObjectGarbageCollect(lua_State *L)
-	{
-		references--;
-		if (!references) delete this;
-		LOG_D("automation/lua") << "Garbage collected LuaAssFile";
-	}
-
-	int LuaAssFile::ObjectIPairs(lua_State *L)
-	{
-		lua_pushvalue(L, lua_upvalueindex(1)); // push 'this' as userdata
-		lua_pushcclosure(L, closure_wrapper<&LuaAssFile::IterNext>, 1);
-		lua_pushnil(L);
-		push_value(L, 0);
-		return 3;
-	}
-
-	int LuaAssFile::IterNext(lua_State *L)
-	{
-		size_t i = check_uint(L, 2);
-		if (i >= lines.size()) {
-			lua_pushnil(L);
-			return 1;
-		}
-
-		push_value(L, i + 1);
-		AssEntryToLua(L, i);
-		return 2;
-	}
-
-	int LuaAssFile::LuaParseKaraokeData(lua_State *L)
-	{
-		auto e = LuaToAssEntry(L, ass);
-		auto dia = check_cast_constptr<AssDialogue>(e.get());
-		argcheck(L, !!dia, 1, "Subtitle line must be a dialogue line");
-
-		int idx = 0;
-
-		// 2.1.x stored everything before the first syllable at index zero
-		// There's no longer any such thing with the new parser, but scripts
-		// may rely on kara[0] existing so add an empty syllable
-		lua_createtable(L, 0, 6);
-		set_field(L, "duration", 0);
-		set_field(L, "start_time", 0);
-		set_field(L, "end_time", 0);
-		set_field(L, "tag", "");
-		set_field(L, "text", "");
-		set_field(L, "text_stripped", "");
-		lua_rawseti(L, -2, idx++);
-
-		AssKaraoke kara(dia, false, false);
-		for (auto const& syl : kara) {
-			lua_createtable(L, 0, 6);
-			set_field(L, "duration", syl.duration);
-			set_field(L, "start_time", syl.start_time - dia->Start);
-			set_field(L, "end_time", syl.start_time + syl.duration - dia->Start);
-			set_field(L, "tag", syl.tag_type);
-			set_field(L, "text", syl.GetText(false));
-			set_field(L, "text_stripped", syl.text);
-			lua_rawseti(L, -2, idx++);
-		}
-
-		return 1;
-	}
-
-	int LuaAssFile::LuaGetScriptResolution(lua_State *L)
-	{
-		int w, h;
-		ass->GetResolution(w, h);
-		push_value(L, w);
-		push_value(L, h);
-		return 2;
-	}
-
-	void LuaAssFile::LuaSetUndoPoint(lua_State *L)
-	{
-		if (!can_set_undo)
-			error(L, "Attempt to set an undo point in a context where it makes no sense to do so.");
-
-		if (modification_type) {
-			pending_commits.emplace_back();
-			PendingCommit& back = pending_commits.back();
-
-			back.modification_type = modification_type;
-			back.mesage = to_wx(check_string(L, 1));
-			back.lines = lines;
-			modification_type = 0;
-		}
-	}
-
-	LuaAssFile *LuaAssFile::GetObjPointer(lua_State *L, int idx, bool allow_expired)
-	{
-		assert(lua_type(L, idx) == LUA_TUSERDATA);
-		auto ud = lua_touserdata(L, idx);
-		auto laf = *static_cast<LuaAssFile **>(ud);
-		if (!allow_expired && laf->references < 2)
-			error(L, "Subtitles object is no longer valid");
-		return laf;
-	}
-
-	std::vector<AssEntry *> LuaAssFile::ProcessingComplete(wxString const& undo_description)
-	{
-		auto apply_lines = [&](std::vector<AssEntry *> const& lines) {
-			if (script_info_copied)
-				ass->Info.clear();
-			ass->Styles.clear();
-			ass->Events.clear();
-
-			for (auto line : lines) {
-				if (!line) continue;
-				switch (line->Group()) {
-					case AssEntryGroup::INFO:     ass->Info.push_back(*static_cast<AssInfo *>(line)); break;
-					case AssEntryGroup::STYLE:    ass->Styles.push_back(*static_cast<AssStyle *>(line)); break;
-					case AssEntryGroup::DIALOGUE: ass->Events.push_back(*static_cast<AssDialogue *>(line)); break;
-					default: break;
-				}
-			}
-		};
-		// Apply any pending commits
-		for (auto const& pc : pending_commits) {
-			apply_lines(pc.lines);
-			ass->Commit(pc.mesage, pc.modification_type);
-		}
-
-		// Commit any changes after the last undo point was set
-		if (modification_type)
-			apply_lines(lines);
-		if (modification_type && can_set_undo && !undo_description.empty())
-			ass->Commit(undo_description, modification_type);
-
-		lines_to_delete.clear();
-
-		auto ret = std::move(lines);
-		references--;
-		if (!references) delete this;
-		return ret;
-	}
-
-	void LuaAssFile::Cancel()
-	{
-		for (auto& line : lines_to_delete) line.release();
-		references--;
-		if (!references) delete this;
-	}
-
-	LuaAssFile::LuaAssFile(lua_State *L, AssFile *ass, bool can_modify, bool can_set_undo)
-	: ass(ass)
-	, L(L)
-	, can_modify(can_modify)
-	, can_set_undo(can_set_undo)
-	{
-        for (size_t i = 0; i < ass->Info.size(); ++i)
-			lines.push_back(nullptr);
-		for (auto& line : ass->Styles)
-			lines.push_back(&line);
-		for (auto& line : ass->Events)
-			lines.push_back(&line);
-
-		// prepare userdata object
-		*static_cast<LuaAssFile**>(lua_newuserdata(L, sizeof(LuaAssFile*))) = this;
-
-		// make the metatable
-		lua_createtable(L, 0, 5);
-		set_field<closure_wrapper<&LuaAssFile::ObjectIndexRead>>(L, "__index");
-		set_field<closure_wrapper_v<&LuaAssFile::ObjectIndexWrite, false>>(L, "__newindex");
-		set_field<closure_wrapper<&LuaAssFile::ObjectGetLen>>(L, "__len");
-		set_field<closure_wrapper_v<&LuaAssFile::ObjectGarbageCollect, true>>(L, "__gc");
-		set_field<closure_wrapper<&LuaAssFile::ObjectIPairs>>(L, "__ipairs");
-		lua_setmetatable(L, -2);
-
-		// register misc functions
-		// assume the "aegisub" global table exists
-		lua_getglobal(L, "aegisub");
-
-		set_field<closure_wrapper<&LuaAssFile::LuaParseKaraokeData>>(L, "parse_karaoke_data");
-		set_field<closure_wrapper_v<&LuaAssFile::LuaSetUndoPoint, false>>(L, "set_undo_point");
-
-		lua_pop(L, 1); // pop "aegisub" table
-
-		// Leaves userdata object on stack
-	}
+int LuaAssFile::ObjectIndexRead(lua_State *L)
+{
+    switch (lua_type(L, 2)) {
+    case LUA_TNUMBER: {
+        // read an indexed AssEntry
+        int idx = lua_tointeger(L, 2);
+        CheckBounds(idx);
+        AssEntryToLua(L, idx - 1);
+        return 1;
+    }
+
+    case LUA_TSTRING: {
+        // either return n or a function doing further stuff
+        const char *idx = lua_tostring(L, 2);
+
+        if (strcmp(idx, "n") == 0) {
+            // get number of items
+            lua_pushnumber(L, lines.size());
+            return 1;
+        }
+
+        lua_pushvalue(L, 1);
+        if (strcmp(idx, "delete") == 0)
+            lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectDelete, false>, 1);
+        else if (strcmp(idx, "deleterange") == 0)
+            lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectDeleteRange, false>, 1);
+        else if (strcmp(idx, "insert") == 0)
+            lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectInsert, false>, 1);
+        else if (strcmp(idx, "append") == 0)
+            lua_pushcclosure(L, closure_wrapper_v<&LuaAssFile::ObjectAppend, false>, 1);
+        else if (strcmp(idx, "script_resolution") == 0)
+            lua_pushcclosure(L, closure_wrapper<&LuaAssFile::LuaGetScriptResolution>, 1);
+        else {
+            // idiot
+            lua_pop(L, 1);
+            return error(L, "Invalid indexing in Subtitle File object: '%s'", idx);
+        }
+
+        return 1;
+    }
+
+    default:
+        // crap, user is stupid!
+        return error(L, "Attempt to index a Subtitle File object with value of type '%s'.", lua_typename(L, lua_type(L, 2)));
+    }
+
+    assert(false);
+    return 0;
+}
+
+void LuaAssFile::InitScriptInfoIfNeeded()
+{
+    if (script_info_copied) return;
+    size_t i = 0;
+    for (auto const &info : ass->Info) {
+        // Just in case an insane user inserted non-info lines into the
+        // script info section...
+        while (lines[i]) ++i;
+        lines_to_delete.emplace_back(agi::make_unique<AssInfo>(info));
+        lines[i++] = lines_to_delete.back().get();
+    }
+    script_info_copied = true;
+}
+
+void LuaAssFile::QueueLineForDeletion(size_t idx)
+{
+    if (!lines[idx] || lines[idx]->Group() == AssEntryGroup::INFO)
+        InitScriptInfoIfNeeded();
+    else
+        lines_to_delete.emplace_back(lines[idx]);
+}
+
+void LuaAssFile::AssignLine(size_t idx, std::unique_ptr<AssEntry> e)
+{
+    auto group = e->Group();
+    if (group == AssEntryGroup::INFO)
+        InitScriptInfoIfNeeded();
+    lines[idx] = e.get();
+    if (group == AssEntryGroup::INFO)
+        lines_to_delete.emplace_back(std::move(e));
+    else
+        e.release();
+}
+
+void LuaAssFile::InsertLine(std::vector<AssEntry *> &vec, size_t idx, std::unique_ptr<AssEntry> e)
+{
+    auto group = e->Group();
+    if (group == AssEntryGroup::INFO)
+        InitScriptInfoIfNeeded();
+    vec.insert(vec.begin() + idx, e.get());
+    if (group == AssEntryGroup::INFO)
+        lines_to_delete.emplace_back(std::move(e));
+    else
+        e.release();
+}
+
+void LuaAssFile::ObjectIndexWrite(lua_State *L)
+{
+    // instead of implementing everything twice, just call the other modification-functions from here
+    // after modifying the stack to match their expectations
+
+    CheckAllowModify();
+
+    int n = check_int(L, 2);
+    if (n < 0) {
+        // insert line so new index is n
+        lua_remove(L, 1);
+        lua_pushinteger(L, -n);
+        lua_replace(L, 1);
+        ObjectInsert(L);
+    }
+    else if (n == 0) {
+        // append line to list
+        lua_remove(L, 1);
+        lua_remove(L, 1);
+        ObjectAppend(L);
+    }
+    else {
+        // replace line at index n or delete
+        if (!lua_isnil(L, 3)) {
+            // insert
+            CheckBounds(n);
+
+            auto e = LuaToAssEntry(L, ass);
+            modification_type |= modification_mask(e.get());
+            QueueLineForDeletion(n - 1);
+            AssignLine(n - 1, std::move(e));
+        }
+        else {
+            // delete
+            lua_remove(L, 1);
+            lua_remove(L, 1);
+            ObjectDelete(L);
+        }
+    }
+}
+
+int LuaAssFile::ObjectGetLen(lua_State *L)
+{
+    lua_pushnumber(L, lines.size());
+    return 1;
+}
+
+void LuaAssFile::ObjectDelete(lua_State *L)
+{
+    CheckAllowModify();
+
+    // get number of items to delete
+    int itemcount = lua_gettop(L);
+    if (itemcount == 0) return;
+
+    std::vector<size_t> ids;
+    if (itemcount == 1 && lua_istable(L, 1)) {
+        lua_pushvalue(L, 1);
+        lua_for_each(L, [&] {
+            size_t n = check_uint(L, -1);
+            argcheck(L, n > 0 && n <= lines.size(), 1, "Out of range line index");
+            ids.push_back(n - 1);
+        });
+    }
+    else {
+        ids.reserve(itemcount);
+        while (itemcount > 0) {
+            size_t n = check_uint(L, -1);
+            argcheck(L, n > 0 && n <= lines.size(), itemcount, "Out of range line index");
+            ids.push_back(n - 1);
+            --itemcount;
+        }
+    }
+
+    sort(ids.begin(), ids.end());
+
+    size_t id_idx = 0, out = 0;
+    for (size_t i = 0; i < lines.size(); ++i) {
+        if (id_idx < ids.size() && ids[id_idx] == i) {
+            modification_type |= modification_mask(lines[i]);
+            QueueLineForDeletion(i);
+            ++id_idx;
+        }
+        else {
+            lines[out++] = lines[i];
+        }
+    }
+
+    lines.erase(lines.begin() + out, lines.end());
+}
+
+void LuaAssFile::ObjectDeleteRange(lua_State *L)
+{
+    CheckAllowModify();
+
+    size_t a = std::max<size_t>(check_uint(L, 1), 1) - 1;
+    size_t b = std::min<size_t>(check_uint(L, 2), lines.size());
+
+    if (a >= b) return;
+
+    for (size_t i = a; i < b; ++i) {
+        modification_type |= modification_mask(lines[i]);
+        QueueLineForDeletion(i);
+    }
+
+    lines.erase(lines.begin() + a, lines.begin() + b);
+}
+
+void LuaAssFile::ObjectAppend(lua_State *L)
+{
+    CheckAllowModify();
+
+    int n = lua_gettop(L);
+
+    for (int i = 1; i <= n; i++) {
+        lua_pushvalue(L, i);
+        auto e = LuaToAssEntry(L, ass);
+        modification_type |= modification_mask(e.get());
+
+        if (lines.empty()) {
+            InsertLine(lines, 0, std::move(e));
+            continue;
+        }
+
+        // Find the appropriate place to put it
+        auto group = e->Group();
+        for (size_t i = lines.size(); i > 0; --i) {
+            auto cur_group = lines[i - 1] ? lines[i - 1]->Group() : AssEntryGroup::INFO;
+            if (cur_group == group) {
+                InsertLine(lines, i, std::move(e));
+                break;
+            }
+        }
+
+        // No lines of this type exist already, so just append it to the end
+        if (e) InsertLine(lines, lines.size(), std::move(e));
+    }
+}
+
+void LuaAssFile::ObjectInsert(lua_State *L)
+{
+    CheckAllowModify();
+
+    size_t before = check_uint(L, 1);
+
+    // + 1 to allow appending at the end of the file
+    argcheck(L, before > 0 && before <= lines.size() + 1, 1,
+             "Out of range line index");
+
+    if (before == lines.size() + 1) {
+        lua_remove(L, 1);
+        ObjectAppend(L);
+        return;
+    }
+
+    int n = lua_gettop(L);
+    std::vector<AssEntry *> new_entries;
+    new_entries.reserve(n - 1);
+    for (int i = 2; i <= n; i++) {
+        lua_pushvalue(L, i);
+        auto e = LuaToAssEntry(L, ass);
+        modification_type |= modification_mask(e.get());
+        InsertLine(new_entries, i - 2, std::move(e));
+        lua_pop(L, 1);
+    }
+    lines.insert(lines.begin() + before - 1, new_entries.begin(), new_entries.end());
+}
+
+void LuaAssFile::ObjectGarbageCollect(lua_State *L)
+{
+    references--;
+    if (!references) delete this;
+    LOG_D("automation/lua") << "Garbage collected LuaAssFile";
+}
+
+int LuaAssFile::ObjectIPairs(lua_State *L)
+{
+    lua_pushvalue(L, lua_upvalueindex(1)); // push 'this' as userdata
+    lua_pushcclosure(L, closure_wrapper<&LuaAssFile::IterNext>, 1);
+    lua_pushnil(L);
+    push_value(L, 0);
+    return 3;
+}
+
+int LuaAssFile::IterNext(lua_State *L)
+{
+    size_t i = check_uint(L, 2);
+    if (i >= lines.size()) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    push_value(L, i + 1);
+    AssEntryToLua(L, i);
+    return 2;
+}
+
+int LuaAssFile::LuaParseKaraokeData(lua_State *L)
+{
+    auto e = LuaToAssEntry(L, ass);
+    auto dia = check_cast_constptr<AssDialogue>(e.get());
+    argcheck(L, !!dia, 1, "Subtitle line must be a dialogue line");
+
+    int idx = 0;
+
+    // 2.1.x stored everything before the first syllable at index zero
+    // There's no longer any such thing with the new parser, but scripts
+    // may rely on kara[0] existing so add an empty syllable
+    lua_createtable(L, 0, 6);
+    set_field(L, "duration", 0);
+    set_field(L, "start_time", 0);
+    set_field(L, "end_time", 0);
+    set_field(L, "tag", "");
+    set_field(L, "text", "");
+    set_field(L, "text_stripped", "");
+    lua_rawseti(L, -2, idx++);
+
+    AssKaraoke kara(dia, false, false);
+    for (auto const &syl : kara) {
+        lua_createtable(L, 0, 6);
+        set_field(L, "duration", syl.duration);
+        set_field(L, "start_time", syl.start_time - dia->Start);
+        set_field(L, "end_time", syl.start_time + syl.duration - dia->Start);
+        set_field(L, "tag", syl.tag_type);
+        set_field(L, "text", syl.GetText(false));
+        set_field(L, "text_stripped", syl.text);
+        lua_rawseti(L, -2, idx++);
+    }
+
+    return 1;
+}
+
+int LuaAssFile::LuaGetScriptResolution(lua_State *L)
+{
+    int w, h;
+    ass->GetResolution(w, h);
+    push_value(L, w);
+    push_value(L, h);
+    return 2;
+}
+
+void LuaAssFile::LuaSetUndoPoint(lua_State *L)
+{
+    if (!can_set_undo)
+        error(L, "Attempt to set an undo point in a context where it makes no sense to do so.");
+
+    if (modification_type) {
+        pending_commits.emplace_back();
+        PendingCommit &back = pending_commits.back();
+
+        back.modification_type = modification_type;
+        back.mesage = to_wx(check_string(L, 1));
+        back.lines = lines;
+        modification_type = 0;
+    }
+}
+
+LuaAssFile *LuaAssFile::GetObjPointer(lua_State *L, int idx, bool allow_expired)
+{
+    assert(lua_type(L, idx) == LUA_TUSERDATA);
+    auto ud = lua_touserdata(L, idx);
+    auto laf = *static_cast<LuaAssFile **>(ud);
+    if (!allow_expired && laf->references < 2)
+        error(L, "Subtitles object is no longer valid");
+    return laf;
+}
+
+std::vector<AssEntry *> LuaAssFile::ProcessingComplete(wxString const &undo_description)
+{
+    auto apply_lines = [&](std::vector<AssEntry *> const & lines) {
+        if (script_info_copied)
+            ass->Info.clear();
+        ass->Styles.clear();
+        ass->Events.clear();
+
+        for (auto line : lines) {
+            if (!line) continue;
+            switch (line->Group()) {
+            case AssEntryGroup::INFO:     ass->Info.push_back(*static_cast<AssInfo *>(line)); break;
+            case AssEntryGroup::STYLE:    ass->Styles.push_back(*static_cast<AssStyle *>(line)); break;
+            case AssEntryGroup::DIALOGUE: ass->Events.push_back(*static_cast<AssDialogue *>(line)); break;
+            default: break;
+            }
+        }
+    };
+    // Apply any pending commits
+    for (auto const &pc : pending_commits) {
+        apply_lines(pc.lines);
+        ass->Commit(pc.mesage, pc.modification_type);
+    }
+
+    // Commit any changes after the last undo point was set
+    if (modification_type)
+        apply_lines(lines);
+    if (modification_type && can_set_undo && !undo_description.empty())
+        ass->Commit(undo_description, modification_type);
+
+    lines_to_delete.clear();
+
+    auto ret = std::move(lines);
+    references--;
+    if (!references) delete this;
+    return ret;
+}
+
+void LuaAssFile::Cancel()
+{
+    for (auto &line : lines_to_delete) line.release();
+    references--;
+    if (!references) delete this;
+}
+
+LuaAssFile::LuaAssFile(lua_State *L, AssFile *ass, bool can_modify, bool can_set_undo)
+    : ass(ass)
+    , L(L)
+    , can_modify(can_modify)
+    , can_set_undo(can_set_undo)
+{
+    for (size_t i = 0; i < ass->Info.size(); ++i)
+        lines.push_back(nullptr);
+    for (auto &line : ass->Styles)
+        lines.push_back(&line);
+    for (auto &line : ass->Events)
+        lines.push_back(&line);
+
+    // prepare userdata object
+    *static_cast<LuaAssFile **>(lua_newuserdata(L, sizeof(LuaAssFile *))) = this;
+
+    // make the metatable
+    lua_createtable(L, 0, 5);
+    set_field<closure_wrapper<&LuaAssFile::ObjectIndexRead>>(L, "__index");
+    set_field<closure_wrapper_v<&LuaAssFile::ObjectIndexWrite, false>>(L, "__newindex");
+    set_field<closure_wrapper<&LuaAssFile::ObjectGetLen>>(L, "__len");
+    set_field<closure_wrapper_v<&LuaAssFile::ObjectGarbageCollect, true>>(L, "__gc");
+    set_field<closure_wrapper<&LuaAssFile::ObjectIPairs>>(L, "__ipairs");
+    lua_setmetatable(L, -2);
+
+    // register misc functions
+    // assume the "aegisub" global table exists
+    lua_getglobal(L, "aegisub");
+
+    set_field<closure_wrapper<&LuaAssFile::LuaParseKaraokeData>>(L, "parse_karaoke_data");
+    set_field<closure_wrapper_v<&LuaAssFile::LuaSetUndoPoint, false>>(L, "set_undo_point");
+
+    lua_pop(L, 1); // pop "aegisub" table
+
+    // Leaves userdata object on stack
+}
 }
diff --git a/src/auto4_lua_dialog.cpp b/src/auto4_lua_dialog.cpp
index 7985c49b91069829dc21f8bf1c60f502fc10e513..fff8fce28ca43429a8f985da11c5e85c665d554c 100644
--- a/src/auto4_lua_dialog.cpp
+++ b/src/auto4_lua_dialog.cpp
@@ -62,489 +62,497 @@
 
 using namespace agi::lua;
 namespace {
-	inline void get_if_right_type(lua_State *L, std::string &def) {
-		if (lua_isstring(L, -1))
-			def = lua_tostring(L, -1);
-	}
-
-	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) {
-		if (lua_isnumber(L, -1))
-			def = lua_tointeger(L, -1);
-	}
-
-	inline void get_if_right_type(lua_State *L, bool &def) {
-		if (lua_isboolean(L, -1))
-			def = !!lua_toboolean(L, -1);
-	}
-
-	template<class T>
-	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 std::string get_field(lua_State *L, const char *name) {
-		return get_field(L, name, std::string());
-	}
-
-	template<class T>
-	void read_string_array(lua_State *L, T &cont) {
-		lua_for_each(L, [&] {
-			if (lua_isstring(L, -1))
-				cont.push_back(lua_tostring(L, -1));
-		});
-	}
-
-	int string_to_wx_id(std::string const& str) {
-		static std::unordered_map<std::string, int> ids;
-		if (ids.empty()) {
-			ids["ok"] = wxID_OK;
-			ids["yes"] = wxID_YES;
-			ids["save"] = wxID_SAVE;
-			ids["apply"] = wxID_APPLY;
-			ids["close"] = wxID_CLOSE;
-			ids["no"] = wxID_NO;
-			ids["cancel"] = wxID_CANCEL;
-			ids["help"] = wxID_HELP;
-			ids["context_help"] = wxID_CONTEXT_HELP;
-		}
-		auto it = ids.find(str);
-		return it == end(ids) ? -1 : it->second;
-	}
+inline void get_if_right_type(lua_State *L, std::string &def)
+{
+    if (lua_isstring(L, -1))
+        def = lua_tostring(L, -1);
+}
+
+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)
+{
+    if (lua_isnumber(L, -1))
+        def = lua_tointeger(L, -1);
+}
+
+inline void get_if_right_type(lua_State *L, bool &def)
+{
+    if (lua_isboolean(L, -1))
+        def = !!lua_toboolean(L, -1);
+}
+
+template<class T>
+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 std::string get_field(lua_State *L, const char *name)
+{
+    return get_field(L, name, std::string());
+}
+
+template<class T>
+void read_string_array(lua_State *L, T &cont)
+{
+    lua_for_each(L, [&] {
+        if (lua_isstring(L, -1))
+            cont.push_back(lua_tostring(L, -1));
+    });
+}
+
+int string_to_wx_id(std::string const &str)
+{
+    static std::unordered_map<std::string, int> ids;
+    if (ids.empty()) {
+        ids["ok"] = wxID_OK;
+        ids["yes"] = wxID_YES;
+        ids["save"] = wxID_SAVE;
+        ids["apply"] = wxID_APPLY;
+        ids["close"] = wxID_CLOSE;
+        ids["no"] = wxID_NO;
+        ids["cancel"] = wxID_CANCEL;
+        ids["help"] = wxID_HELP;
+        ids["context_help"] = wxID_CONTEXT_HELP;
+    }
+    auto it = ids.find(str);
+    return it == end(ids) ? -1 : it->second;
+}
 }
 
 namespace Automation4 {
-	// LuaDialogControl
-	LuaDialogControl::LuaDialogControl(lua_State *L)
-	// Assume top of stack is a control table (don't do checking)
-	: name(get_field(L, "name"))
-	, hint(get_field(L, "hint"))
-	, x(get_field(L, "x", 0))
-	, y(get_field(L, "y", 0))
-	, width(get_field(L, "width", 1))
-	, height(get_field(L, "height", 1))
-	{
-		LOG_D("automation/lua/dialog") << "created control: '" << name << "', (" << x << "," << y << ")(" << width << "," << height << "), " << hint;
-	}
-
-	namespace LuaControl {
-		/// A static text label
-		class Label final : public LuaDialogControl {
-			std::string label;
-		public:
-			Label(lua_State *L) : LuaDialogControl(L), label(get_field(L, "label")) { }
-
-			wxControl *Create(wxWindow *parent) override {
-				return new wxStaticText(parent, -1, to_wx(label));
-			}
-
-			int GetSizerFlags() const override { return wxALIGN_CENTRE_VERTICAL | wxALIGN_LEFT; }
-
-			void LuaReadBack(lua_State *L) override {
-				// Label doesn't produce output, so let it be nil
-				lua_pushnil(L);
-			}
-		};
-
-		/// A single-line text edit control
-		class Edit : public LuaDialogControl {
-		protected:
-			std::string text;
-			wxTextCtrl *cw = nullptr;
-
-		public:
-			Edit(lua_State *L)
-			: LuaDialogControl(L)
-			, text(get_field(L, "value"))
-			{
-				// Undocumented behaviour, 'value' is also accepted as key for text,
-				// mostly so a text control can stand in for other things.
-				// This shouldn't be exploited and might change later.
-				text = get_field(L, "text", text);
-			}
-
-			bool CanSerialiseValue() const override { return true; }
-			std::string SerialiseValue() const override { return inline_string_encode(text); }
-			void UnserialiseValue(const std::string &serialised) override { text = inline_string_decode(serialised); }
-
-			wxControl *Create(wxWindow *parent) override {
-				cw = new wxTextCtrl(parent, -1, to_wx(text));
-				cw->SetMaxLength(0);
-				cw->SetValidator(StringBinder(&text));
-				cw->SetToolTip(to_wx(hint));
-				return cw;
-			}
-
-			void LuaReadBack(lua_State *L) override {
-				lua_pushstring(L, text.c_str());
-			}
-		};
-
-		/// A color-picker button
-		class Color final : public LuaDialogControl {
-			agi::Color color;
-			bool alpha;
-
-		public:
-			Color(lua_State *L, bool alpha)
-			: LuaDialogControl(L)
-			, color(get_field(L, "value"))
-			, alpha(alpha)
-			{
-			}
-
-			bool CanSerialiseValue() const override { return true; }
-			std::string SerialiseValue() const override { return inline_string_encode(color.GetHexFormatted(alpha)); }
-			void UnserialiseValue(const std::string &serialised) override { color = inline_string_decode(serialised); }
-
-			wxControl *Create(wxWindow *parent) override {
-				wxControl *cw = new ColourButton(parent, wxSize(50*width,10*height), alpha, color, ColorValidator(&color));
-				cw->SetToolTip(to_wx(hint));
-				return cw;
-			}
-
-			void LuaReadBack(lua_State *L) override {
-				lua_pushstring(L, color.GetHexFormatted(alpha).c_str());
-			}
-		};
-
-		/// A multiline text edit control
-		class Textbox final : public Edit {
-		public:
-			Textbox(lua_State *L) : Edit(L) { }
-
-			// Same serialisation interface as single-line edit
-			wxControl *Create(wxWindow *parent) override {
-				cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, StringBinder(&text));
-				cw->SetMinSize(wxSize(0, 30));
-				cw->SetToolTip(to_wx(hint));
-				return cw;
-			}
-		};
-
-		/// Integer only edit
-		class IntEdit final : public Edit {
-			wxSpinCtrl *cw = nullptr;
-			int value;
-			int min, max;
-
-		public:
-			IntEdit(lua_State *L)
-			: Edit(L)
-			, value(get_field(L, "value", 0))
-			, min(get_field(L, "min", INT_MIN))
-			, max(get_field(L, "max", INT_MAX))
-			{
-				if (min >= max) {
-					max = INT_MAX;
-					min = INT_MIN;
-				}
-			}
-
-			bool CanSerialiseValue() const override  { return true; }
-			std::string SerialiseValue() const override { return std::to_string(value); }
-			void UnserialiseValue(const std::string &serialised) override { value = atoi(serialised.c_str()); }
-
-			wxControl *Create(wxWindow *parent) override {
-				cw = new wxSpinCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value);
-				cw->SetValidator(wxGenericValidator(&value));
-				cw->SetToolTip(to_wx(hint));
-				return cw;
-			}
-
-			void LuaReadBack(lua_State *L) override {
-				lua_pushinteger(L, value);
-			}
-		};
-
-		// Float only edit
-		class FloatEdit final : public Edit {
-			double value;
-			double min;
-			double max;
-			double step;
-			wxSpinCtrlDouble *scd = nullptr;
-
-		public:
-			FloatEdit(lua_State *L)
-			: Edit(L)
-			, value(get_field(L, "value", 0.0))
-			, min(get_field(L, "min", -DBL_MAX))
-			, max(get_field(L, "max", DBL_MAX))
-			, step(get_field(L, "step", 0.0))
-			{
-				if (min >= max) {
-					max = DBL_MAX;
-					min = -DBL_MAX;
-				}
-			}
-
-			bool CanSerialiseValue() const override { return true; }
-			std::string SerialiseValue() const override { return std::to_string(value); }
-			void UnserialiseValue(const std::string &serialised) override { value = atof(serialised.c_str()); }
-
-			wxControl *Create(wxWindow *parent) override {
-				if (step > 0) {
-					scd = new wxSpinCtrlDouble(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value, step);
-					scd->SetValidator(DoubleSpinValidator(&value));
-					scd->SetToolTip(to_wx(hint));
-					return scd;
-				}
-
-				DoubleValidator val(&value, min, max);
-				cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, val);
-				cw->SetToolTip(to_wx(hint));
-				return cw;
-			}
-
-			void LuaReadBack(lua_State *L) override {
-				lua_pushnumber(L, value);
-			}
-		};
-
-		/// A dropdown list
-		class Dropdown final : public LuaDialogControl {
-			std::vector<std::string> items;
-			std::string value;
-			wxComboBox *cw = nullptr;
-
-		public:
-			Dropdown(lua_State *L)
-			: LuaDialogControl(L)
-			, value(get_field(L, "value"))
-			{
-				lua_getfield(L, -1, "items");
-				read_string_array(L, items);
-			}
-
-			bool CanSerialiseValue() const override { return true; }
-			std::string SerialiseValue() const override { return inline_string_encode(value); }
-			void UnserialiseValue(const std::string &serialised) override { value = inline_string_decode(serialised); }
-
-			wxControl *Create(wxWindow *parent) override {
-				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) override {
-				lua_pushstring(L, value.c_str());
-			}
-		};
-
-		class Checkbox final : public LuaDialogControl {
-			std::string label;
-			bool value;
-			wxCheckBox *cw = nullptr;
-
-		public:
-			Checkbox(lua_State *L)
-			: LuaDialogControl(L)
-			, label(get_field(L, "label"))
-			, value(get_field(L, "value", false))
-			{
-			}
-
-			bool CanSerialiseValue() const override { return true; }
-			std::string SerialiseValue() const override { return value ? "1" : "0"; }
-			void UnserialiseValue(const std::string &serialised) override { value = serialised != "0"; }
-
-			wxControl *Create(wxWindow *parent) override {
-				cw = new wxCheckBox(parent, -1, to_wx(label));
-				cw->SetValidator(wxGenericValidator(&value));
-				cw->SetToolTip(to_wx(hint));
-				cw->SetValue(value);
-				return cw;
-			}
-
-			void LuaReadBack(lua_State *L) override {
-				lua_pushboolean(L, value);
-			}
-		};
-	}
-
-	// LuaDialog
-	LuaDialog::LuaDialog(lua_State *L, bool include_buttons)
-	: use_buttons(include_buttons)
-	{
-		LOG_D("automation/lua/dialog") << "creating LuaDialoug, addr: " << this;
-
-		// assume top of stack now contains a dialog table
-		if (!lua_istable(L, 1))
-			error(L, "Cannot create config dialog from something non-table");
-
-		// Ok, so there is a table with controls
-		lua_pushvalue(L, 1);
-		lua_for_each(L, [&] {
-			if (!lua_istable(L, -1))
-				error(L, "bad control table entry");
-
-			std::string controlclass = get_field(L, "class");
-			boost::to_lower(controlclass);
-
-			std::unique_ptr<LuaDialogControl> ctl;
-
-			// Check control class and create relevant control
-			if (controlclass == "label")
-				ctl = agi::make_unique<LuaControl::Label>(L);
-			else if (controlclass == "edit")
-				ctl = agi::make_unique<LuaControl::Edit>(L);
-			else if (controlclass == "intedit")
-				ctl = agi::make_unique<LuaControl::IntEdit>(L);
-			else if (controlclass == "floatedit")
-				ctl = agi::make_unique<LuaControl::FloatEdit>(L);
-			else if (controlclass == "textbox")
-				ctl = agi::make_unique<LuaControl::Textbox>(L);
-			else if (controlclass == "dropdown")
-				ctl = agi::make_unique<LuaControl::Dropdown>(L);
-			else if (controlclass == "checkbox")
-				ctl = agi::make_unique<LuaControl::Checkbox>(L);
-			else if (controlclass == "color")
-				ctl = agi::make_unique<LuaControl::Color>(L, false);
-			else if (controlclass == "coloralpha")
-				ctl = agi::make_unique<LuaControl::Color>(L, true);
-			else if (controlclass == "alpha")
-				// FIXME
-				ctl = agi::make_unique<LuaControl::Edit>(L);
-			else
-				error(L, "bad control table entry");
-
-			controls.emplace_back(std::move(ctl));
-		});
-
-		if (include_buttons && lua_istable(L, 2)) {
-			lua_pushvalue(L, 2);
-			lua_for_each(L, [&]{
-				buttons.emplace_back(-1, check_string(L, -1));
-			});
-		}
-
-		if (include_buttons && lua_istable(L, 3)) {
-			lua_pushvalue(L, 3);
-			lua_for_each(L, [&]{
-				int id = string_to_wx_id(check_string(L, -2));
-				std::string label = check_string(L, -1);
-				auto btn = boost::find_if(buttons,
-					[&](std::pair<int, std::string>& btn) { return btn.second == label; });
-				if (btn == end(buttons))
-					error(L, "Invalid button for id %s", lua_tostring(L, -2));
-				btn->first = id;
-			});
-		}
-	}
-
-	wxWindow* LuaDialog::CreateWindow(wxWindow *parent) {
-		window = new wxPanel(parent);
-
-		auto 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) {
-			window->SetSizerAndFit(s);
-			return window;
-		}
-
-		if (buttons.size() == 0) {
-			buttons.emplace_back(wxID_OK, "");
-			buttons.emplace_back(wxID_CANCEL, "");
-		}
-
-		auto dialog = static_cast<wxDialog *>(parent);
-		auto bs = new wxStdDialogButtonSizer;
-
-		auto make_button = [&](wxWindowID id, int button_pushed, std::string const& text) -> wxButton *{
-			auto button = new wxButton(window, id, to_wx(text));
-			button->Bind(wxEVT_BUTTON, [=](wxCommandEvent &evt) {
-				this->button_pushed = button_pushed;
-				dialog->TransferDataFromWindow();
-				dialog->EndModal(0);
-			});
-
-			if (id == wxID_OK || id == wxID_YES || id == wxID_SAVE) {
-				button->SetDefault();
-				dialog->SetAffirmativeId(id);
-			}
-
-			if (id == wxID_CLOSE || id == wxID_NO)
-				dialog->SetEscapeId(id);
-
-			return button;
-		};
-
-		if (boost::count(buttons | boost::adaptors::map_keys, -1) == 0) {
-			for (size_t i = 0; i < buttons.size(); ++i)
-				bs->AddButton(make_button(buttons[i].first, i, buttons[i].second));
-			bs->Realize();
-		}
-		else {
-			for (size_t i = 0; i < buttons.size(); ++i)
-				bs->Add(make_button(buttons[i].first, i, buttons[i].second));
-		}
-
-		auto ms = new wxBoxSizer(wxVERTICAL);
-		ms->Add(s, 0, wxBOTTOM, 5);
-		ms->Add(bs);
-		window->SetSizerAndFit(ms);
-
-		return window;
-	}
-
-	int LuaDialog::LuaReadBack(lua_State *L) {
-		// First read back which button was pressed, if any
-		if (use_buttons) {
-			if (button_pushed == -1 || buttons[button_pushed].first == wxID_CANCEL)
-				lua_pushboolean(L, false);
-			else
-				lua_pushstring(L, buttons[button_pushed].second.c_str());
-		}
-
-		// Then read controls back
-		lua_createtable(L, 0, controls.size());
-		for (auto& control : controls) {
-			control->LuaReadBack(L);
-			lua_setfield(L, -2, control->name.c_str());
-		}
-
-		return use_buttons ? 2 : 1;
-	}
-
-	std::string LuaDialog::Serialise() {
-		std::string res;
-
-		// Format into "name1:value1|name2:value2|name3:value3"
-		for (auto& control : controls) {
-			if (control->CanSerialiseValue()) {
-				if (!res.empty())
-					res += "|";
-				res += inline_string_encode(control->name) + ":" + control->SerialiseValue();
-			}
-		}
-
-		return res;
-	}
-
-	void LuaDialog::Unserialise(const std::string &serialised) {
-		for (auto tok : agi::Split(serialised, '|')) {
-			auto pos = std::find(begin(tok), end(tok), ':');
-			if (pos == end(tok)) continue;
-
-			std::string name = inline_string_decode(std::string(begin(tok), pos));
-			std::string value(pos + 1, end(tok));
-
-			// Hand value to all controls matching name
-			for (auto& control : controls) {
-				if (control->name == name && control->CanSerialiseValue())
-					control->UnserialiseValue(value);
-			}
-		}
-	}
+// LuaDialogControl
+LuaDialogControl::LuaDialogControl(lua_State *L)
+// Assume top of stack is a control table (don't do checking)
+    : name(get_field(L, "name"))
+    , hint(get_field(L, "hint"))
+    , x(get_field(L, "x", 0))
+    , y(get_field(L, "y", 0))
+    , width(get_field(L, "width", 1))
+    , height(get_field(L, "height", 1))
+{
+    LOG_D("automation/lua/dialog") << "created control: '" << name << "', (" << x << "," << y << ")(" << width << "," << height << "), " << hint;
+}
+
+namespace LuaControl {
+/// A static text label
+class Label final : public LuaDialogControl {
+    std::string label;
+public:
+    Label(lua_State *L) : LuaDialogControl(L), label(get_field(L, "label")) { }
+
+    wxControl *Create(wxWindow *parent) override {
+        return new wxStaticText(parent, -1, to_wx(label));
+    }
+
+    int GetSizerFlags() const override { return wxALIGN_CENTRE_VERTICAL | wxALIGN_LEFT; }
+
+    void LuaReadBack(lua_State *L) override {
+        // Label doesn't produce output, so let it be nil
+        lua_pushnil(L);
+    }
+};
+
+/// A single-line text edit control
+class Edit : public LuaDialogControl {
+protected:
+    std::string text;
+    wxTextCtrl *cw = nullptr;
+
+public:
+    Edit(lua_State *L)
+        : LuaDialogControl(L)
+        , text(get_field(L, "value")) {
+        // Undocumented behaviour, 'value' is also accepted as key for text,
+        // mostly so a text control can stand in for other things.
+        // This shouldn't be exploited and might change later.
+        text = get_field(L, "text", text);
+    }
+
+    bool CanSerialiseValue() const override { return true; }
+    std::string SerialiseValue() const override { return inline_string_encode(text); }
+    void UnserialiseValue(const std::string &serialised) override { text = inline_string_decode(serialised); }
+
+    wxControl *Create(wxWindow *parent) override {
+        cw = new wxTextCtrl(parent, -1, to_wx(text));
+        cw->SetMaxLength(0);
+        cw->SetValidator(StringBinder(&text));
+        cw->SetToolTip(to_wx(hint));
+        return cw;
+    }
+
+    void LuaReadBack(lua_State *L) override {
+        lua_pushstring(L, text.c_str());
+    }
+};
+
+/// A color-picker button
+class Color final : public LuaDialogControl {
+    agi::Color color;
+    bool alpha;
+
+public:
+    Color(lua_State *L, bool alpha)
+        : LuaDialogControl(L)
+        , color(get_field(L, "value"))
+        , alpha(alpha) {
+    }
+
+    bool CanSerialiseValue() const override { return true; }
+    std::string SerialiseValue() const override { return inline_string_encode(color.GetHexFormatted(alpha)); }
+    void UnserialiseValue(const std::string &serialised) override { color = inline_string_decode(serialised); }
+
+    wxControl *Create(wxWindow *parent) override {
+        wxControl *cw = new ColourButton(parent, wxSize(50 * width, 10 * height), alpha, color, ColorValidator(&color));
+        cw->SetToolTip(to_wx(hint));
+        return cw;
+    }
+
+    void LuaReadBack(lua_State *L) override {
+        lua_pushstring(L, color.GetHexFormatted(alpha).c_str());
+    }
+};
+
+/// A multiline text edit control
+class Textbox final : public Edit {
+public:
+    Textbox(lua_State *L) : Edit(L) { }
+
+    // Same serialisation interface as single-line edit
+    wxControl *Create(wxWindow *parent) override {
+        cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE, StringBinder(&text));
+        cw->SetMinSize(wxSize(0, 30));
+        cw->SetToolTip(to_wx(hint));
+        return cw;
+    }
+};
+
+/// Integer only edit
+class IntEdit final : public Edit {
+    wxSpinCtrl *cw = nullptr;
+    int value;
+    int min, max;
+
+public:
+    IntEdit(lua_State *L)
+        : Edit(L)
+        , value(get_field(L, "value", 0))
+        , min(get_field(L, "min", INT_MIN))
+        , max(get_field(L, "max", INT_MAX)) {
+        if (min >= max) {
+            max = INT_MAX;
+            min = INT_MIN;
+        }
+    }
+
+    bool CanSerialiseValue() const override  { return true; }
+    std::string SerialiseValue() const override { return std::to_string(value); }
+    void UnserialiseValue(const std::string &serialised) override { value = atoi(serialised.c_str()); }
+
+    wxControl *Create(wxWindow *parent) override {
+        cw = new wxSpinCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value);
+        cw->SetValidator(wxGenericValidator(&value));
+        cw->SetToolTip(to_wx(hint));
+        return cw;
+    }
+
+    void LuaReadBack(lua_State *L) override {
+        lua_pushinteger(L, value);
+    }
+};
+
+// Float only edit
+class FloatEdit final : public Edit {
+    double value;
+    double min;
+    double max;
+    double step;
+    wxSpinCtrlDouble *scd = nullptr;
+
+public:
+    FloatEdit(lua_State *L)
+        : Edit(L)
+        , value(get_field(L, "value", 0.0))
+        , min(get_field(L, "min", -DBL_MAX))
+        , max(get_field(L, "max", DBL_MAX))
+        , step(get_field(L, "step", 0.0)) {
+        if (min >= max) {
+            max = DBL_MAX;
+            min = -DBL_MAX;
+        }
+    }
+
+    bool CanSerialiseValue() const override { return true; }
+    std::string SerialiseValue() const override { return std::to_string(value); }
+    void UnserialiseValue(const std::string &serialised) override { value = atof(serialised.c_str()); }
+
+    wxControl *Create(wxWindow *parent) override {
+        if (step > 0) {
+            scd = new wxSpinCtrlDouble(parent, -1, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, value, step);
+            scd->SetValidator(DoubleSpinValidator(&value));
+            scd->SetToolTip(to_wx(hint));
+            return scd;
+        }
+
+        DoubleValidator val(&value, min, max);
+        cw = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, val);
+        cw->SetToolTip(to_wx(hint));
+        return cw;
+    }
+
+    void LuaReadBack(lua_State *L) override {
+        lua_pushnumber(L, value);
+    }
+};
+
+/// A dropdown list
+class Dropdown final : public LuaDialogControl {
+    std::vector<std::string> items;
+    std::string value;
+    wxComboBox *cw = nullptr;
+
+public:
+    Dropdown(lua_State *L)
+        : LuaDialogControl(L)
+        , value(get_field(L, "value")) {
+        lua_getfield(L, -1, "items");
+        read_string_array(L, items);
+    }
+
+    bool CanSerialiseValue() const override { return true; }
+    std::string SerialiseValue() const override { return inline_string_encode(value); }
+    void UnserialiseValue(const std::string &serialised) override { value = inline_string_decode(serialised); }
+
+    wxControl *Create(wxWindow *parent) override {
+        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) override {
+        lua_pushstring(L, value.c_str());
+    }
+};
+
+class Checkbox final : public LuaDialogControl {
+    std::string label;
+    bool value;
+    wxCheckBox *cw = nullptr;
+
+public:
+    Checkbox(lua_State *L)
+        : LuaDialogControl(L)
+        , label(get_field(L, "label"))
+        , value(get_field(L, "value", false)) {
+    }
+
+    bool CanSerialiseValue() const override { return true; }
+    std::string SerialiseValue() const override { return value ? "1" : "0"; }
+    void UnserialiseValue(const std::string &serialised) override { value = serialised != "0"; }
+
+    wxControl *Create(wxWindow *parent) override {
+        cw = new wxCheckBox(parent, -1, to_wx(label));
+        cw->SetValidator(wxGenericValidator(&value));
+        cw->SetToolTip(to_wx(hint));
+        cw->SetValue(value);
+        return cw;
+    }
+
+    void LuaReadBack(lua_State *L) override {
+        lua_pushboolean(L, value);
+    }
+};
+}
+
+// LuaDialog
+LuaDialog::LuaDialog(lua_State *L, bool include_buttons)
+    : use_buttons(include_buttons)
+{
+    LOG_D("automation/lua/dialog") << "creating LuaDialoug, addr: " << this;
+
+    // assume top of stack now contains a dialog table
+    if (!lua_istable(L, 1))
+        error(L, "Cannot create config dialog from something non-table");
+
+    // Ok, so there is a table with controls
+    lua_pushvalue(L, 1);
+    lua_for_each(L, [&] {
+        if (!lua_istable(L, -1))
+            error(L, "bad control table entry");
+
+        std::string controlclass = get_field(L, "class");
+        boost::to_lower(controlclass);
+
+        std::unique_ptr<LuaDialogControl> ctl;
+
+        // Check control class and create relevant control
+        if (controlclass == "label")
+            ctl = agi::make_unique<LuaControl::Label>(L);
+        else if (controlclass == "edit")
+            ctl = agi::make_unique<LuaControl::Edit>(L);
+        else if (controlclass == "intedit")
+            ctl = agi::make_unique<LuaControl::IntEdit>(L);
+        else if (controlclass == "floatedit")
+            ctl = agi::make_unique<LuaControl::FloatEdit>(L);
+        else if (controlclass == "textbox")
+            ctl = agi::make_unique<LuaControl::Textbox>(L);
+        else if (controlclass == "dropdown")
+            ctl = agi::make_unique<LuaControl::Dropdown>(L);
+        else if (controlclass == "checkbox")
+            ctl = agi::make_unique<LuaControl::Checkbox>(L);
+        else if (controlclass == "color")
+            ctl = agi::make_unique<LuaControl::Color>(L, false);
+        else if (controlclass == "coloralpha")
+            ctl = agi::make_unique<LuaControl::Color>(L, true);
+        else if (controlclass == "alpha")
+            // FIXME
+            ctl = agi::make_unique<LuaControl::Edit>(L);
+        else
+            error(L, "bad control table entry");
+
+        controls.emplace_back(std::move(ctl));
+    });
+
+    if (include_buttons && lua_istable(L, 2)) {
+        lua_pushvalue(L, 2);
+        lua_for_each(L, [&] {
+            buttons.emplace_back(-1, check_string(L, -1));
+        });
+    }
+
+    if (include_buttons && lua_istable(L, 3)) {
+        lua_pushvalue(L, 3);
+        lua_for_each(L, [&] {
+            int id = string_to_wx_id(check_string(L, -2));
+            std::string label = check_string(L, -1);
+            auto btn = boost::find_if(buttons,
+            [&](std::pair<int, std::string> &btn) { return btn.second == label; });
+            if (btn == end(buttons))
+                error(L, "Invalid button for id %s", lua_tostring(L, -2));
+            btn->first = id;
+        });
+    }
+}
+
+wxWindow *LuaDialog::CreateWindow(wxWindow *parent)
+{
+    window = new wxPanel(parent);
+
+    auto 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) {
+        window->SetSizerAndFit(s);
+        return window;
+    }
+
+    if (buttons.size() == 0) {
+        buttons.emplace_back(wxID_OK, "");
+        buttons.emplace_back(wxID_CANCEL, "");
+    }
+
+    auto dialog = static_cast<wxDialog *>(parent);
+    auto bs = new wxStdDialogButtonSizer;
+
+    auto make_button = [&](wxWindowID id, int button_pushed, std::string const & text) -> wxButton * {
+        auto button = new wxButton(window, id, to_wx(text));
+        button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent & evt)
+        {
+            this->button_pushed = button_pushed;
+            dialog->TransferDataFromWindow();
+            dialog->EndModal(0);
+        });
+
+        if (id == wxID_OK || id == wxID_YES || id == wxID_SAVE)
+        {
+            button->SetDefault();
+            dialog->SetAffirmativeId(id);
+        }
+
+        if (id == wxID_CLOSE || id == wxID_NO)
+            dialog->SetEscapeId(id);
+
+        return button;
+    };
+
+    if (boost::count(buttons | boost::adaptors::map_keys, -1) == 0) {
+        for (size_t i = 0; i < buttons.size(); ++i)
+            bs->AddButton(make_button(buttons[i].first, i, buttons[i].second));
+        bs->Realize();
+    }
+    else {
+        for (size_t i = 0; i < buttons.size(); ++i)
+            bs->Add(make_button(buttons[i].first, i, buttons[i].second));
+    }
+
+    auto ms = new wxBoxSizer(wxVERTICAL);
+    ms->Add(s, 0, wxBOTTOM, 5);
+    ms->Add(bs);
+    window->SetSizerAndFit(ms);
+
+    return window;
+}
+
+int LuaDialog::LuaReadBack(lua_State *L)
+{
+    // First read back which button was pressed, if any
+    if (use_buttons) {
+        if (button_pushed == -1 || buttons[button_pushed].first == wxID_CANCEL)
+            lua_pushboolean(L, false);
+        else
+            lua_pushstring(L, buttons[button_pushed].second.c_str());
+    }
+
+    // Then read controls back
+    lua_createtable(L, 0, controls.size());
+    for (auto &control : controls) {
+        control->LuaReadBack(L);
+        lua_setfield(L, -2, control->name.c_str());
+    }
+
+    return use_buttons ? 2 : 1;
+}
+
+std::string LuaDialog::Serialise()
+{
+    std::string res;
+
+    // Format into "name1:value1|name2:value2|name3:value3"
+    for (auto &control : controls) {
+        if (control->CanSerialiseValue()) {
+            if (!res.empty())
+                res += "|";
+            res += inline_string_encode(control->name) + ":" + control->SerialiseValue();
+        }
+    }
+
+    return res;
+}
+
+void LuaDialog::Unserialise(const std::string &serialised)
+{
+    for (auto tok : agi::Split(serialised, '|')) {
+        auto pos = std::find(begin(tok), end(tok), ':');
+        if (pos == end(tok)) continue;
+
+        std::string name = inline_string_decode(std::string(begin(tok), pos));
+        std::string value(pos + 1, end(tok));
+
+        // Hand value to all controls matching name
+        for (auto &control : controls) {
+            if (control->name == name && control->CanSerialiseValue())
+                control->UnserialiseValue(value);
+        }
+    }
+}
 }
diff --git a/src/auto4_lua_factory.h b/src/auto4_lua_factory.h
index f800d896e2c0f475b0b7d067093d55d070f39212..68745bc6a2009063f0483fe9e99b4ce7a37bffde 100644
--- a/src/auto4_lua_factory.h
+++ b/src/auto4_lua_factory.h
@@ -35,9 +35,9 @@
 #include "auto4_base.h"
 
 namespace Automation4 {
-	class LuaScriptFactory final : public ScriptFactory {
-		std::unique_ptr<Script> Produce(agi::fs::path const& filename) const override;
-	public:
-		LuaScriptFactory();
-	};
+class LuaScriptFactory final : public ScriptFactory {
+    std::unique_ptr<Script> Produce(agi::fs::path const &filename) const override;
+public:
+    LuaScriptFactory();
+};
 }
diff --git a/src/auto4_lua_progresssink.cpp b/src/auto4_lua_progresssink.cpp
index 026cb958370a2f103100bbc89e4dc6ef4bc6498f..6467164d5d48a4e78744e72d693969f78c02f657 100644
--- a/src/auto4_lua_progresssink.cpp
+++ b/src/auto4_lua_progresssink.cpp
@@ -43,217 +43,217 @@
 using namespace agi::lua;
 
 namespace {
-	template<lua_CFunction fn>
-	void set_field_to_closure(lua_State *L, const char *name, int ps_idx = -3)
-	{
-		lua_pushvalue(L, ps_idx);
-		lua_pushcclosure(L, exception_wrapper<fn>, 1);
-		lua_setfield(L, -2, name);
-	}
-
-	void set_field_to_nil(lua_State *L, int idx, const char *name)
-	{
-		lua_pushnil(L);
-		lua_setfield(L, idx, name);
-	}
-
-	wxString check_wxstring(lua_State *L, int idx)
-	{
-		return to_wx(check_string(L, idx));
-	}
+template<lua_CFunction fn>
+void set_field_to_closure(lua_State *L, const char *name, int ps_idx = -3)
+{
+    lua_pushvalue(L, ps_idx);
+    lua_pushcclosure(L, exception_wrapper<fn>, 1);
+    lua_setfield(L, -2, name);
+}
+
+void set_field_to_nil(lua_State *L, int idx, const char *name)
+{
+    lua_pushnil(L);
+    lua_setfield(L, idx, name);
+}
+
+wxString check_wxstring(lua_State *L, int idx)
+{
+    return to_wx(check_string(L, idx));
+}
 }
 
 namespace Automation4 {
-	LuaProgressSink::LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog)
-	: L(L)
-	{
-		auto ud = (ProgressSink**)lua_newuserdata(L, sizeof(ProgressSink*));
-		*ud = ps;
-
-		// register progress reporting stuff
-		lua_getglobal(L, "aegisub");
-
-		// Create aegisub.progress table
-		lua_createtable(L, 0, 5);
-		set_field_to_closure<LuaSetProgress>(L, "set");
-		set_field_to_closure<LuaSetTask>(L, "task");
-		set_field_to_closure<LuaSetTitle>(L, "title");
-		set_field_to_closure<LuaGetCancelled>(L, "is_cancelled");
-		lua_setfield(L, -2, "progress");
-
-		// Create aegisub.debug table
-		lua_createtable(L, 0, 4);
-		set_field_to_closure<LuaDebugOut>(L, "out");
-		lua_setfield(L, -2, "debug");
-
-		// Set aegisub.log
-		set_field_to_closure<LuaDebugOut>(L, "log", -2);
-
-		if (allow_config_dialog) {
-			lua_createtable(L, 0, 3);
-			set_field_to_closure<LuaDisplayDialog>(L, "display");
-			set_field_to_closure<LuaDisplayOpenDialog>(L, "open");
-			set_field_to_closure<LuaDisplaySaveDialog>(L, "save");
-			lua_setfield(L, -2, "dialog");
-		}
-
-		// reference so other objects can also find the progress sink
-		lua_pushvalue(L, -2);
-		lua_setfield(L, LUA_REGISTRYINDEX, "progress_sink");
-
-		lua_pop(L, 2);
-	}
-
-	LuaProgressSink::~LuaProgressSink()
-	{
-		// remove progress reporting stuff
-		lua_getglobal(L, "aegisub");
-		set_field_to_nil(L, -2, "progress");
-		set_field_to_nil(L, -2, "debug");
-		lua_pop(L, 1);
-
-		set_field_to_nil(L, LUA_REGISTRYINDEX, "progress_sink");
-	}
-
-	ProgressSink* LuaProgressSink::GetObjPointer(lua_State *L, int idx)
-	{
-		assert(lua_type(L, idx) == LUA_TUSERDATA);
-		return *((ProgressSink**)lua_touserdata(L, idx));
-	}
-
-	int LuaProgressSink::LuaSetProgress(lua_State *L)
-	{
-		GetObjPointer(L, lua_upvalueindex(1))->SetProgress(lua_tonumber(L, 1), 100);
-		return 0;
-	}
-
-	int LuaProgressSink::LuaSetTask(lua_State *L)
-	{
-		GetObjPointer(L, lua_upvalueindex(1))->SetMessage(check_string(L, 1));
-		return 0;
-	}
-
-	int LuaProgressSink::LuaSetTitle(lua_State *L)
-	{
-		GetObjPointer(L, lua_upvalueindex(1))->SetTitle(check_string(L, 1));
-		return 0;
-	}
-
-	int LuaProgressSink::LuaGetCancelled(lua_State *L)
-	{
-		lua_pushboolean(L, GetObjPointer(L, lua_upvalueindex(1))->IsCancelled());
-		return 1;
-	}
-
-	int LuaProgressSink::LuaDebugOut(lua_State *L)
-	{
-		ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
-
-		// Check trace level
-		if (lua_type(L, 1) == LUA_TNUMBER) {
-			if (lua_tointeger(L, 1) > ps->GetTraceLevel())
-				return 0;
-			// remove trace level
-			lua_remove(L, 1);
-		}
-
-		// Only do format-string handling if there's more than one argument left
-		// (If there's more than one argument left, assume first is a format string and rest are format arguments)
-		if (lua_gettop(L) > 1) {
-			// Format the string
-			lua_getglobal(L, "string");
-			lua_getfield(L, -1, "format");
-			// Here stack contains format string, format arguments, 'string' table, format function
-			// remove 'string' table
-			lua_remove(L, -2);
-			// put the format function into place
-			lua_insert(L, 1);
-			// call format function
-			if (lua_pcall(L, lua_gettop(L) - 1, 1, 0)) {
-				// format failed so top of the stack now has an error message
-				// which we want to add position information to
-				luaL_where(L, 1);
-				lua_insert(L, 1);
-				lua_concat(L, 2);
-				throw error_tag{};
-			}
-		}
-
-		// Top of stack is now a string to output
-		ps->Log(check_string(L, 1));
-		return 0;
-	}
-
-	int LuaProgressSink::LuaDisplayDialog(lua_State *L)
-	{
-		ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
-
-		LuaDialog dlg(L, true); // magically creates the config dialog structure etc
-		ps->ShowDialog(&dlg);
-
-		// more magic: puts two values on stack: button pushed and table with control results
-		return dlg.LuaReadBack(L);
-	}
-
-	int LuaProgressSink::LuaDisplayOpenDialog(lua_State *L)
-	{
-		ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
-		wxString message(check_wxstring(L, 1));
-		wxString dir(check_wxstring(L, 2));
-		wxString file(check_wxstring(L, 3));
-		wxString wildcard(check_wxstring(L, 4));
-		bool multiple = !!lua_toboolean(L, 5);
-		bool must_exist = lua_toboolean(L, 6) || lua_isnil(L, 6);
-
-		int flags = wxFD_OPEN;
-		if (multiple)
-			flags |= wxFD_MULTIPLE;
-		if (must_exist)
-			flags |= wxFD_FILE_MUST_EXIST;
-
-		wxFileDialog diag(nullptr, message, dir, file, wildcard, flags);
-		if (ps->ShowDialog(&diag) == wxID_CANCEL) {
-			lua_pushnil(L);
-			return 1;
-		}
-
-		if (multiple) {
-			wxArrayString files;
-			diag.GetPaths(files);
-
-			lua_createtable(L, files.size(), 0);
-			for (size_t i = 0; i < files.size(); ++i) {
-				lua_pushstring(L, files[i].utf8_str());
-				lua_rawseti(L, -2, i + 1);
-			}
-
-			return 1;
-		}
-
-		lua_pushstring(L, diag.GetPath().utf8_str());
-		return 1;
-	}
-
-	int LuaProgressSink::LuaDisplaySaveDialog(lua_State *L)
-	{
-		ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
-		wxString message(check_wxstring(L, 1));
-		wxString dir(check_wxstring(L, 2));
-		wxString file(check_wxstring(L, 3));
-		wxString wildcard(check_wxstring(L, 4));
-		bool prompt_overwrite = !lua_toboolean(L, 5);
-
-		int flags = wxFD_SAVE;
-		if (prompt_overwrite)
-			flags |= wxFD_OVERWRITE_PROMPT;
-
-		wxFileDialog diag(ps->GetParentWindow(), message, dir, file, wildcard, flags);
-		if (ps->ShowDialog(&diag) == wxID_CANCEL) {
-			lua_pushnil(L);
-			return 1;
-		}
-
-		lua_pushstring(L, diag.GetPath().utf8_str());
-		return 1;
-	}
+LuaProgressSink::LuaProgressSink(lua_State *L, ProgressSink *ps, bool allow_config_dialog)
+    : L(L)
+{
+    auto ud = (ProgressSink **)lua_newuserdata(L, sizeof(ProgressSink *));
+    *ud = ps;
+
+    // register progress reporting stuff
+    lua_getglobal(L, "aegisub");
+
+    // Create aegisub.progress table
+    lua_createtable(L, 0, 5);
+    set_field_to_closure<LuaSetProgress>(L, "set");
+    set_field_to_closure<LuaSetTask>(L, "task");
+    set_field_to_closure<LuaSetTitle>(L, "title");
+    set_field_to_closure<LuaGetCancelled>(L, "is_cancelled");
+    lua_setfield(L, -2, "progress");
+
+    // Create aegisub.debug table
+    lua_createtable(L, 0, 4);
+    set_field_to_closure<LuaDebugOut>(L, "out");
+    lua_setfield(L, -2, "debug");
+
+    // Set aegisub.log
+    set_field_to_closure<LuaDebugOut>(L, "log", -2);
+
+    if (allow_config_dialog) {
+        lua_createtable(L, 0, 3);
+        set_field_to_closure<LuaDisplayDialog>(L, "display");
+        set_field_to_closure<LuaDisplayOpenDialog>(L, "open");
+        set_field_to_closure<LuaDisplaySaveDialog>(L, "save");
+        lua_setfield(L, -2, "dialog");
+    }
+
+    // reference so other objects can also find the progress sink
+    lua_pushvalue(L, -2);
+    lua_setfield(L, LUA_REGISTRYINDEX, "progress_sink");
+
+    lua_pop(L, 2);
+}
+
+LuaProgressSink::~LuaProgressSink()
+{
+    // remove progress reporting stuff
+    lua_getglobal(L, "aegisub");
+    set_field_to_nil(L, -2, "progress");
+    set_field_to_nil(L, -2, "debug");
+    lua_pop(L, 1);
+
+    set_field_to_nil(L, LUA_REGISTRYINDEX, "progress_sink");
+}
+
+ProgressSink *LuaProgressSink::GetObjPointer(lua_State *L, int idx)
+{
+    assert(lua_type(L, idx) == LUA_TUSERDATA);
+    return *((ProgressSink **)lua_touserdata(L, idx));
+}
+
+int LuaProgressSink::LuaSetProgress(lua_State *L)
+{
+    GetObjPointer(L, lua_upvalueindex(1))->SetProgress(lua_tonumber(L, 1), 100);
+    return 0;
+}
+
+int LuaProgressSink::LuaSetTask(lua_State *L)
+{
+    GetObjPointer(L, lua_upvalueindex(1))->SetMessage(check_string(L, 1));
+    return 0;
+}
+
+int LuaProgressSink::LuaSetTitle(lua_State *L)
+{
+    GetObjPointer(L, lua_upvalueindex(1))->SetTitle(check_string(L, 1));
+    return 0;
+}
+
+int LuaProgressSink::LuaGetCancelled(lua_State *L)
+{
+    lua_pushboolean(L, GetObjPointer(L, lua_upvalueindex(1))->IsCancelled());
+    return 1;
+}
+
+int LuaProgressSink::LuaDebugOut(lua_State *L)
+{
+    ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+
+    // Check trace level
+    if (lua_type(L, 1) == LUA_TNUMBER) {
+        if (lua_tointeger(L, 1) > ps->GetTraceLevel())
+            return 0;
+        // remove trace level
+        lua_remove(L, 1);
+    }
+
+    // Only do format-string handling if there's more than one argument left
+    // (If there's more than one argument left, assume first is a format string and rest are format arguments)
+    if (lua_gettop(L) > 1) {
+        // Format the string
+        lua_getglobal(L, "string");
+        lua_getfield(L, -1, "format");
+        // Here stack contains format string, format arguments, 'string' table, format function
+        // remove 'string' table
+        lua_remove(L, -2);
+        // put the format function into place
+        lua_insert(L, 1);
+        // call format function
+        if (lua_pcall(L, lua_gettop(L) - 1, 1, 0)) {
+            // format failed so top of the stack now has an error message
+            // which we want to add position information to
+            luaL_where(L, 1);
+            lua_insert(L, 1);
+            lua_concat(L, 2);
+            throw error_tag{};
+        }
+    }
+
+    // Top of stack is now a string to output
+    ps->Log(check_string(L, 1));
+    return 0;
+}
+
+int LuaProgressSink::LuaDisplayDialog(lua_State *L)
+{
+    ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+
+    LuaDialog dlg(L, true); // magically creates the config dialog structure etc
+    ps->ShowDialog(&dlg);
+
+    // more magic: puts two values on stack: button pushed and table with control results
+    return dlg.LuaReadBack(L);
+}
+
+int LuaProgressSink::LuaDisplayOpenDialog(lua_State *L)
+{
+    ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+    wxString message(check_wxstring(L, 1));
+    wxString dir(check_wxstring(L, 2));
+    wxString file(check_wxstring(L, 3));
+    wxString wildcard(check_wxstring(L, 4));
+    bool multiple = !!lua_toboolean(L, 5);
+    bool must_exist = lua_toboolean(L, 6) || lua_isnil(L, 6);
+
+    int flags = wxFD_OPEN;
+    if (multiple)
+        flags |= wxFD_MULTIPLE;
+    if (must_exist)
+        flags |= wxFD_FILE_MUST_EXIST;
+
+    wxFileDialog diag(nullptr, message, dir, file, wildcard, flags);
+    if (ps->ShowDialog(&diag) == wxID_CANCEL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    if (multiple) {
+        wxArrayString files;
+        diag.GetPaths(files);
+
+        lua_createtable(L, files.size(), 0);
+        for (size_t i = 0; i < files.size(); ++i) {
+            lua_pushstring(L, files[i].utf8_str());
+            lua_rawseti(L, -2, i + 1);
+        }
+
+        return 1;
+    }
+
+    lua_pushstring(L, diag.GetPath().utf8_str());
+    return 1;
+}
+
+int LuaProgressSink::LuaDisplaySaveDialog(lua_State *L)
+{
+    ProgressSink *ps = GetObjPointer(L, lua_upvalueindex(1));
+    wxString message(check_wxstring(L, 1));
+    wxString dir(check_wxstring(L, 2));
+    wxString file(check_wxstring(L, 3));
+    wxString wildcard(check_wxstring(L, 4));
+    bool prompt_overwrite = !lua_toboolean(L, 5);
+
+    int flags = wxFD_SAVE;
+    if (prompt_overwrite)
+        flags |= wxFD_OVERWRITE_PROMPT;
+
+    wxFileDialog diag(ps->GetParentWindow(), message, dir, file, wildcard, flags);
+    if (ps->ShowDialog(&diag) == wxID_CANCEL) {
+        lua_pushnil(L);
+        return 1;
+    }
+
+    lua_pushstring(L, diag.GetPath().utf8_str());
+    return 1;
+}
 }
diff --git a/src/avisynth.h b/src/avisynth.h
index 7e15f2133663035bb7d8c1ba5991039d1d1289c7..20342041e86696340f61f15f0c10b85de7a73ca6 100644
--- a/src/avisynth.h
+++ b/src/avisynth.h
@@ -66,22 +66,22 @@ typedef	long			PixOffset;
 
 // Tell MSVC to stop precompiling here
 #ifdef _MSC_VER
-  #pragma hdrstop
+#pragma hdrstop
 #endif
 
 // Set up debugging macros for MS compilers; for others, step down to the
 // standard <assert.h> interface
 #ifdef _MSC_VER
-  #include <crtdbg.h>
+#include <crtdbg.h>
 #else
-  #define _RPT0(a,b) ((void)0)
-  #define _RPT1(a,b,c) ((void)0)
-  #define _RPT2(a,b,c,d) ((void)0)
-  #define _RPT3(a,b,c,d,e) ((void)0)
-  #define _RPT4(a,b,c,d,e,f) ((void)0)
-
-  #define _ASSERTE(x) assert(x)
-  #include <assert.h>
+#define _RPT0(a,b) ((void)0)
+#define _RPT1(a,b,c) ((void)0)
+#define _RPT2(a,b,c,d) ((void)0)
+#define _RPT3(a,b,c,d,e) ((void)0)
+#define _RPT4(a,b,c,d,e,f) ((void)0)
+
+#define _ASSERTE(x) assert(x)
+#include <assert.h>
 #endif
 
 
@@ -100,181 +100,182 @@ typedef	long			PixOffset;
 // Audio Sample information
 typedef float SFLOAT;
 
-enum {SAMPLE_INT8  = 1<<0,
-        SAMPLE_INT16 = 1<<1,
-        SAMPLE_INT24 = 1<<2,    // Int24 is a very stupid thing to code, but it's supported by some hardware.
-        SAMPLE_INT32 = 1<<3,
-        SAMPLE_FLOAT = 1<<4};
+enum {SAMPLE_INT8  = 1 << 0,
+      SAMPLE_INT16 = 1 << 1,
+      SAMPLE_INT24 = 1 << 2,  // Int24 is a very stupid thing to code, but it's supported by some hardware.
+      SAMPLE_INT32 = 1 << 3,
+      SAMPLE_FLOAT = 1 << 4
+     };
 
 enum {
-   PLANAR_Y=1<<0,
-   PLANAR_U=1<<1,
-   PLANAR_V=1<<2,
-   PLANAR_ALIGNED=1<<3,
-   PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED,
-   PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED,
-   PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED,
-  };
+    PLANAR_Y = 1 << 0,
+    PLANAR_U = 1 << 1,
+    PLANAR_V = 1 << 2,
+    PLANAR_ALIGNED = 1 << 3,
+    PLANAR_Y_ALIGNED = PLANAR_Y | PLANAR_ALIGNED,
+    PLANAR_U_ALIGNED = PLANAR_U | PLANAR_ALIGNED,
+    PLANAR_V_ALIGNED = PLANAR_V | PLANAR_ALIGNED,
+};
 
 struct VideoInfo {
-  int width, height;    // width=0 means no video
-  unsigned fps_numerator, fps_denominator;
-  int num_frames;
-  // This is more extensible than previous versions. More properties can be added seeminglesly.
-
-  // Colorspace properties.
-  enum {
-    CS_BGR = 1<<28,
-    CS_YUV = 1<<29,
-    CS_INTERLEAVED = 1<<30,
-    CS_PLANAR = 1<<31
-  };
-
-  // Specific colorformats
-  enum { CS_UNKNOWN = 0,
-         CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED,
-         CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED,
-         CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED,
-         CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR,  // y-v-u, planar
-         CS_I420 = 1<<4 | CS_YUV | CS_PLANAR,  // y-u-v, planar
-         CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR  // same as above
+    int width, height;    // width=0 means no video
+    unsigned fps_numerator, fps_denominator;
+    int num_frames;
+    // This is more extensible than previous versions. More properties can be added seeminglesly.
+
+    // Colorspace properties.
+    enum {
+        CS_BGR = 1 << 28,
+        CS_YUV = 1 << 29,
+        CS_INTERLEAVED = 1 << 30,
+        CS_PLANAR = 1 << 31
+    };
+
+    // Specific colorformats
+    enum { CS_UNKNOWN = 0,
+           CS_BGR24 = 1 << 0 | CS_BGR | CS_INTERLEAVED,
+           CS_BGR32 = 1 << 1 | CS_BGR | CS_INTERLEAVED,
+           CS_YUY2 = 1 << 2 | CS_YUV | CS_INTERLEAVED,
+           CS_YV12 = 1 << 3 | CS_YUV | CS_PLANAR, // y-v-u, planar
+           CS_I420 = 1 << 4 | CS_YUV | CS_PLANAR, // y-u-v, planar
+           CS_IYUV = 1 << 4 | CS_YUV | CS_PLANAR // same as above
          };
-  int pixel_type;                // changed to int as of 2.5
-
-
-  int audio_samples_per_second;   // 0 means no audio
-  int sample_type;                // as of 2.5
-  __int64 num_audio_samples;      // changed as of 2.5
-  int nchannels;                  // as of 2.5
-
-  // Imagetype properties
-
-  int image_type;
-
-  enum {
-    IT_BFF = 1<<0,
-    IT_TFF = 1<<1,
-    IT_FIELDBASED = 1<<2
-  };
-
-  // useful functions of the above
-  bool HasVideo() const { return (width!=0); }
-  bool HasAudio() const { return (audio_samples_per_second!=0); }
-  bool IsRGB() const { return !!(pixel_type&CS_BGR); }
-  bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties
-  bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; }
-  bool IsYUV() const { return !!(pixel_type&CS_YUV ); }
-  bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; }
-  bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); }
-  bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); }
-  bool Is(int property) const { return ((pixel_type & property)==property ); }
-  bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); }
-  bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); }
-  bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF|IT_TFF))); }
-  bool IsBFF() const { return !!(image_type & IT_BFF); }
-  bool IsTFF() const { return !!(image_type & IT_TFF); }
-
-  bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); }  // Don't use this
-  int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); }   // Will not work on planar images, but will return only luma planes
-  int RowSize() const { return BytesFromPixels(width); }  // Also only returns first plane on planar images
-  int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p;  } return height * ((RowSize()+3) & ~3); }
-  __int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; }
-  int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator)/((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; }
-  __int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; }
-  __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); }
-  int AudioChannels() const { return nchannels; }
-  int SampleType() const{ return sample_type;}
-  bool IsSampleType(int testtype) const{ return !!(sample_type&testtype);}
-  int SamplesPerSecond() const { return audio_samples_per_second; }
-  int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();}
-  void SetFieldBased(bool isfieldbased)  { if (isfieldbased) image_type|=IT_FIELDBASED; else  image_type&=~IT_FIELDBASED; }
-  void Set(int property)  { image_type|=property; }
-  void Clear(int property)  { image_type&=~property; }
-
-  int BitsPerPixel() const {
-    switch (pixel_type) {
-      case CS_BGR24:
-        return 24;
-      case CS_BGR32:
-        return 32;
-      case CS_YUY2:
-        return 16;
-      case CS_YV12:
-      case CS_I420:
-        return 12;
-      default:
-        return 0;
+    int pixel_type;                // changed to int as of 2.5
+
+
+    int audio_samples_per_second;   // 0 means no audio
+    int sample_type;                // as of 2.5
+    __int64 num_audio_samples;      // changed as of 2.5
+    int nchannels;                  // as of 2.5
+
+    // Imagetype properties
+
+    int image_type;
+
+    enum {
+        IT_BFF = 1 << 0,
+        IT_TFF = 1 << 1,
+        IT_FIELDBASED = 1 << 2
+    };
+
+    // useful functions of the above
+    bool HasVideo() const { return (width != 0); }
+    bool HasAudio() const { return (audio_samples_per_second != 0); }
+    bool IsRGB() const { return !!(pixel_type & CS_BGR); }
+    bool IsRGB24() const { return (pixel_type & CS_BGR24) == CS_BGR24; } // Clear out additional properties
+    bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; }
+    bool IsYUV() const { return !!(pixel_type & CS_YUV ); }
+    bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; }
+    bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12) || ((pixel_type & CS_I420) == CS_I420); }
+    bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); }
+    bool Is(int property) const { return ((pixel_type & property) == property ); }
+    bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); }
+    bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); }
+    bool IsParityKnown() const { return ((image_type & IT_FIELDBASED) && (image_type & (IT_BFF | IT_TFF))); }
+    bool IsBFF() const { return !!(image_type & IT_BFF); }
+    bool IsTFF() const { return !!(image_type & IT_TFF); }
+
+    bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); }  // Don't use this
+    int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel() >> 3); } // Will not work on planar images, but will return only luma planes
+    int RowSize() const { return BytesFromPixels(width); }  // Also only returns first plane on planar images
+    int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize() + 3) & ~3); p += p >> 1; return p;  } return height * ((RowSize() + 3) & ~3); }
+    __int64 AudioSamplesFromFrames(__int64 frames) const { return (fps_numerator && HasVideo()) ? ((__int64)(frames) * audio_samples_per_second * fps_denominator / fps_numerator) : 0; }
+    int FramesFromAudioSamples(__int64 samples) const { return (fps_denominator && HasAudio()) ? (int)((samples * (__int64)fps_numerator) / ((__int64)fps_denominator * (__int64)audio_samples_per_second)) : 0; }
+    __int64 AudioSamplesFromBytes(__int64 bytes) const { return HasAudio() ? bytes / BytesPerAudioSample() : 0; }
+    __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); }
+    int AudioChannels() const { return nchannels; }
+    int SampleType() const { return sample_type;}
+    bool IsSampleType(int testtype) const { return !!(sample_type & testtype);}
+    int SamplesPerSecond() const { return audio_samples_per_second; }
+    int BytesPerAudioSample() const { return nchannels * BytesPerChannelSample();}
+    void SetFieldBased(bool isfieldbased)  { if (isfieldbased) image_type |= IT_FIELDBASED; else  image_type &= ~IT_FIELDBASED; }
+    void Set(int property)  { image_type |= property; }
+    void Clear(int property)  { image_type &= ~property; }
+
+    int BitsPerPixel() const {
+        switch (pixel_type) {
+        case CS_BGR24:
+            return 24;
+        case CS_BGR32:
+            return 32;
+        case CS_YUY2:
+            return 16;
+        case CS_YV12:
+        case CS_I420:
+            return 12;
+        default:
+            return 0;
+        }
+    }
+    int BytesPerChannelSample() const {
+        switch (sample_type) {
+        case SAMPLE_INT8:
+            return sizeof(signed char);
+        case SAMPLE_INT16:
+            return sizeof(signed short);
+        case SAMPLE_INT24:
+            return 3;
+        case SAMPLE_INT32:
+            return sizeof(signed int);
+        case SAMPLE_FLOAT:
+            return sizeof(SFLOAT);
+        default:
+            _ASSERTE("Sample type not recognized!");
+            return 0;
+        }
+    }
+
+    // useful mutator
+    void SetFPS(unsigned numerator, unsigned denominator) {
+        if ((numerator == 0) || (denominator == 0)) {
+            fps_numerator = 0;
+            fps_denominator = 1;
+        }
+        else {
+            unsigned x = numerator, y = denominator;
+            while (y) {   // find gcd
+                unsigned t = x % y; x = y; y = t;
+            }
+            fps_numerator = numerator / x;
+            fps_denominator = denominator / x;
+        }
     }
-  }
-  int BytesPerChannelSample() const {
-    switch (sample_type) {
-    case SAMPLE_INT8:
-      return sizeof(signed char);
-    case SAMPLE_INT16:
-      return sizeof(signed short);
-    case SAMPLE_INT24:
-      return 3;
-    case SAMPLE_INT32:
-      return sizeof(signed int);
-    case SAMPLE_FLOAT:
-      return sizeof(SFLOAT);
-    default:
-      _ASSERTE("Sample type not recognized!");
-      return 0;
+
+    // Range protected multiply-divide of FPS
+    void MulDivFPS(unsigned multiplier, unsigned divisor) {
+        unsigned __int64 numerator   = UInt32x32To64(fps_numerator,   multiplier);
+        unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor);
+
+        unsigned __int64 x = numerator, y = denominator;
+        while (y) {   // find gcd
+            unsigned __int64 t = x % y; x = y; y = t;
+        }
+        numerator   /= x; // normalize
+        denominator /= x;
+
+        unsigned __int64 temp = numerator | denominator; // Just looking top bit
+        unsigned u = 0;
+        while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2
+            temp = Int64ShrlMod32(temp, 1);
+            u++;
+        }
+        if (u) { // Scale to fit
+            const unsigned round = 1 << (u - 1);
+            SetFPS( (unsigned)Int64ShrlMod32(numerator   + round, u),
+                    (unsigned)Int64ShrlMod32(denominator + round, u) );
+        }
+        else {
+            fps_numerator   = (unsigned)numerator;
+            fps_denominator = (unsigned)denominator;
+        }
+    }
+
+    // Test for same colorspace
+    bool IsSameColorspace(const VideoInfo &vi) const {
+        if (vi.pixel_type == pixel_type) return TRUE;
+        if (IsYV12() && vi.IsYV12()) return TRUE;
+        return FALSE;
     }
-  }
-
-  // useful mutator
-  void SetFPS(unsigned numerator, unsigned denominator) {
-	if ((numerator == 0) || (denominator == 0)) {
-	  fps_numerator = 0;
-	  fps_denominator = 1;
-	}
-	else {
-	  unsigned x=numerator, y=denominator;
-	  while (y) {   // find gcd
-		unsigned t = x%y; x = y; y = t;
-	  }
-	  fps_numerator = numerator/x;
-	  fps_denominator = denominator/x;
-	}
-  }
-
-  // Range protected multiply-divide of FPS
-  void MulDivFPS(unsigned multiplier, unsigned divisor) {
-	unsigned __int64 numerator   = UInt32x32To64(fps_numerator,   multiplier);
-	unsigned __int64 denominator = UInt32x32To64(fps_denominator, divisor);
-
-	unsigned __int64 x=numerator, y=denominator;
-	while (y) {   // find gcd
-	  unsigned __int64 t = x%y; x = y; y = t;
-	}
-	numerator   /= x; // normalize
-	denominator /= x;
-
-	unsigned __int64 temp = numerator | denominator; // Just looking top bit
-	unsigned u = 0;
-	while (temp & 0xffffffff80000000) { // or perhaps > 16777216*2
-	  temp = Int64ShrlMod32(temp, 1);
-	  u++;
-	}
-	if (u) { // Scale to fit
-	  const unsigned round = 1 << (u-1);
-	  SetFPS( (unsigned)Int64ShrlMod32(numerator   + round, u),
-	          (unsigned)Int64ShrlMod32(denominator + round, u) );
-	}
-	else {
-	  fps_numerator   = (unsigned)numerator;
-	  fps_denominator = (unsigned)denominator;
-	}
-  }
-
-  // Test for same colorspace
-  bool IsSameColorspace(const VideoInfo& vi) const {
-    if (vi.pixel_type == pixel_type) return TRUE;
-    if (IsYV12() && vi.IsYV12()) return TRUE;
-    return FALSE;
-  }
 
 };
 
@@ -288,27 +289,27 @@ struct VideoInfo {
 // file is closed.
 
 class VideoFrameBuffer {
-  BYTE* const data;
-  const int data_size;
-  // sequence_number is incremented every time the buffer is changed, so
-  // that stale views can tell they're no longer valid.
-  long sequence_number;
+    BYTE *const data;
+    const int data_size;
+    // sequence_number is incremented every time the buffer is changed, so
+    // that stale views can tell they're no longer valid.
+    long sequence_number;
 
-  friend class VideoFrame;
-  friend class Cache;
-  friend class ScriptEnvironment;
-  long refcount;
+    friend class VideoFrame;
+    friend class Cache;
+    friend class ScriptEnvironment;
+    long refcount;
 
 public:
-  VideoFrameBuffer(int size);
-  VideoFrameBuffer();
-  ~VideoFrameBuffer();
-
-  const BYTE* GetReadPtr() const { return data; }
-  BYTE* GetWritePtr() { ++sequence_number; return data; }
-  int GetDataSize() { return data_size; }
-  int GetSequenceNumber() { return sequence_number; }
-  int GetRefcount() { return refcount; }
+    VideoFrameBuffer(int size);
+    VideoFrameBuffer();
+    ~VideoFrameBuffer();
+
+    const BYTE *GetReadPtr() const { return data; }
+    BYTE *GetWritePtr() { ++sequence_number; return data; }
+    int GetDataSize() { return data_size; }
+    int GetSequenceNumber() { return sequence_number; }
+    int GetRefcount() { return refcount; }
 };
 
 
@@ -323,275 +324,277 @@ class AVSValue;
 // is overloaded to recycle class instances.
 
 class VideoFrame {
-  int refcount;
-  VideoFrameBuffer* const vfb;
-  const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV;  // U&V offsets are from top of picture.
+    int refcount;
+    VideoFrameBuffer *const vfb;
+    const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV;  // U&V offsets are from top of picture.
 
-  friend class PVideoFrame;
-  void AddRef() { InterlockedIncrement((long *)&refcount); }
-  void Release() { if (refcount==1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); }
+    friend class PVideoFrame;
+    void AddRef() { InterlockedIncrement((long *)&refcount); }
+    void Release() { if (refcount == 1) InterlockedDecrement(&vfb->refcount); InterlockedDecrement((long *)&refcount); }
 
-  friend class ScriptEnvironment;
-  friend class Cache;
+    friend class ScriptEnvironment;
+    friend class Cache;
 
-  VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height);
-  VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV);
+    VideoFrame(VideoFrameBuffer *_vfb, int _offset, int _pitch, int _row_size, int _height);
+    VideoFrame(VideoFrameBuffer *_vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV);
 
-  void* operator new(size_t size);
+    void *operator new (size_t size);
 // TESTME: OFFSET U/V may be switched to what could be expected from AVI standard!
 public:
-  int GetPitch() const { return pitch; }
-  int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; }
-  int GetRowSize() const { return row_size; }
-  int GetRowSize(int plane) const {
-    switch (plane) {
-    case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0;
-    case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED:
-      if (pitchUV) {
-        int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize
-        if (r<=pitchUV)
-          return r;
-        return row_size>>1;
-      } else return 0;
-    case PLANAR_Y_ALIGNED:
-      int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize
-      if (r<=pitch)
-        return r;
-      return row_size;
+    int GetPitch() const { return pitch; }
+int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; }
+    int GetRowSize() const { return row_size; }
+    int GetRowSize(int plane) const {
+        switch (plane) {
+        case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size >> 1; else return 0;
+        case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED:
+            if (pitchUV) {
+                int r = ((row_size + FRAME_ALIGN - 1) & (~(FRAME_ALIGN - 1)) ) >> 1; // Aligned rowsize
+                if (r <= pitchUV)
+                    return r;
+                return row_size >> 1;
+            }
+            else return 0;
+        case PLANAR_Y_ALIGNED:
+            int r = (row_size + FRAME_ALIGN - 1) & (~(FRAME_ALIGN - 1)); // Aligned rowsize
+            if (r <= pitch)
+                return r;
+            return row_size;
+        }
+        return row_size;
     }
-    return row_size; }
-  int GetHeight() const { return height; }
-  int GetHeight(int plane) const {  switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; }
+    int GetHeight() const { return height; }
+int GetHeight(int plane) const {  switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height >> 1; return 0;} return height; }
 
-  // generally you shouldn't use these three
-  VideoFrameBuffer* GetFrameBuffer() const { return vfb; }
-  int GetOffset() const { return offset; }
-  int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; }
+    // generally you shouldn't use these three
+    VideoFrameBuffer *GetFrameBuffer() const { return vfb; }
+    int GetOffset() const { return offset; }
+int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU; case PLANAR_V: return offsetV; default: return offset;}; }
 
-  // in plugins use env->SubFrame()
-  VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
-  VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const;
+    // in plugins use env->SubFrame()
+    VideoFrame *Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
+    VideoFrame *Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const;
 
 
-  const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; }
-  const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); }
+    const BYTE *GetReadPtr() const { return vfb->GetReadPtr() + offset; }
+    const BYTE *GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); }
 
-  bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
+    bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
 
-  BYTE* GetWritePtr() const {
-    if (vfb->GetRefcount()>1) {
-      _ASSERT(FALSE);
-      //throw AvisynthError("Internal Error - refcount was more than one!");
+    BYTE *GetWritePtr() const {
+        if (vfb->GetRefcount() > 1) {
+            _ASSERT(FALSE);
+            //throw AvisynthError("Internal Error - refcount was more than one!");
+        }
+        return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
     }
-    return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
-  }
 
-  BYTE* GetWritePtr(int plane) const {
-    if (plane==PLANAR_Y) {
-      if (vfb->GetRefcount()>1) {
-        _ASSERT(FALSE);
+    BYTE *GetWritePtr(int plane) const {
+        if (plane == PLANAR_Y) {
+            if (vfb->GetRefcount() > 1) {
+                _ASSERT(FALSE);
 //        throw AvisynthError("Internal Error - refcount was more than one!");
-      }
-      return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0;
+            }
+            return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0;
+        }
+        return vfb->data + GetOffset(plane);
     }
-    return vfb->data + GetOffset(plane);
-  }
 
-  ~VideoFrame() { InterlockedDecrement(&vfb->refcount); }
+    ~VideoFrame() { InterlockedDecrement(&vfb->refcount); }
 };
 
 enum {
-  CACHE_NOTHING=0,
-  CACHE_RANGE=1,
-  CACHE_ALL=2,
-  CACHE_AUDIO=3,
-  CACHE_AUDIO_NONE=4
- };
+    CACHE_NOTHING = 0,
+    CACHE_RANGE = 1,
+    CACHE_ALL = 2,
+    CACHE_AUDIO = 3,
+    CACHE_AUDIO_NONE = 4
+};
 
 // Base class for all filters.
 class IClip {
-  friend class PClip;
-  friend class AVSValue;
-  int refcnt;
-  void AddRef() { InterlockedIncrement((long *)&refcnt); }
-  void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; }
+    friend class PClip;
+    friend class AVSValue;
+    int refcnt;
+    void AddRef() { InterlockedIncrement((long *)&refcnt); }
+    void Release() { InterlockedDecrement((long *)&refcnt); if (!refcnt) delete this; }
 public:
-  IClip() : refcnt(0) {}
+    IClip() : refcnt(0) {}
 
-  virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
+    virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
 
-  virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
-  virtual bool __stdcall GetParity(int n) = 0;  // return field parity if field_based, else parity of first field in frame
-  virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0;  // start and count are in samples
-  virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ;  // We do not pass cache requests upwards, only to the next filter.
-  virtual const VideoInfo& __stdcall GetVideoInfo() = 0;
-  virtual __stdcall ~IClip() {}
+    virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env) = 0;
+    virtual bool __stdcall GetParity(int n) = 0;  // return field parity if field_based, else parity of first field in frame
+    virtual void __stdcall GetAudio(void *buf, __int64 start, __int64 count, IScriptEnvironment *env) = 0;  // start and count are in samples
+    virtual void __stdcall SetCacheHints(int cachehints, int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter.
+    virtual const VideoInfo &__stdcall GetVideoInfo() = 0;
+    virtual __stdcall ~IClip() {}
 };
 
 
 // smart pointer to IClip
 class PClip {
 
-  IClip* p;
+    IClip *p;
 
-  IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
-  friend class AVSValue;
-  friend class VideoFrame;
+    IClip *GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
+    friend class AVSValue;
+    friend class VideoFrame;
 
-  void Init(IClip* x) {
-    if (x) x->AddRef();
-    p=x;
-  }
-  void Set(IClip* x) {
-    if (x) x->AddRef();
-    if (p) p->Release();
-    p=x;
-  }
+    void Init(IClip *x) {
+        if (x) x->AddRef();
+        p = x;
+    }
+    void Set(IClip *x) {
+        if (x) x->AddRef();
+        if (p) p->Release();
+        p = x;
+    }
 
 public:
-  PClip() { p = 0; }
-  PClip(const PClip& x) { Init(x.p); }
-  PClip(IClip* x) { Init(x); }
-  void operator=(IClip* x) { Set(x); }
-  void operator=(const PClip& x) { Set(x.p); }
+    PClip() { p = 0; }
+    PClip(const PClip &x) { Init(x.p); }
+    PClip(IClip *x) { Init(x); }
+    void operator=(IClip *x) { Set(x); }
+    void operator=(const PClip &x) { Set(x.p); }
 
-  IClip* operator->() const { return p; }
+    IClip *operator->() const { return p; }
 
-  // useful in conditional expressions
-  operator void*() const { return p; }
-  bool operator!() const { return !p; }
+    // useful in conditional expressions
+    operator void *() const { return p; }
+    bool operator!() const { return !p; }
 
-  ~PClip() { if (p) p->Release(); }
+    ~PClip() { if (p) p->Release(); }
 };
 
 
 // smart pointer to VideoFrame
 class PVideoFrame {
 
-  VideoFrame* p;
+    VideoFrame *p;
 
-  void Init(VideoFrame* x) {
-    if (x) x->AddRef();
-    p=x;
-  }
-  void Set(VideoFrame* x) {
-    if (x) x->AddRef();
-    if (p) p->Release();
-    p=x;
-  }
+    void Init(VideoFrame *x) {
+        if (x) x->AddRef();
+        p = x;
+    }
+    void Set(VideoFrame *x) {
+        if (x) x->AddRef();
+        if (p) p->Release();
+        p = x;
+    }
 
 public:
-  PVideoFrame() { p = 0; }
-  PVideoFrame(const PVideoFrame& x) { Init(x.p); }
-  PVideoFrame(VideoFrame* x) { Init(x); }
-  void operator=(VideoFrame* x) { Set(x); }
-  void operator=(const PVideoFrame& x) { Set(x.p); }
+    PVideoFrame() { p = 0; }
+    PVideoFrame(const PVideoFrame &x) { Init(x.p); }
+    PVideoFrame(VideoFrame *x) { Init(x); }
+    void operator=(VideoFrame *x) { Set(x); }
+    void operator=(const PVideoFrame &x) { Set(x.p); }
 
-  VideoFrame* operator->() const { return p; }
+    VideoFrame *operator->() const { return p; }
 
-  // for conditional expressions
-  operator void*() const { return p; }
-  bool operator!() const { return !p; }
+    // for conditional expressions
+    operator void *() const { return p; }
+    bool operator!() const { return !p; }
 
-  ~PVideoFrame() { if (p) p->Release();}
+    ~PVideoFrame() { if (p) p->Release();}
 };
 
 
 class AVSValue {
 public:
 
-  AVSValue() { type = 'v'; }
-  AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); }
-  AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
-  AVSValue(bool b) { type = 'b'; boolean = b; }
-  AVSValue(int i) { type = 'i'; integer = i; }
+    AVSValue() { type = 'v'; }
+    AVSValue(IClip *c) { type = 'c'; clip = c; if (c) c->AddRef(); }
+    AVSValue(const PClip &c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
+    AVSValue(bool b) { type = 'b'; boolean = b; }
+    AVSValue(int i) { type = 'i'; integer = i; }
 //  AVSValue(__int64 l) { type = 'l'; longlong = l; }
-  AVSValue(float f) { type = 'f'; floating_pt = f; }
-  AVSValue(double f) { type = 'f'; floating_pt = float(f); }
-  AVSValue(const char* s) { type = 's'; string = s; }
-  AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; }
-  AVSValue(const AVSValue& v) { Assign(&v, true); }
-
-  ~AVSValue() { if (IsClip() && clip) clip->Release(); }
-  AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; }
-
-  // Note that we transparently allow 'int' to be treated as 'float'.
-  // There are no int<->bool conversions, though.
-
-  bool Defined() const { return type != 'v'; }
-  bool IsClip() const { return type == 'c'; }
-  bool IsBool() const { return type == 'b'; }
-  bool IsInt() const { return type == 'i'; }
+    AVSValue(float f) { type = 'f'; floating_pt = f; }
+    AVSValue(double f) { type = 'f'; floating_pt = float(f); }
+    AVSValue(const char *s) { type = 's'; string = s; }
+    AVSValue(const AVSValue *a, int size) { type = 'a'; array = a; array_size = size; }
+    AVSValue(const AVSValue &v) { Assign(&v, true); }
+
+    ~AVSValue() { if (IsClip() && clip) clip->Release(); }
+    AVSValue &operator=(const AVSValue &v) { Assign(&v, false); return *this; }
+
+    // Note that we transparently allow 'int' to be treated as 'float'.
+    // There are no int<->bool conversions, though.
+
+    bool Defined() const { return type != 'v'; }
+    bool IsClip() const { return type == 'c'; }
+    bool IsBool() const { return type == 'b'; }
+    bool IsInt() const { return type == 'i'; }
 //  bool IsLong() const { return (type == 'l'|| type == 'i'); }
-  bool IsFloat() const { return type == 'f' || type == 'i'; }
-  bool IsString() const { return type == 's'; }
-  bool IsArray() const { return type == 'a'; }
+    bool IsFloat() const { return type == 'f' || type == 'i'; }
+    bool IsString() const { return type == 's'; }
+    bool IsArray() const { return type == 'a'; }
 
-  PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; }
-  bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
-  int AsInt() const { _ASSERTE(IsInt()); return integer; }
+    PClip AsClip() const { _ASSERTE(IsClip()); return IsClip() ? clip : 0; }
+    bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
+    int AsInt() const { _ASSERTE(IsInt()); return integer; }
 //  int AsLong() const { _ASSERTE(IsLong()); return longlong; }
-  const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; }
-  double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; }
+    const char *AsString() const { _ASSERTE(IsString()); return IsString() ? string : 0; }
+    double AsFloat() const { _ASSERTE(IsFloat()); return IsInt() ? integer : floating_pt; }
 
-  bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; }
-  int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; }
-  double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; }
-  const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; }
+    bool AsBool(bool def) const { _ASSERTE(IsBool() || !Defined()); return IsBool() ? boolean : def; }
+    int AsInt(int def) const { _ASSERTE(IsInt() || !Defined()); return IsInt() ? integer : def; }
+    double AsFloat(double def) const { _ASSERTE(IsFloat() || !Defined()); return IsInt() ? integer : type == 'f' ? floating_pt : def; }
+    const char *AsString(const char *def) const { _ASSERTE(IsString() || !Defined()); return IsString() ? string : def; }
 
-  int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; }
+    int ArraySize() const { _ASSERTE(IsArray()); return IsArray() ? array_size : 1; }
 
-  const AVSValue& operator[](int index) const {
-    _ASSERTE(IsArray() && index>=0 && index<array_size);
-    return (IsArray() && index>=0 && index<array_size) ? array[index] : *this;
-  }
+    const AVSValue &operator[](int index) const {
+        _ASSERTE(IsArray() && index >= 0 && index < array_size);
+        return (IsArray() && index >= 0 && index < array_size) ? array[index] : *this;
+    }
 
 private:
 
-  short type;  // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
-  short array_size;
-  union {
-    IClip* clip;
-    bool boolean;
-    int integer;
-    float floating_pt;
-    const char* string;
-    const AVSValue* array;
+    short type;  // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
+    short array_size;
+    union {
+        IClip *clip;
+        bool boolean;
+        int integer;
+        float floating_pt;
+        const char *string;
+        const AVSValue *array;
 //    __int64 longlong;
-  };
-
-  void Assign(const AVSValue* src, bool init) {
-    if (src->IsClip() && src->clip)
-      src->clip->AddRef();
-    if (!init && IsClip() && clip)
-      clip->Release();
-    // make sure this copies the whole struct!
-    //((__int32*)this)[0] = ((__int32*)src)[0];
-    //((__int32*)this)[1] = ((__int32*)src)[1];
-	memcpy(this, src, sizeof(AVSValue));
-  }
+    };
+
+    void Assign(const AVSValue *src, bool init) {
+        if (src->IsClip() && src->clip)
+            src->clip->AddRef();
+        if (!init && IsClip() && clip)
+            clip->Release();
+        // make sure this copies the whole struct!
+        //((__int32*)this)[0] = ((__int32*)src)[0];
+        //((__int32*)this)[1] = ((__int32*)src)[1];
+        memcpy(this, src, sizeof(AVSValue));
+    }
 };
 
 
 // instantiable null filter
 class GenericVideoFilter : public IClip {
 protected:
-  PClip child;
-  VideoInfo vi;
+    PClip child;
+    VideoInfo vi;
 public:
-  GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
-  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
-  void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); }
-  const VideoInfo& __stdcall GetVideoInfo() { return vi; }
-  bool __stdcall GetParity(int n) { return child->GetParity(n); }
-  void __stdcall SetCacheHints(int cachehints,int frame_range) { } ;  // We do not pass cache requests upwards, only to the next filter.
+    GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
+    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env) { return child->GetFrame(n, env); }
+    void __stdcall GetAudio(void *buf, __int64 start, __int64 count, IScriptEnvironment *env) { child->GetAudio(buf, start, count, env); }
+    const VideoInfo &__stdcall GetVideoInfo() { return vi; }
+    bool __stdcall GetParity(int n) { return child->GetParity(n); }
+    void __stdcall SetCacheHints(int cachehints, int frame_range) { } ; // We do not pass cache requests upwards, only to the next filter.
 };
 
 
-class AvisynthError /* exception */ {
+class AvisynthError { /* exception */
 public:
-  const char* const msg;
-  AvisynthError(const char* _msg) : msg(_msg) {}
+    const char *const msg;
+    AvisynthError(const char *_msg) : msg(_msg) {}
 };
 
 
@@ -599,22 +602,20 @@ public:
 
 /* Helper classes useful to plugin authors */
 
-class AlignPlanar : public GenericVideoFilter
-{
+class AlignPlanar : public GenericVideoFilter {
 public:
-  AlignPlanar(PClip _clip);
-  static PClip Create(PClip clip);
-  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
+    AlignPlanar(PClip _clip);
+    static PClip Create(PClip clip);
+    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env);
 };
 
 
 
-class FillBorder : public GenericVideoFilter
-{
+class FillBorder : public GenericVideoFilter {
 public:
-  FillBorder(PClip _clip);
-  static PClip Create(PClip clip);
-  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
+    FillBorder(PClip _clip);
+    static PClip Create(PClip clip);
+    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *env);
 };
 
 
@@ -625,56 +626,56 @@ class ConvertAudio : public GenericVideoFilter
  **/
 {
 public:
-  ConvertAudio(PClip _clip, int prefered_format);
-  void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env);
-  void __stdcall SetCacheHints(int cachehints,int frame_range);  // We do pass cache requests upwards, to the cache!
-
-  static PClip Create(PClip clip, int sample_type, int prefered_type);
-  static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*);
-  static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*);
-  static AVSValue __cdecl Create_24bit(AVSValue args, void*, IScriptEnvironment*);
-  static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*);
-  static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*);
-  virtual ~ConvertAudio();
+    ConvertAudio(PClip _clip, int prefered_format);
+    void __stdcall GetAudio(void *buf, __int64 start, __int64 count, IScriptEnvironment *env);
+    void __stdcall SetCacheHints(int cachehints, int frame_range); // We do pass cache requests upwards, to the cache!
+
+    static PClip Create(PClip clip, int sample_type, int prefered_type);
+    static AVSValue __cdecl Create_float(AVSValue args, void *, IScriptEnvironment *);
+    static AVSValue __cdecl Create_32bit(AVSValue args, void *, IScriptEnvironment *);
+    static AVSValue __cdecl Create_24bit(AVSValue args, void *, IScriptEnvironment *);
+    static AVSValue __cdecl Create_16bit(AVSValue args, void *, IScriptEnvironment *);
+    static AVSValue __cdecl Create_8bit(AVSValue args, void *, IScriptEnvironment *);
+    virtual ~ConvertAudio();
 
 private:
-  void convertToFloat(char* inbuf, float* outbuf, char sample_type, int count);
-  void convertToFloat_3DN(char* inbuf, float* outbuf, char sample_type, int count);
-  void convertToFloat_SSE(char* inbuf, float* outbuf, char sample_type, int count);
-  void convertToFloat_SSE2(char* inbuf, float* outbuf, char sample_type, int count);
-  void convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count);
-  void convertFromFloat_3DN(float* inbuf, void* outbuf, char sample_type, int count);
-  void convertFromFloat_SSE(float* inbuf, void* outbuf, char sample_type, int count);
-  void convertFromFloat_SSE2(float* inbuf, void* outbuf, char sample_type, int count);
-
-  __inline int Saturate_int8(float n);
-  __inline short Saturate_int16(float n);
-  __inline int Saturate_int24(float n);
-  __inline int Saturate_int32(float n);
-
-  char src_format;
-  char dst_format;
-  int src_bps;
-  char *tempbuffer;
-  SFLOAT *floatbuffer;
-  int tempbuffer_size;
+    void convertToFloat(char *inbuf, float *outbuf, char sample_type, int count);
+    void convertToFloat_3DN(char *inbuf, float *outbuf, char sample_type, int count);
+    void convertToFloat_SSE(char *inbuf, float *outbuf, char sample_type, int count);
+    void convertToFloat_SSE2(char *inbuf, float *outbuf, char sample_type, int count);
+    void convertFromFloat(float *inbuf, void *outbuf, char sample_type, int count);
+    void convertFromFloat_3DN(float *inbuf, void *outbuf, char sample_type, int count);
+    void convertFromFloat_SSE(float *inbuf, void *outbuf, char sample_type, int count);
+    void convertFromFloat_SSE2(float *inbuf, void *outbuf, char sample_type, int count);
+
+    __inline int Saturate_int8(float n);
+    __inline short Saturate_int16(float n);
+    __inline int Saturate_int24(float n);
+    __inline int Saturate_int32(float n);
+
+    char src_format;
+    char dst_format;
+    int src_bps;
+    char *tempbuffer;
+    SFLOAT *floatbuffer;
+    int tempbuffer_size;
 };
 
 
 // For GetCPUFlags.  These are backwards-compatible with those in VirtualDub.
 enum {
-                    /* slowest CPU to support extension */
-  CPUF_FORCE			  = 0x01,   // N/A
-  CPUF_FPU			    = 0x02,   // 386/486DX
-  CPUF_MMX			    = 0x04,   // P55C, K6, PII
-  CPUF_INTEGER_SSE	= 0x08,		// PIII, Athlon
-  CPUF_SSE			    = 0x10,		// PIII, Athlon XP/MP
-  CPUF_SSE2			    = 0x20,		// PIV, Hammer
-  CPUF_3DNOW			  = 0x40,   // K6-2
-  CPUF_3DNOW_EXT		= 0x80,		// Athlon
-  CPUF_X86_64       = 0xA0,   // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
-                              //         will have anyway)
-  CPUF_SSE3		= 0x100,		    // Some P4 & Athlon 64.
+    /* slowest CPU to support extension */
+    CPUF_FORCE			  = 0x01,   // N/A
+    CPUF_FPU			    = 0x02,   // 386/486DX
+    CPUF_MMX			    = 0x04,   // P55C, K6, PII
+    CPUF_INTEGER_SSE	= 0x08,		// PIII, Athlon
+    CPUF_SSE			    = 0x10,		// PIII, Athlon XP/MP
+    CPUF_SSE2			    = 0x20,		// PIV, Hammer
+    CPUF_3DNOW			  = 0x40,   // K6-2
+    CPUF_3DNOW_EXT		= 0x80,		// Athlon
+    CPUF_X86_64       = 0xA0,   // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
+    //         will have anyway)
+    CPUF_SSE3		= 0x100,		    // Some P4 & Athlon 64.
 };
 #define MAX_INT 0x7fffffff
 #define MIN_INT -0x7fffffff
@@ -683,66 +684,67 @@ enum {
 
 class IScriptEnvironment {
 public:
-  virtual __stdcall ~IScriptEnvironment() {}
+    virtual __stdcall ~IScriptEnvironment() {}
 
-  virtual /*static*/ long __stdcall GetCPUFlags() = 0;
+    virtual /*static*/ long __stdcall GetCPUFlags() = 0;
 
-  virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
-  virtual char* __stdcall Sprintf(const char* fmt, ...) = 0;
-  // note: val is really a va_list; I hope everyone typedefs va_list to a pointer
-  virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0;
+    virtual char *__stdcall SaveString(const char *s, int length = -1) = 0;
+    virtual char *__stdcall Sprintf(const char *fmt, ...) = 0;
+    // note: val is really a va_list; I hope everyone typedefs va_list to a pointer
+    virtual char *__stdcall VSprintf(const char *fmt, void *val) = 0;
 
-  __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0;
+    __declspec(noreturn) virtual void __stdcall ThrowError(const char *fmt, ...) = 0;
 
-  class NotFound /*exception*/ {};  // thrown by Invoke and GetVar
+    class NotFound /*exception*/ {};  // thrown by Invoke and GetVar
 
-  typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env);
+    typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void *user_data, IScriptEnvironment *env);
 
-  virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0;
-  virtual bool __stdcall FunctionExists(const char* name) = 0;
-  virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0;
+    virtual void __stdcall AddFunction(const char *name, const char *params, ApplyFunc apply, void *user_data) = 0;
+    virtual bool __stdcall FunctionExists(const char *name) = 0;
+    virtual AVSValue __stdcall Invoke(const char *name, const AVSValue args, const char **arg_names = 0) = 0;
 
-  virtual AVSValue __stdcall GetVar(const char* name) = 0;
-  virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0;
-  virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0;
+    virtual AVSValue __stdcall GetVar(const char *name) = 0;
+    virtual bool __stdcall SetVar(const char *name, const AVSValue &val) = 0;
+    virtual bool __stdcall SetGlobalVar(const char *name, const AVSValue &val) = 0;
 
-  virtual void __stdcall PushContext(int level=0) = 0;
-  virtual void __stdcall PopContext() = 0;
+    virtual void __stdcall PushContext(int level = 0) = 0;
+    virtual void __stdcall PopContext() = 0;
 
-  // align should be 4 or 8
-  virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0;
+    // align should be 4 or 8
+    virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo &vi, int align = FRAME_ALIGN) = 0;
 
-  virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0;
+    virtual bool __stdcall MakeWritable(PVideoFrame *pvf) = 0;
 
-  virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0;
+    virtual /*static*/ void __stdcall BitBlt(BYTE *dstp, int dst_pitch, const BYTE *srcp, int src_pitch, int row_size, int height) = 0;
 
-  typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env);
-  virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0;
+    typedef void (__cdecl *ShutdownFunc)(void *user_data, IScriptEnvironment *env);
+    virtual void __stdcall AtExit(ShutdownFunc function, void *user_data) = 0;
 
-  virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
+    virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
 
-  virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
+    virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
 
-  virtual int __stdcall SetMemoryMax(int mem) = 0;
+    virtual int __stdcall SetMemoryMax(int mem) = 0;
 
-  virtual int __stdcall SetWorkingDir(const char * newdir) = 0;
+    virtual int __stdcall SetWorkingDir(const char *newdir) = 0;
 
-  virtual void* __stdcall ManageCache(int key, void* data) = 0;
+    virtual void *__stdcall ManageCache(int key, void *data) = 0;
 
-  enum PlanarChromaAlignmentMode {
-			PlanarChromaAlignmentOff,
-			PlanarChromaAlignmentOn,
-			PlanarChromaAlignmentTest };
+    enum PlanarChromaAlignmentMode {
+        PlanarChromaAlignmentOff,
+        PlanarChromaAlignmentOn,
+        PlanarChromaAlignmentTest
+    };
 
-  virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0;
+    virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0;
 
-  virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0;
+    virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0;
 };
 
 
 // avisynth.dll exports this; it's a way to use it as a library, without
 // writing an AVS script or without going through AVIFile.
-IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
+IScriptEnvironment *__stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
 
 
 #pragma pack(pop)
diff --git a/src/avisynth_wrap.cpp b/src/avisynth_wrap.cpp
index d76a377d2845a99396f21cbe22d3aee56ac2c901..4ce9c989f7558f2bb4e0f54f1056051e465d2660 100644
--- a/src/avisynth_wrap.cpp
+++ b/src/avisynth_wrap.cpp
@@ -42,54 +42,58 @@
 
 // Allocate storage for and initialise static members
 namespace {
-	int avs_refcount = 0;
-	HINSTANCE hLib = nullptr;
-	IScriptEnvironment *env = nullptr;
-	std::mutex AviSynthMutex;
+int avs_refcount = 0;
+HINSTANCE hLib = nullptr;
+IScriptEnvironment *env = nullptr;
+std::mutex AviSynthMutex;
 }
 
-typedef IScriptEnvironment* __stdcall FUNC(int);
+typedef IScriptEnvironment *__stdcall FUNC(int);
 
-AviSynthWrapper::AviSynthWrapper() {
-	if (!avs_refcount++) {
-		hLib = LoadLibrary(L"avisynth.dll");
+AviSynthWrapper::AviSynthWrapper()
+{
+    if (!avs_refcount++) {
+        hLib = LoadLibrary(L"avisynth.dll");
 
-		if (!hLib)
-			throw AvisynthError("Could not load avisynth.dll");
+        if (!hLib)
+            throw AvisynthError("Could not load avisynth.dll");
 
-		FUNC *CreateScriptEnv = (FUNC*)GetProcAddress(hLib, "CreateScriptEnvironment");
-		if (!CreateScriptEnv)
-			throw AvisynthError("Failed to get address of CreateScriptEnv from avisynth.dll");
+        FUNC *CreateScriptEnv = (FUNC *)GetProcAddress(hLib, "CreateScriptEnvironment");
+        if (!CreateScriptEnv)
+            throw AvisynthError("Failed to get address of CreateScriptEnv from avisynth.dll");
 
-		// Require Avisynth 2.5.6+?
-		if (OPT_GET("Provider/Avisynth/Allow Ancient")->GetBool())
-			env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION-1);
-		else
-			env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION);
+        // Require Avisynth 2.5.6+?
+        if (OPT_GET("Provider/Avisynth/Allow Ancient")->GetBool())
+            env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION - 1);
+        else
+            env = CreateScriptEnv(AVISYNTH_INTERFACE_VERSION);
 
-		if (!env)
-			throw AvisynthError("Failed to create a new avisynth script environment. Avisynth is too old?");
+        if (!env)
+            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)
-			env->SetMemoryMax(memoryMax);
-	}
+        // Set memory limit
+        const int memoryMax = OPT_GET("Provider/Avisynth/Memory Max")->GetInt();
+        if (memoryMax)
+            env->SetMemoryMax(memoryMax);
+    }
 }
 
-AviSynthWrapper::~AviSynthWrapper() {
-	if (!--avs_refcount) {
-		delete env;
-		FreeLibrary(hLib);
-	}
+AviSynthWrapper::~AviSynthWrapper()
+{
+    if (!--avs_refcount) {
+        delete env;
+        FreeLibrary(hLib);
+    }
 }
 
-std::mutex& AviSynthWrapper::GetMutex() const {
-	return AviSynthMutex;
+std::mutex &AviSynthWrapper::GetMutex() const
+{
+    return AviSynthMutex;
 }
 
-IScriptEnvironment *AviSynthWrapper::GetEnv() const {
-	return env;
+IScriptEnvironment *AviSynthWrapper::GetEnv() const
+{
+    return env;
 }
 
 #endif
diff --git a/src/avisynth_wrap.h b/src/avisynth_wrap.h
index ece6fac634bdd16b8e242fb5cf659777242069af..b5a3e83a26937db59a4c6071c478610ba7a77f4b 100644
--- a/src/avisynth_wrap.h
+++ b/src/avisynth_wrap.h
@@ -40,13 +40,13 @@ class IScriptEnvironment;
 namespace std { class mutex; }
 
 class AviSynthWrapper {
-	AviSynthWrapper(AviSynthWrapper const&);
+    AviSynthWrapper(AviSynthWrapper const &);
 public:
-	std::mutex& GetMutex() const;
-	IScriptEnvironment *GetEnv() const;
+    std::mutex &GetMutex() const;
+    IScriptEnvironment *GetEnv() const;
 
-	AviSynthWrapper();
-	~AviSynthWrapper();
+    AviSynthWrapper();
+    ~AviSynthWrapper();
 };
 
 #endif
diff --git a/src/base_grid.cpp b/src/base_grid.cpp
index 151b15be035038e7412a0e3df42c99b4e0efa486..f495c493a2ea1a3c7f0789158ee97e30fb19d942 100644
--- a/src/base_grid.cpp
+++ b/src/base_grid.cpp
@@ -56,668 +56,689 @@
 #include <wx/sizer.h>
 
 enum {
-	GRID_SCROLLBAR = 1730,
-	MENU_SHOW_COL = 1250 // Needs 15 IDs after this
+    GRID_SCROLLBAR = 1730,
+    MENU_SHOW_COL = 1250 // Needs 15 IDs after this
 };
 
-BaseGrid::BaseGrid(wxWindow* parent, agi::Context *context)
-: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER)
-, scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL))
-, context(context)
-, columns(GetGridColumns())
-, columns_visible(OPT_GET("Subtitle/Grid/Column")->GetListBool())
-, seek_listener(context->videoController->AddSeekListener(&BaseGrid::OnSeek, this))
+BaseGrid::BaseGrid(wxWindow *parent, agi::Context *context)
+    : wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER)
+    , scrollBar(new wxScrollBar(this, GRID_SCROLLBAR, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL))
+    , context(context)
+    , columns(GetGridColumns())
+    , columns_visible(OPT_GET("Subtitle/Grid/Column")->GetListBool())
+    , seek_listener(context->videoController->AddSeekListener(&BaseGrid::OnSeek, this))
 {
-	scrollBar->SetScrollbar(0,10,100,10);
-
-	auto scrollbarpositioner = new wxBoxSizer(wxHORIZONTAL);
-	scrollbarpositioner->AddStretchSpacer();
-	scrollbarpositioner->Add(scrollBar, 0, wxEXPAND, 0);
-
-	SetSizerAndFit(scrollbarpositioner);
-
-	SetBackgroundStyle(wxBG_STYLE_PAINT);
-
-	for (size_t i : agi::util::range(std::min(columns_visible.size(), columns.size()))) {
-		if (!columns_visible[i])
-			columns[i]->SetVisible(false);
-	}
-
-	UpdateStyle();
-	OnHighlightVisibleChange(*OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame"));
-
-	connections = agi::signal::make_vector({
-		context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this),
-
-		context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this),
-		context->selectionController->AddSelectionListener([&]{ Refresh(false); }),
-
-		OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Header", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Left Column", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Lines", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Selection", &BaseGrid::UpdateStyle, this),
-		OPT_SUB("Colour/Subtitle Grid/Standard", &BaseGrid::UpdateStyle, this),
-
-		OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this),
-		OPT_SUB("Subtitle/Grid/Hide Overrides", [&](agi::OptionValue const&) { Refresh(false); }),
-	});
-
-	Bind(wxEVT_CONTEXT_MENU, &BaseGrid::OnContextMenu, this);
+    scrollBar->SetScrollbar(0, 10, 100, 10);
+
+    auto scrollbarpositioner = new wxBoxSizer(wxHORIZONTAL);
+    scrollbarpositioner->AddStretchSpacer();
+    scrollbarpositioner->Add(scrollBar, 0, wxEXPAND, 0);
+
+    SetSizerAndFit(scrollbarpositioner);
+
+    SetBackgroundStyle(wxBG_STYLE_PAINT);
+
+    for (size_t i : agi::util::range(std::min(columns_visible.size(), columns.size()))) {
+        if (!columns_visible[i])
+            columns[i]->SetVisible(false);
+    }
+
+    UpdateStyle();
+    OnHighlightVisibleChange(*OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame"));
+
+    connections = agi::signal::make_vector({
+        context->ass->AddCommitListener(&BaseGrid::OnSubtitlesCommit, this),
+
+        context->selectionController->AddActiveLineListener(&BaseGrid::OnActiveLineChanged, this),
+        context->selectionController->AddSelectionListener([&]{ Refresh(false); }),
+
+        OPT_SUB("Subtitle/Grid/Font Face", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Subtitle/Grid/Font Size", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Active Border", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Background/Background", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Background/Comment", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Background/Inframe", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Background/Selected Comment", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Background/Selection", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Collision", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Header", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Left Column", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Lines", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Selection", &BaseGrid::UpdateStyle, this),
+        OPT_SUB("Colour/Subtitle Grid/Standard", &BaseGrid::UpdateStyle, this),
+
+        OPT_SUB("Subtitle/Grid/Highlight Subtitles in Frame", &BaseGrid::OnHighlightVisibleChange, this),
+        OPT_SUB("Subtitle/Grid/Hide Overrides", [&](agi::OptionValue const &) { Refresh(false); }),
+    });
+
+    Bind(wxEVT_CONTEXT_MENU, &BaseGrid::OnContextMenu, this);
 }
 
 BaseGrid::~BaseGrid() { }
 
-BEGIN_EVENT_TABLE(BaseGrid,wxWindow)
-	EVT_PAINT(BaseGrid::OnPaint)
-	EVT_SIZE(BaseGrid::OnSize)
-	EVT_COMMAND_SCROLL(GRID_SCROLLBAR,BaseGrid::OnScroll)
-	EVT_MOUSE_EVENTS(BaseGrid::OnMouseEvent)
-	EVT_KEY_DOWN(BaseGrid::OnKeyDown)
-	EVT_CHAR_HOOK(BaseGrid::OnCharHook)
-	EVT_MENU_RANGE(MENU_SHOW_COL,MENU_SHOW_COL+15,BaseGrid::OnShowColMenu)
+BEGIN_EVENT_TABLE(BaseGrid, wxWindow)
+    EVT_PAINT(BaseGrid::OnPaint)
+    EVT_SIZE(BaseGrid::OnSize)
+    EVT_COMMAND_SCROLL(GRID_SCROLLBAR, BaseGrid::OnScroll)
+    EVT_MOUSE_EVENTS(BaseGrid::OnMouseEvent)
+    EVT_KEY_DOWN(BaseGrid::OnKeyDown)
+    EVT_CHAR_HOOK(BaseGrid::OnCharHook)
+    EVT_MENU_RANGE(MENU_SHOW_COL, MENU_SHOW_COL + 15, BaseGrid::OnShowColMenu)
 END_EVENT_TABLE()
 
-void BaseGrid::OnSubtitlesCommit(int type) {
-	if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_ORDER || type & AssFile::COMMIT_DIAG_ADDREM)
-		UpdateMaps();
-
-	if (type & AssFile::COMMIT_DIAG_META) {
-		SetColumnWidths();
-		Refresh(false);
-		return;
-	}
-	if (type & AssFile::COMMIT_DIAG_TIME)
-		Refresh(false);
-	else if (type & AssFile::COMMIT_DIAG_TEXT) {
-		for (auto const& rect : text_refresh_rects)
-			RefreshRect(rect, false);
-	}
+void BaseGrid::OnSubtitlesCommit(int type)
+{
+    if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_ORDER || type & AssFile::COMMIT_DIAG_ADDREM)
+        UpdateMaps();
+
+    if (type & AssFile::COMMIT_DIAG_META) {
+        SetColumnWidths();
+        Refresh(false);
+        return;
+    }
+    if (type & AssFile::COMMIT_DIAG_TIME)
+        Refresh(false);
+    else if (type & AssFile::COMMIT_DIAG_TEXT) {
+        for (auto const &rect : text_refresh_rects)
+            RefreshRect(rect, false);
+    }
 }
 
-void BaseGrid::OnShowColMenu(wxCommandEvent &event) {
-	int item = event.GetId() - MENU_SHOW_COL;
-	bool new_value = !columns_visible[item];
+void BaseGrid::OnShowColMenu(wxCommandEvent &event)
+{
+    int item = event.GetId() - MENU_SHOW_COL;
+    bool new_value = !columns_visible[item];
 
-	columns_visible.resize(columns.size(), true);
-	columns_visible[item] = new_value;
-	OPT_SET("Subtitle/Grid/Column")->SetListBool(columns_visible);
-	columns[item]->SetVisible(new_value);
+    columns_visible.resize(columns.size(), true);
+    columns_visible[item] = new_value;
+    OPT_SET("Subtitle/Grid/Column")->SetListBool(columns_visible);
+    columns[item]->SetVisible(new_value);
 
-	SetColumnWidths();
+    SetColumnWidths();
 
-	Refresh(false);
+    Refresh(false);
 }
 
-void BaseGrid::OnHighlightVisibleChange(agi::OptionValue const& opt) {
-	if (opt.GetBool())
-		seek_listener.Unblock();
-	else
-		seek_listener.Block();
+void BaseGrid::OnHighlightVisibleChange(agi::OptionValue const &opt)
+{
+    if (opt.GetBool())
+        seek_listener.Unblock();
+    else
+        seek_listener.Block();
 }
 
-void BaseGrid::UpdateStyle() {
-	wxString fontname = FontFace("Subtitle/Grid");
-	if (fontname.empty()) fontname = "Tahoma";
-	font.SetFaceName(fontname);
-	font.SetPointSize(OPT_GET("Subtitle/Grid/Font Size")->GetInt());
-	font.SetWeight(wxFONTWEIGHT_NORMAL);
-
-	wxClientDC dc(this);
-	dc.SetFont(font);
-
-	// Set line height
-	lineHeight = dc.GetCharHeight() + 4;
-
-	// Set row brushes
-	row_colors.Default.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Background")->GetColor()));
-	row_colors.Header.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Header")->GetColor()));
-	row_colors.Selection.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selection")->GetColor()));
-	row_colors.Comment.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Comment")->GetColor()));
-	row_colors.Visible.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Inframe")->GetColor()));
-	row_colors.SelectedComment.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selected Comment")->GetColor()));
-	row_colors.LeftCol.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Left Column")->GetColor()));
-
-	SetColumnWidths();
-
-	AdjustScrollbar();
-	Refresh(false);
+void BaseGrid::UpdateStyle()
+{
+    wxString fontname = FontFace("Subtitle/Grid");
+    if (fontname.empty()) fontname = "Tahoma";
+    font.SetFaceName(fontname);
+    font.SetPointSize(OPT_GET("Subtitle/Grid/Font Size")->GetInt());
+    font.SetWeight(wxFONTWEIGHT_NORMAL);
+
+    wxClientDC dc(this);
+    dc.SetFont(font);
+
+    // Set line height
+    lineHeight = dc.GetCharHeight() + 4;
+
+    // Set row brushes
+    row_colors.Default.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Background")->GetColor()));
+    row_colors.Header.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Header")->GetColor()));
+    row_colors.Selection.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selection")->GetColor()));
+    row_colors.Comment.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Comment")->GetColor()));
+    row_colors.Visible.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Inframe")->GetColor()));
+    row_colors.SelectedComment.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selected Comment")->GetColor()));
+    row_colors.LeftCol.SetColour(to_wx(OPT_GET("Colour/Subtitle Grid/Left Column")->GetColor()));
+
+    SetColumnWidths();
+
+    AdjustScrollbar();
+    Refresh(false);
 }
 
-void BaseGrid::UpdateMaps() {
-	index_line_map.clear();
+void BaseGrid::UpdateMaps()
+{
+    index_line_map.clear();
 
-	for (auto& curdiag : context->ass->Events)
-		index_line_map.push_back(&curdiag);
+    for (auto &curdiag : context->ass->Events)
+        index_line_map.push_back(&curdiag);
 
-	SetColumnWidths();
-	AdjustScrollbar();
-	Refresh(false);
+    SetColumnWidths();
+    AdjustScrollbar();
+    Refresh(false);
 }
 
-void BaseGrid::OnActiveLineChanged(AssDialogue *new_active) {
-	if (new_active) {
-		if (new_active->Row != active_row)
-			MakeRowVisible(new_active->Row);
-		extendRow = active_row = new_active->Row;
-		Refresh(false);
-	}
-	else
-		active_row = -1;
+void BaseGrid::OnActiveLineChanged(AssDialogue *new_active)
+{
+    if (new_active) {
+        if (new_active->Row != active_row)
+            MakeRowVisible(new_active->Row);
+        extendRow = active_row = new_active->Row;
+        Refresh(false);
+    }
+    else
+        active_row = -1;
 }
 
-void BaseGrid::MakeRowVisible(int row) {
-	int h = GetClientSize().GetHeight();
+void BaseGrid::MakeRowVisible(int row)
+{
+    int h = GetClientSize().GetHeight();
 
-	if (row < yPos + 1)
-		ScrollTo(row - 1);
-	else if (row > yPos + h/lineHeight - 3)
-		ScrollTo(row - h/lineHeight + 3);
+    if (row < yPos + 1)
+        ScrollTo(row - 1);
+    else if (row > yPos + h / lineHeight - 3)
+        ScrollTo(row - h / lineHeight + 3);
 }
 
-void BaseGrid::SelectRow(int row, bool addToSelected, bool select) {
-	if (row < 0 || (size_t)row >= index_line_map.size()) return;
-
-	AssDialogue *line = index_line_map[row];
-
-	if (!addToSelected) {
-		context->selectionController->SetSelectedSet(Selection{line});
-		return;
-	}
-
-	bool selected = !!context->selectionController->GetSelectedSet().count(line);
-	if (select != selected) {
-		auto selection = context->selectionController->GetSelectedSet();
-		if (select)
-			selection.insert(line);
-		else
-			selection.erase(line);
-		context->selectionController->SetSelectedSet(std::move(selection));
-	}
+void BaseGrid::SelectRow(int row, bool addToSelected, bool select)
+{
+    if (row < 0 || (size_t)row >= index_line_map.size()) return;
+
+    AssDialogue *line = index_line_map[row];
+
+    if (!addToSelected) {
+        context->selectionController->SetSelectedSet(Selection{line});
+        return;
+    }
+
+    bool selected = !!context->selectionController->GetSelectedSet().count(line);
+    if (select != selected) {
+        auto selection = context->selectionController->GetSelectedSet();
+        if (select)
+            selection.insert(line);
+        else
+            selection.erase(line);
+        context->selectionController->SetSelectedSet(std::move(selection));
+    }
 }
 
-void BaseGrid::OnSeek() {
-	int lines = GetClientSize().GetHeight() / lineHeight + 1;
-	lines = mid(0, lines, GetRows() - yPos);
-
-	auto it = begin(visible_rows);
-	for (int i : boost::irange(yPos, yPos + lines)) {
-		if (IsDisplayed(index_line_map[i])) {
-			if (it == end(visible_rows) || *it != i) {
-				Refresh(false);
-				return;
-			}
-			++it;
-		}
-	}
-	if (it != end(visible_rows))
-		Refresh(false);
+void BaseGrid::OnSeek()
+{
+    int lines = GetClientSize().GetHeight() / lineHeight + 1;
+    lines = mid(0, lines, GetRows() - yPos);
+
+    auto it = begin(visible_rows);
+    for (int i : boost::irange(yPos, yPos + lines)) {
+        if (IsDisplayed(index_line_map[i])) {
+            if (it == end(visible_rows) || *it != i) {
+                Refresh(false);
+                return;
+            }
+            ++it;
+        }
+    }
+    if (it != end(visible_rows))
+        Refresh(false);
 }
 
-void BaseGrid::OnPaint(wxPaintEvent &) {
-	// Find which columns need to be repainted
-	std::vector<char> paint_columns;
-	paint_columns.resize(columns.size(), false);
-	bool any = false;
-	for (wxRegionIterator region(GetUpdateRegion()); region; ++region) {
-		wxRect updrect = region.GetRect();
-		int x = 0;
-		for (size_t i : agi::util::range(columns.size())) {
-			int width = columns[i]->Width();
-			if (width && updrect.x < x + width && updrect.x + updrect.width > x) {
-				paint_columns[i] = true;
-				any = true;
-			}
-			x += width;
-		}
-	}
-
-	if (!any) return;
-
-	int w = 0;
-	int h = 0;
-	GetClientSize(&w,&h);
-	w -= scrollBar->GetSize().GetWidth();
-
-	wxAutoBufferedPaintDC dc(this);
-	dc.SetFont(font);
-
-	dc.SetBackground(row_colors.Default);
-	dc.Clear();
-
-	// Draw labels
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.SetBrush(row_colors.LeftCol);
-	dc.DrawRectangle(0, lineHeight, columns[0]->Width(), h-lineHeight);
-
-	// Row colors
-	wxColour text_standard(to_wx(OPT_GET("Colour/Subtitle Grid/Standard")->GetColor()));
-	wxColour text_selection(to_wx(OPT_GET("Colour/Subtitle Grid/Selection")->GetColor()));
-	wxColour text_collision(to_wx(OPT_GET("Colour/Subtitle Grid/Collision")->GetColor()));
-
-	// First grid row
-	wxPen grid_pen(to_wx(OPT_GET("Colour/Subtitle Grid/Lines")->GetColor()));
-	dc.SetPen(grid_pen);
-	dc.DrawLine(0, 0, w, 0);
-	dc.SetPen(*wxTRANSPARENT_PEN);
-
-	auto paint_text = [&](wxString const& str, int x, int y, int col) {
-		int left = x + 4;
-		if (columns[col]->Centered()) {
-			wxSize ext = dc.GetTextExtent(str);
-			left += (columns[col]->Width() - 6 - ext.GetWidth()) / 2;
-		}
-
-		dc.DrawText(str, left, y + 2);
-	};
-
-	// Paint header
-	{
-		dc.SetTextForeground(text_standard);
-		dc.SetBrush(row_colors.Header);
-		dc.DrawRectangle(0, 0, w, lineHeight);
-
-		int x = 0;
-		for (size_t i : agi::util::range(columns.size())) {
-			if (paint_columns[i])
-				paint_text(columns[i]->Header(), x, 0, i);
-			x += columns[i]->Width();
-		}
-
-		dc.SetPen(grid_pen);
-		dc.DrawLine(0, lineHeight, w, lineHeight);
-	}
-
-	// Paint the rows
-	const int drawPerScreen = h/lineHeight + 1;
-	const int nDraw = mid(0, drawPerScreen, GetRows() - yPos);
-	const int grid_x = columns[0]->Width();
-
-	const auto active_line = context->selectionController->GetActiveLine();
-	auto const& selection = context->selectionController->GetSelectedSet();
-	visible_rows.clear();
-
-	for (int i : agi::util::range(nDraw)) {
-		wxBrush color = row_colors.Default;
-		AssDialogue *curDiag = index_line_map[i + yPos];
-
-		bool inSel = !!selection.count(curDiag);
-		if (inSel && curDiag->Comment)
-			color = row_colors.SelectedComment;
-		else if (inSel)
-			color = row_colors.Selection;
-		else if (curDiag->Comment)
-			color = row_colors.Comment;
-
-		if (OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame")->GetBool() && IsDisplayed(curDiag)) {
-			if (color == row_colors.Default)
-				color = row_colors.Visible;
-			visible_rows.push_back(i + yPos);
-		}
-		dc.SetBrush(color);
-
-		// Draw row background color
-		if (color != row_colors.Default) {
-			dc.SetPen(*wxTRANSPARENT_PEN);
-			dc.DrawRectangle(grid_x, (i + 1) * lineHeight + 1, w, lineHeight);
-		}
-
-		if (active_line != curDiag && curDiag->CollidesWith(active_line))
-			dc.SetTextForeground(text_collision);
-		else if (inSel)
-			dc.SetTextForeground(text_selection);
-		else
-			dc.SetTextForeground(text_standard);
-
-		// Draw text
-		int x = 0;
-		int y = (i + 1) * lineHeight;
-		for (size_t j : agi::util::range(columns.size())) {
-			if (paint_columns[j])
-				columns[j]->Paint(dc, x, y, curDiag, context);
-			x += columns[j]->Width();
-		}
-
-		// Draw grid
-		dc.SetPen(grid_pen);
-		dc.DrawLine(0, y + lineHeight, w , y + lineHeight);
-		dc.SetPen(*wxTRANSPARENT_PEN);
-	}
-
-	// Draw grid columns
-	{
-		int maxH = (nDraw + 1) * lineHeight;
-		int x = 0;
-		dc.SetPen(grid_pen);
-		for (auto const& column : columns) {
-			x += column->Width();
-			if (x < w)
-				dc.DrawLine(x, 0, x, maxH);
-		}
-		dc.DrawLine(0, 0, 0, maxH);
-		dc.DrawLine(w, 0, w, maxH);
-	}
-
-	if (active_line && active_line->Row >= yPos && active_line->Row < yPos + nDraw) {
-		dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor())));
-		dc.SetBrush(*wxTRANSPARENT_BRUSH);
-		dc.DrawRectangle(0, (active_line->Row - yPos + 1) * lineHeight, w, lineHeight + 1);
-	}
+void BaseGrid::OnPaint(wxPaintEvent &)
+{
+    // Find which columns need to be repainted
+    std::vector<char> paint_columns;
+    paint_columns.resize(columns.size(), false);
+    bool any = false;
+    for (wxRegionIterator region(GetUpdateRegion()); region; ++region) {
+        wxRect updrect = region.GetRect();
+        int x = 0;
+        for (size_t i : agi::util::range(columns.size())) {
+            int width = columns[i]->Width();
+            if (width && updrect.x < x + width && updrect.x + updrect.width > x) {
+                paint_columns[i] = true;
+                any = true;
+            }
+            x += width;
+        }
+    }
+
+    if (!any) return;
+
+    int w = 0;
+    int h = 0;
+    GetClientSize(&w, &h);
+    w -= scrollBar->GetSize().GetWidth();
+
+    wxAutoBufferedPaintDC dc(this);
+    dc.SetFont(font);
+
+    dc.SetBackground(row_colors.Default);
+    dc.Clear();
+
+    // Draw labels
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.SetBrush(row_colors.LeftCol);
+    dc.DrawRectangle(0, lineHeight, columns[0]->Width(), h - lineHeight);
+
+    // Row colors
+    wxColour text_standard(to_wx(OPT_GET("Colour/Subtitle Grid/Standard")->GetColor()));
+    wxColour text_selection(to_wx(OPT_GET("Colour/Subtitle Grid/Selection")->GetColor()));
+    wxColour text_collision(to_wx(OPT_GET("Colour/Subtitle Grid/Collision")->GetColor()));
+
+    // First grid row
+    wxPen grid_pen(to_wx(OPT_GET("Colour/Subtitle Grid/Lines")->GetColor()));
+    dc.SetPen(grid_pen);
+    dc.DrawLine(0, 0, w, 0);
+    dc.SetPen(*wxTRANSPARENT_PEN);
+
+    auto paint_text = [&](wxString const & str, int x, int y, int col) {
+        int left = x + 4;
+        if (columns[col]->Centered()) {
+            wxSize ext = dc.GetTextExtent(str);
+            left += (columns[col]->Width() - 6 - ext.GetWidth()) / 2;
+        }
+
+        dc.DrawText(str, left, y + 2);
+    };
+
+    // Paint header
+    {
+        dc.SetTextForeground(text_standard);
+        dc.SetBrush(row_colors.Header);
+        dc.DrawRectangle(0, 0, w, lineHeight);
+
+        int x = 0;
+        for (size_t i : agi::util::range(columns.size())) {
+            if (paint_columns[i])
+                paint_text(columns[i]->Header(), x, 0, i);
+            x += columns[i]->Width();
+        }
+
+        dc.SetPen(grid_pen);
+        dc.DrawLine(0, lineHeight, w, lineHeight);
+    }
+
+    // Paint the rows
+    const int drawPerScreen = h / lineHeight + 1;
+    const int nDraw = mid(0, drawPerScreen, GetRows() - yPos);
+    const int grid_x = columns[0]->Width();
+
+    const auto active_line = context->selectionController->GetActiveLine();
+    auto const &selection = context->selectionController->GetSelectedSet();
+    visible_rows.clear();
+
+    for (int i : agi::util::range(nDraw)) {
+        wxBrush color = row_colors.Default;
+        AssDialogue *curDiag = index_line_map[i + yPos];
+
+        bool inSel = !!selection.count(curDiag);
+        if (inSel && curDiag->Comment)
+            color = row_colors.SelectedComment;
+        else if (inSel)
+            color = row_colors.Selection;
+        else if (curDiag->Comment)
+            color = row_colors.Comment;
+
+        if (OPT_GET("Subtitle/Grid/Highlight Subtitles in Frame")->GetBool() && IsDisplayed(curDiag)) {
+            if (color == row_colors.Default)
+                color = row_colors.Visible;
+            visible_rows.push_back(i + yPos);
+        }
+        dc.SetBrush(color);
+
+        // Draw row background color
+        if (color != row_colors.Default) {
+            dc.SetPen(*wxTRANSPARENT_PEN);
+            dc.DrawRectangle(grid_x, (i + 1) * lineHeight + 1, w, lineHeight);
+        }
+
+        if (active_line != curDiag && curDiag->CollidesWith(active_line))
+            dc.SetTextForeground(text_collision);
+        else if (inSel)
+            dc.SetTextForeground(text_selection);
+        else
+            dc.SetTextForeground(text_standard);
+
+        // Draw text
+        int x = 0;
+        int y = (i + 1) * lineHeight;
+        for (size_t j : agi::util::range(columns.size())) {
+            if (paint_columns[j])
+                columns[j]->Paint(dc, x, y, curDiag, context);
+            x += columns[j]->Width();
+        }
+
+        // Draw grid
+        dc.SetPen(grid_pen);
+        dc.DrawLine(0, y + lineHeight, w, y + lineHeight);
+        dc.SetPen(*wxTRANSPARENT_PEN);
+    }
+
+    // Draw grid columns
+    {
+        int maxH = (nDraw + 1) * lineHeight;
+        int x = 0;
+        dc.SetPen(grid_pen);
+        for (auto const &column : columns) {
+            x += column->Width();
+            if (x < w)
+                dc.DrawLine(x, 0, x, maxH);
+        }
+        dc.DrawLine(0, 0, 0, maxH);
+        dc.DrawLine(w, 0, w, maxH);
+    }
+
+    if (active_line && active_line->Row >= yPos && active_line->Row < yPos + nDraw) {
+        dc.SetPen(wxPen(to_wx(OPT_GET("Colour/Subtitle Grid/Active Border")->GetColor())));
+        dc.SetBrush(*wxTRANSPARENT_BRUSH);
+        dc.DrawRectangle(0, (active_line->Row - yPos + 1) * lineHeight, w, lineHeight + 1);
+    }
 }
 
-void BaseGrid::OnSize(wxSizeEvent &) {
-	AdjustScrollbar();
-	Refresh(false);
+void BaseGrid::OnSize(wxSizeEvent &)
+{
+    AdjustScrollbar();
+    Refresh(false);
 }
 
-void BaseGrid::OnScroll(wxScrollEvent &event) {
-	int newPos = event.GetPosition();
-	if (yPos != newPos) {
-		context->ass->Properties.scroll_position = yPos = newPos;
-		Refresh(false);
-	}
+void BaseGrid::OnScroll(wxScrollEvent &event)
+{
+    int newPos = event.GetPosition();
+    if (yPos != newPos) {
+        context->ass->Properties.scroll_position = yPos = newPos;
+        Refresh(false);
+    }
 }
 
-void BaseGrid::OnMouseEvent(wxMouseEvent &event) {
-	int h = GetClientSize().GetHeight();
-	bool shift = event.ShiftDown();
-	bool alt = event.AltDown();
-	bool ctrl = event.CmdDown();
-
-	// Row that mouse is over
-	bool click = event.LeftDown();
-	bool dclick = event.LeftDClick();
-	int row = event.GetY() / lineHeight + yPos - 1;
-	if (holding && !click)
-		row = mid(0, row, GetRows()-1);
-	AssDialogue *dlg = GetDialogue(row);
-	if (!dlg) row = 0;
-
-	if (event.ButtonDown() && OPT_GET("Subtitle/Grid/Focus Allow")->GetBool())
-		SetFocus();
-
-	if (holding) {
-		if (!event.LeftIsDown()) {
-			if (dlg)
-				MakeRowVisible(row);
-			holding = false;
-			ReleaseMouse();
-		}
-		else {
-			// Only scroll if the mouse has moved to a different row to avoid
-			// scrolling on sloppy clicks
-			if (row != extendRow) {
-				if (row <= yPos)
-					ScrollTo(yPos - 3);
-				// When dragging down we give a 3 row margin to make it easier
-				// to see what's going on, but we don't want to scroll down if
-				// the user clicks on the bottom row and drags up
-				else if (row > yPos + h / lineHeight - (row > extendRow ? 3 : 1))
-					ScrollTo(yPos + 3);
-			}
-		}
-	}
-	else if (click && dlg) {
-		holding = true;
-		CaptureMouse();
-	}
-
-	if ((click || holding || dclick) && dlg) {
-		int old_extend = extendRow;
-
-		// SetActiveLine will scroll the grid if the row is only half-visible,
-		// but we don't want to scroll until the mouse moves or the button is
-		// released, to avoid selecting multiple lines on a click
-		int old_y_pos = yPos;
-		context->selectionController->SetActiveLine(dlg);
-		ScrollTo(old_y_pos);
-		extendRow = row;
-
-		auto const& selection = context->selectionController->GetSelectedSet();
-
-		// Toggle selected
-		if (click && ctrl && !shift && !alt) {
-			bool isSel = !!selection.count(dlg);
-			if (isSel && selection.size() == 1) return;
-			SelectRow(row, true, !isSel);
-			return;
-		}
-
-		// Normal click
-		if ((click || dclick) && !shift && !ctrl && !alt) {
-			if (dclick) {
-				context->audioBox->ScrollToActiveLine();
-				context->videoController->JumpToTime(dlg->Start);
-			}
-			SelectRow(row, false);
-			return;
-		}
-
-		// Change active line only
-		if (click && !shift && !ctrl && alt)
-			return;
-
-		// Block select
-		if ((click && shift && !alt) || holding) {
-			extendRow = old_extend;
-			int i1 = row;
-			int i2 = extendRow;
-
-			if (i1 > i2)
-				std::swap(i1, i2);
-
-			// Toggle each
-			Selection newsel;
-			if (ctrl) newsel = selection;
-			for (int i = i1; i <= i2; i++)
-				newsel.insert(GetDialogue(i));
-			context->selectionController->SetSelectedSet(std::move(newsel));
-			return;
-		}
-
-		return;
-	}
-
-	// Mouse wheel
-	if (event.GetWheelRotation() != 0) {
-		if (ForwardMouseWheelEvent(this, event)) {
-			int step = shift ? h / lineHeight - 2 : 3;
-			ScrollTo(yPos - step * event.GetWheelRotation() / event.GetWheelDelta());
-		}
-		return;
-	}
-
-	event.Skip();
+void BaseGrid::OnMouseEvent(wxMouseEvent &event)
+{
+    int h = GetClientSize().GetHeight();
+    bool shift = event.ShiftDown();
+    bool alt = event.AltDown();
+    bool ctrl = event.CmdDown();
+
+    // Row that mouse is over
+    bool click = event.LeftDown();
+    bool dclick = event.LeftDClick();
+    int row = event.GetY() / lineHeight + yPos - 1;
+    if (holding && !click)
+        row = mid(0, row, GetRows() - 1);
+    AssDialogue *dlg = GetDialogue(row);
+    if (!dlg) row = 0;
+
+    if (event.ButtonDown() && OPT_GET("Subtitle/Grid/Focus Allow")->GetBool())
+        SetFocus();
+
+    if (holding) {
+        if (!event.LeftIsDown()) {
+            if (dlg)
+                MakeRowVisible(row);
+            holding = false;
+            ReleaseMouse();
+        }
+        else {
+            // Only scroll if the mouse has moved to a different row to avoid
+            // scrolling on sloppy clicks
+            if (row != extendRow) {
+                if (row <= yPos)
+                    ScrollTo(yPos - 3);
+                // When dragging down we give a 3 row margin to make it easier
+                // to see what's going on, but we don't want to scroll down if
+                // the user clicks on the bottom row and drags up
+                else if (row > yPos + h / lineHeight - (row > extendRow ? 3 : 1))
+                    ScrollTo(yPos + 3);
+            }
+        }
+    }
+    else if (click && dlg) {
+        holding = true;
+        CaptureMouse();
+    }
+
+    if ((click || holding || dclick) && dlg) {
+        int old_extend = extendRow;
+
+        // SetActiveLine will scroll the grid if the row is only half-visible,
+        // but we don't want to scroll until the mouse moves or the button is
+        // released, to avoid selecting multiple lines on a click
+        int old_y_pos = yPos;
+        context->selectionController->SetActiveLine(dlg);
+        ScrollTo(old_y_pos);
+        extendRow = row;
+
+        auto const &selection = context->selectionController->GetSelectedSet();
+
+        // Toggle selected
+        if (click && ctrl && !shift && !alt) {
+            bool isSel = !!selection.count(dlg);
+            if (isSel && selection.size() == 1) return;
+            SelectRow(row, true, !isSel);
+            return;
+        }
+
+        // Normal click
+        if ((click || dclick) && !shift && !ctrl && !alt) {
+            if (dclick) {
+                context->audioBox->ScrollToActiveLine();
+                context->videoController->JumpToTime(dlg->Start);
+            }
+            SelectRow(row, false);
+            return;
+        }
+
+        // Change active line only
+        if (click && !shift && !ctrl && alt)
+            return;
+
+        // Block select
+        if ((click && shift && !alt) || holding) {
+            extendRow = old_extend;
+            int i1 = row;
+            int i2 = extendRow;
+
+            if (i1 > i2)
+                std::swap(i1, i2);
+
+            // Toggle each
+            Selection newsel;
+            if (ctrl) newsel = selection;
+            for (int i = i1; i <= i2; i++)
+                newsel.insert(GetDialogue(i));
+            context->selectionController->SetSelectedSet(std::move(newsel));
+            return;
+        }
+
+        return;
+    }
+
+    // Mouse wheel
+    if (event.GetWheelRotation() != 0) {
+        if (ForwardMouseWheelEvent(this, event)) {
+            int step = shift ? h / lineHeight - 2 : 3;
+            ScrollTo(yPos - step * event.GetWheelRotation() / event.GetWheelDelta());
+        }
+        return;
+    }
+
+    event.Skip();
 }
 
-void BaseGrid::OnContextMenu(wxContextMenuEvent &evt) {
-	wxPoint pos = evt.GetPosition();
-	if (pos == wxDefaultPosition || ScreenToClient(pos).y > lineHeight) {
-		if (!context_menu) context_menu = menu::GetMenu("grid_context", context);
-		menu::OpenPopupMenu(context_menu.get(), this);
-	}
-	else {
-		wxMenu menu;
-		for (size_t i : agi::util::range(columns.size())) {
-			if (columns[i]->CanHide())
-				menu.Append(MENU_SHOW_COL + i, columns[i]->Description(), "", wxITEM_CHECK)->Check(columns[i]->Visible());
-		}
-		PopupMenu(&menu);
-	}
+void BaseGrid::OnContextMenu(wxContextMenuEvent &evt)
+{
+    wxPoint pos = evt.GetPosition();
+    if (pos == wxDefaultPosition || ScreenToClient(pos).y > lineHeight) {
+        if (!context_menu) context_menu = menu::GetMenu("grid_context", context);
+        menu::OpenPopupMenu(context_menu.get(), this);
+    }
+    else {
+        wxMenu menu;
+        for (size_t i : agi::util::range(columns.size())) {
+            if (columns[i]->CanHide())
+                menu.Append(MENU_SHOW_COL + i, columns[i]->Description(), "", wxITEM_CHECK)->Check(columns[i]->Visible());
+        }
+        PopupMenu(&menu);
+    }
 }
 
-void BaseGrid::ScrollTo(int y) {
-	int nextY = mid(0, y, GetRows() - 1);
-	if (yPos != nextY) {
-		context->ass->Properties.scroll_position = yPos = nextY;
-		scrollBar->SetThumbPosition(yPos);
-		Refresh(false);
-	}
+void BaseGrid::ScrollTo(int y)
+{
+    int nextY = mid(0, y, GetRows() - 1);
+    if (yPos != nextY) {
+        context->ass->Properties.scroll_position = yPos = nextY;
+        scrollBar->SetThumbPosition(yPos);
+        Refresh(false);
+    }
 }
 
-void BaseGrid::AdjustScrollbar() {
-	wxSize clientSize = GetClientSize();
-	wxSize scrollbarSize = scrollBar->GetSize();
+void BaseGrid::AdjustScrollbar()
+{
+    wxSize clientSize = GetClientSize();
+    wxSize scrollbarSize = scrollBar->GetSize();
 
-	scrollBar->Freeze();
-	scrollBar->SetSize(clientSize.GetWidth() - scrollbarSize.GetWidth(), 0, scrollbarSize.GetWidth(), clientSize.GetHeight());
+    scrollBar->Freeze();
+    scrollBar->SetSize(clientSize.GetWidth() - scrollbarSize.GetWidth(), 0, scrollbarSize.GetWidth(), clientSize.GetHeight());
 
-	if (GetRows() <= 1) {
-		yPos = 0;
-		scrollBar->Enable(false);
-		scrollBar->Thaw();
-		return;
-	}
+    if (GetRows() <= 1) {
+        yPos = 0;
+        scrollBar->Enable(false);
+        scrollBar->Thaw();
+        return;
+    }
 
-	if (!scrollBar->IsEnabled())
-		scrollBar->Enable(true);
+    if (!scrollBar->IsEnabled())
+        scrollBar->Enable(true);
 
-	int drawPerScreen = clientSize.GetHeight() / lineHeight;
-	int rows = GetRows();
+    int drawPerScreen = clientSize.GetHeight() / lineHeight;
+    int rows = GetRows();
 
-	context->ass->Properties.scroll_position = yPos = mid(0, yPos, rows - 1);
+    context->ass->Properties.scroll_position = yPos = mid(0, yPos, rows - 1);
 
-	scrollBar->SetScrollbar(yPos, drawPerScreen, rows + drawPerScreen - 1, drawPerScreen - 2, true);
-	scrollBar->Thaw();
+    scrollBar->SetScrollbar(yPos, drawPerScreen, rows + drawPerScreen - 1, drawPerScreen - 2, true);
+    scrollBar->Thaw();
 }
 
-void BaseGrid::SetColumnWidths() {
-	int w, h;
-	GetClientSize(&w, &h);
-
-	// DC for text extents test
-	wxClientDC dc(this);
-	dc.SetFont(font);
-
-	text_refresh_rects.clear();
-	int x = 0;
-
-	if (!width_helper)
-		width_helper = agi::make_unique<WidthHelper>();
-	width_helper->SetDC(&dc);
-
-	for (auto const& column : columns) {
-		column->UpdateWidth(context, *width_helper);
-		if (column->Width() && column->RefreshOnTextChange())
-			text_refresh_rects.emplace_back(x, 0, column->Width(), h);
-		x += column->Width();
-	}
-	width_helper->Age();
+void BaseGrid::SetColumnWidths()
+{
+    int w, h;
+    GetClientSize(&w, &h);
+
+    // DC for text extents test
+    wxClientDC dc(this);
+    dc.SetFont(font);
+
+    text_refresh_rects.clear();
+    int x = 0;
+
+    if (!width_helper)
+        width_helper = agi::make_unique<WidthHelper>();
+    width_helper->SetDC(&dc);
+
+    for (auto const &column : columns) {
+        column->UpdateWidth(context, *width_helper);
+        if (column->Width() && column->RefreshOnTextChange())
+            text_refresh_rects.emplace_back(x, 0, column->Width(), h);
+        x += column->Width();
+    }
+    width_helper->Age();
 }
 
-AssDialogue *BaseGrid::GetDialogue(int n) const {
-	if (static_cast<size_t>(n) >= index_line_map.size()) return nullptr;
-	return index_line_map[n];
+AssDialogue *BaseGrid::GetDialogue(int n) const
+{
+    if (static_cast<size_t>(n) >= index_line_map.size()) return nullptr;
+    return index_line_map[n];
 }
 
-bool BaseGrid::IsDisplayed(const AssDialogue *line) const {
-	if (!context->project->VideoProvider()) return false;
-	int frame = context->videoController->GetFrameN();
-	return context->project->Timecodes().FrameAtTime(line->Start, agi::vfr::START) <= frame
-		&& context->project->Timecodes().FrameAtTime(line->End, agi::vfr::END) >= frame;
+bool BaseGrid::IsDisplayed(const AssDialogue *line) const
+{
+    if (!context->project->VideoProvider()) return false;
+    int frame = context->videoController->GetFrameN();
+    return context->project->Timecodes().FrameAtTime(line->Start, agi::vfr::START) <= frame
+           && context->project->Timecodes().FrameAtTime(line->End, agi::vfr::END) >= frame;
 }
 
-void BaseGrid::OnCharHook(wxKeyEvent &event) {
-	if (hotkey::check("Subtitle Grid", context, event))
-		return;
+void BaseGrid::OnCharHook(wxKeyEvent &event)
+{
+    if (hotkey::check("Subtitle Grid", context, event))
+        return;
 
-	int key = event.GetKeyCode();
+    int key = event.GetKeyCode();
 
-	if (key == WXK_UP || key == WXK_DOWN ||
-		key == WXK_PAGEUP || key == WXK_PAGEDOWN ||
-		key == WXK_HOME || key == WXK_END)
-	{
-		event.Skip();
-		return;
-	}
+    if (key == WXK_UP || key == WXK_DOWN ||
+        key == WXK_PAGEUP || key == WXK_PAGEDOWN ||
+        key == WXK_HOME || key == WXK_END) {
+        event.Skip();
+        return;
+    }
 
-	hotkey::check("Audio", context, event);
+    hotkey::check("Audio", context, event);
 }
 
-void BaseGrid::OnKeyDown(wxKeyEvent &event) {
-	int w,h;
-	GetClientSize(&w, &h);
-
-	int key = event.GetKeyCode();
-	bool ctrl = event.CmdDown();
-	bool alt = event.AltDown();
-	bool shift = event.ShiftDown();
-
-	int dir = 0;
-	int step = 1;
-	if (key == WXK_UP) dir = -1;
-	else if (key == WXK_DOWN) dir = 1;
-	else if (key == WXK_PAGEUP) {
-		dir = -1;
-		step = h / lineHeight - 2;
-	}
-	else if (key == WXK_PAGEDOWN) {
-		dir = 1;
-		step = h / lineHeight - 2;
-	}
-	else if (key == WXK_HOME) {
-		dir = -1;
-		step = GetRows();
-	}
-	else if (key == WXK_END) {
-		dir = 1;
-		step = GetRows();
-	}
-
-	if (!dir) {
-		event.Skip();
-		return;
-	}
-
-	auto active_line = context->selectionController->GetActiveLine();
-	int old_extend = extendRow;
-	int next = mid(0, (active_line ? active_line->Row : 0) + dir * step, GetRows() - 1);
-	context->selectionController->SetActiveLine(GetDialogue(next));
-
-	// Move selection
-	if (!ctrl && !shift && !alt) {
-		SelectRow(next);
-		return;
-	}
-
-	// Move active only
-	if (alt && !shift && !ctrl)
-		return;
-
-	// Shift-selection
-	if (shift && !ctrl && !alt) {
-		extendRow = old_extend;
-		// Set range
-		int begin = next;
-		int end = extendRow;
-		if (end < begin)
-			std::swap(begin, end);
-
-		// Select range
-		Selection newsel;
-		for (int i = begin; i <= end; i++)
-			newsel.insert(GetDialogue(i));
-
-		context->selectionController->SetSelectedSet(std::move(newsel));
-
-		MakeRowVisible(next);
-		return;
-	}
+void BaseGrid::OnKeyDown(wxKeyEvent &event)
+{
+    int w, h;
+    GetClientSize(&w, &h);
+
+    int key = event.GetKeyCode();
+    bool ctrl = event.CmdDown();
+    bool alt = event.AltDown();
+    bool shift = event.ShiftDown();
+
+    int dir = 0;
+    int step = 1;
+    if (key == WXK_UP) dir = -1;
+    else if (key == WXK_DOWN) dir = 1;
+    else if (key == WXK_PAGEUP) {
+        dir = -1;
+        step = h / lineHeight - 2;
+    }
+    else if (key == WXK_PAGEDOWN) {
+        dir = 1;
+        step = h / lineHeight - 2;
+    }
+    else if (key == WXK_HOME) {
+        dir = -1;
+        step = GetRows();
+    }
+    else if (key == WXK_END) {
+        dir = 1;
+        step = GetRows();
+    }
+
+    if (!dir) {
+        event.Skip();
+        return;
+    }
+
+    auto active_line = context->selectionController->GetActiveLine();
+    int old_extend = extendRow;
+    int next = mid(0, (active_line ? active_line->Row : 0) + dir * step, GetRows() - 1);
+    context->selectionController->SetActiveLine(GetDialogue(next));
+
+    // Move selection
+    if (!ctrl && !shift && !alt) {
+        SelectRow(next);
+        return;
+    }
+
+    // Move active only
+    if (alt && !shift && !ctrl)
+        return;
+
+    // Shift-selection
+    if (shift && !ctrl && !alt) {
+        extendRow = old_extend;
+        // Set range
+        int begin = next;
+        int end = extendRow;
+        if (end < begin)
+            std::swap(begin, end);
+
+        // Select range
+        Selection newsel;
+        for (int i = begin; i <= end; i++)
+            newsel.insert(GetDialogue(i));
+
+        context->selectionController->SetSelectedSet(std::move(newsel));
+
+        MakeRowVisible(next);
+        return;
+    }
 }
 
-void BaseGrid::SetByFrame(bool state) {
-	if (byFrame == state) return;
-	byFrame = state;
-	for (auto& column : columns)
-		column->SetByFrame(byFrame);
-	SetColumnWidths();
-	Refresh(false);
+void BaseGrid::SetByFrame(bool state)
+{
+    if (byFrame == state) return;
+    byFrame = state;
+    for (auto &column : columns)
+        column->SetByFrame(byFrame);
+    SetColumnWidths();
+    Refresh(false);
 }
diff --git a/src/base_grid.h b/src/base_grid.h
index 366189588c9dbbf0ea5f48b3bffaddd1a3199f57..a245837d245f7da2b60ac577bd1fe64a5d7edbf5 100644
--- a/src/base_grid.h
+++ b/src/base_grid.h
@@ -35,99 +35,99 @@
 #include <wx/window.h>
 
 namespace agi {
-	struct Context;
-	class OptionValue;
+struct Context;
+class OptionValue;
 }
 class AssDialogue;
 class GridColumn;
 class WidthHelper;
 
 class BaseGrid final : public wxWindow {
-	std::vector<agi::signal::Connection> connections;
-	int lineHeight = 1;     ///< Height of a line in pixels in the current font
-	bool holding = false;   ///< Is a drag selection in process?
-	wxFont font;            ///< Current grid font
-	wxScrollBar *scrollBar; ///< The grid's scrollbar
-	bool byFrame = false;   ///< Should times be displayed as frame numbers
+    std::vector<agi::signal::Connection> connections;
+    int lineHeight = 1;     ///< Height of a line in pixels in the current font
+    bool holding = false;   ///< Is a drag selection in process?
+    wxFont font;            ///< Current grid font
+    wxScrollBar *scrollBar; ///< The grid's scrollbar
+    bool byFrame = false;   ///< Should times be displayed as frame numbers
 
-	/// Row from which the selection shrinks/grows from when selecting via the
-	/// keyboard, shift-clicking or dragging
-	int extendRow = -1;
+    /// Row from which the selection shrinks/grows from when selecting via the
+    /// keyboard, shift-clicking or dragging
+    int extendRow = -1;
 
-	/// First row that is visible at the current scroll position
-	int yPos = 0;
+    /// First row that is visible at the current scroll position
+    int yPos = 0;
 
-	int active_row = -1;
+    int active_row = -1;
 
-	std::unique_ptr<WidthHelper> width_helper;
+    std::unique_ptr<WidthHelper> width_helper;
 
-	/// Rows which are visible on the current video frame
-	std::vector<int> visible_rows;
+    /// Rows which are visible on the current video frame
+    std::vector<int> visible_rows;
 
-	agi::Context *context; ///< Associated project context
+    agi::Context *context; ///< Associated project context
 
-	std::vector<std::unique_ptr<GridColumn>> columns;
-	std::vector<bool> columns_visible;
+    std::vector<std::unique_ptr<GridColumn>> columns;
+    std::vector<bool> columns_visible;
 
-	std::vector<wxRect> text_refresh_rects;
+    std::vector<wxRect> text_refresh_rects;
 
-	/// Cached brushes used for row backgrounds
-	struct {
-		wxBrush Default;
-		wxBrush Header;
-		wxBrush Selection;
-		wxBrush Comment;
-		wxBrush Visible;
-		wxBrush SelectedComment;
-		wxBrush LeftCol;
-	} row_colors;
+    /// Cached brushes used for row backgrounds
+    struct {
+        wxBrush Default;
+        wxBrush Header;
+        wxBrush Selection;
+        wxBrush Comment;
+        wxBrush Visible;
+        wxBrush SelectedComment;
+        wxBrush LeftCol;
+    } row_colors;
 
-	std::vector<AssDialogue*> index_line_map;  ///< Row number -> dialogue line
+    std::vector<AssDialogue *> index_line_map; ///< Row number -> dialogue line
 
-	/// Connection for video seek event. Stored explicitly so that it can be
-	/// blocked if the relevant option is disabled
-	agi::signal::Connection seek_listener;
+    /// Connection for video seek event. Stored explicitly so that it can be
+    /// blocked if the relevant option is disabled
+    agi::signal::Connection seek_listener;
 
-	/// Cached grid body context menu
-	std::unique_ptr<wxMenu> context_menu;
+    /// Cached grid body context menu
+    std::unique_ptr<wxMenu> context_menu;
 
-	void OnContextMenu(wxContextMenuEvent &evt);
-	void OnHighlightVisibleChange(agi::OptionValue const& opt);
-	void OnKeyDown(wxKeyEvent &event);
-	void OnCharHook(wxKeyEvent &event);
-	void OnMouseEvent(wxMouseEvent &event);
-	void OnPaint(wxPaintEvent &event);
-	void OnScroll(wxScrollEvent &event);
-	void OnShowColMenu(wxCommandEvent &event);
-	void OnSize(wxSizeEvent &event);
-	void OnSubtitlesCommit(int type);
-	void OnActiveLineChanged(AssDialogue *);
-	void OnSeek();
+    void OnContextMenu(wxContextMenuEvent &evt);
+    void OnHighlightVisibleChange(agi::OptionValue const &opt);
+    void OnKeyDown(wxKeyEvent &event);
+    void OnCharHook(wxKeyEvent &event);
+    void OnMouseEvent(wxMouseEvent &event);
+    void OnPaint(wxPaintEvent &event);
+    void OnScroll(wxScrollEvent &event);
+    void OnShowColMenu(wxCommandEvent &event);
+    void OnSize(wxSizeEvent &event);
+    void OnSubtitlesCommit(int type);
+    void OnActiveLineChanged(AssDialogue *);
+    void OnSeek();
 
-	void AdjustScrollbar();
-	void SetColumnWidths();
+    void AdjustScrollbar();
+    void SetColumnWidths();
 
-	bool IsDisplayed(const AssDialogue *line) const;
+    bool IsDisplayed(const AssDialogue *line) const;
 
-	void UpdateMaps();
-	void UpdateStyle();
+    void UpdateMaps();
+    void UpdateStyle();
 
-	void SelectRow(int row, bool addToSelected = false, bool select=true);
+    void SelectRow(int row, bool addToSelected = false, bool select = true);
 
-	int GetRows() const { return index_line_map.size(); }
-	void MakeRowVisible(int row);
+    int GetRows() const { return index_line_map.size(); }
+    void MakeRowVisible(int row);
 
-	/// @brief Get dialogue by index
-	/// @param n Index to look up
-	/// @return Subtitle dialogue line for index, or 0 if invalid index
-	AssDialogue *GetDialogue(int n) const;
+    /// @brief Get dialogue by index
+    /// @param n Index to look up
+    /// @return Subtitle dialogue line for index, or 0 if invalid index
+    AssDialogue *GetDialogue(int n) const;
 
 public:
-	BaseGrid(wxWindow* parent, agi::Context *context);
-	~BaseGrid();
+    BaseGrid(wxWindow *parent, agi::Context *context);
+    ~BaseGrid();
 
-	void SetByFrame(bool state);
-	void ScrollTo(int y);
+    void SetByFrame(bool state);
+    void ScrollTo(int y);
 
-	DECLARE_EVENT_TABLE()
+    DECLARE_EVENT_TABLE()
 };
diff --git a/src/block_cache.h b/src/block_cache.h
index f2ebfa261b3b702471d6bb059945065b50d80055..399bb3c6d5d3667f63b938b8c8ed8c4de09b198f 100644
--- a/src/block_cache.h
+++ b/src/block_cache.h
@@ -44,157 +44,148 @@
 /// @tparam BlockFactoryT      Type of block factory, see BasicDataBlockFactory class for detail on these
 template <typename BlockT, int MacroblockExponent, typename BlockFactoryT>
 class DataBlockCache {
-	/// Type of an array of blocks
-	typedef std::vector<typename BlockFactoryT::BlockType> BlockArray;
+    /// Type of an array of blocks
+    typedef std::vector<typename BlockFactoryT::BlockType> BlockArray;
 
-	struct MacroBlock {
-		/// This macroblock's position in the age list
-		/// Is valid iff blocks.size() > 0
-		typename std::list<MacroBlock*>::iterator position;
+    struct MacroBlock {
+        /// This macroblock's position in the age list
+        /// Is valid iff blocks.size() > 0
+        typename std::list<MacroBlock *>::iterator position;
 
-		/// The blocks contained in the macroblock
-		BlockArray blocks;
-	};
+        /// The blocks contained in the macroblock
+        BlockArray blocks;
+    };
 
-	/// Type of an array of macro blocks
-	typedef std::vector<MacroBlock> MacroBlockArray;
+    /// Type of an array of macro blocks
+    typedef std::vector<MacroBlock> MacroBlockArray;
 
-	/// The data in the cache
-	MacroBlockArray data;
+    /// The data in the cache
+    MacroBlockArray data;
 
-	/// Type of a list of macro blocks
-	typedef std::list<MacroBlock*> AgeList;
+    /// Type of a list of macro blocks
+    typedef std::list<MacroBlock *> AgeList;
 
-	/// The data sorted by how long it's been since they were used
-	AgeList age;
+    /// The data sorted by how long it's been since they were used
+    AgeList age;
 
-	/// Number of blocks per macroblock
-	size_t macroblock_size;
+    /// Number of blocks per macroblock
+    size_t macroblock_size;
 
-	/// Bitmask to extract the inside-macroblock index for a block by bitwise and
-	size_t macroblock_index_mask;
+    /// Bitmask to extract the inside-macroblock index for a block by bitwise and
+    size_t macroblock_index_mask;
 
-	/// Current size of the cache in bytes
-	size_t size = 0;
+    /// Current size of the cache in bytes
+    size_t size = 0;
 
-	/// Factory object for blocks
-	BlockFactoryT factory;
+    /// Factory object for blocks
+    BlockFactoryT factory;
 
-	/// @brief Dispose of all blocks in a macroblock and mark it empty
-	/// @param mb_index Index of macroblock to clear
-	void KillMacroBlock(MacroBlock &mb)
-	{
-		if (mb.blocks.empty())
-			return;
+    /// @brief Dispose of all blocks in a macroblock and mark it empty
+    /// @param mb_index Index of macroblock to clear
+    void KillMacroBlock(MacroBlock &mb) {
+        if (mb.blocks.empty())
+            return;
 
-		auto& ba = mb.blocks;
-		size -= (ba.size() - std::count(ba.begin(), ba.end(), nullptr)) * factory.GetBlockSize();
+        auto &ba = mb.blocks;
+        size -= (ba.size() - std::count(ba.begin(), ba.end(), nullptr)) * factory.GetBlockSize();
 
-		ba.clear();
-		age.erase(mb.position);
-	}
+        ba.clear();
+        age.erase(mb.position);
+    }
 
 public:
-	/// @brief Constructor
-	/// @param block_count Total number of blocks the cache will manage
-	/// @param factory     Factory object to use for producing blocks
-	///
-	/// Note that the block_count is the maximum block index the cache will ever see,
-	/// it is an error to request a block number greater than block_count.
-	///
-	/// The factory object passed must respond well to copying.
-	DataBlockCache(size_t block_count, BlockFactoryT factory = BlockFactoryT())
-	: factory(std::move(factory))
-	{
-		SetBlockCount(block_count);
-	}
-
-	DataBlockCache(DataBlockCache&&) = default;
-	DataBlockCache& operator=(DataBlockCache&&) = default;
-
-	/// @brief Change the number of blocks in cache
-	/// @param block_count New number of blocks to hold
-	///
-	/// This will completely de-allocate the cache and re-allocate it with the new block count.
-	void SetBlockCount(size_t block_count)
-	{
-		if (data.size() > 0)
-			Age(0);
-
-		macroblock_size = 1UL << MacroblockExponent;
-		macroblock_index_mask = macroblock_size - 1;
-
-		data.resize((block_count + macroblock_size - 1) >> MacroblockExponent);
-		size = 0;
-	}
-
-	/// @brief Clean up the cache
-	/// @param max_size Target maximum size of the cache in bytes
-	///
-	/// Passing a max_size of 0 (zero) causes the cache to be completely flushed
-	/// in a fast manner.
-	///
-	/// The max_size is not a hard limit, the cache size might somewhat exceed the max
-	/// after the aging operation, though it shouldn't be by much.
-	void Age(size_t max_size)
-	{
-		// Quick way out: get rid of everything
-		if (max_size == 0)
-		{
-			size_t block_count = data.size();
-			data.clear();
-			data.resize(block_count);
-			age.clear();
-			size = 0;
-			return;
-		}
-
-		// Remove old entries until we're under the max size
-		for (auto it = age.rbegin(); size > max_size && it != age.rend(); it++)
-			KillMacroBlock(**it);
-	}
-
-	/// @brief Obtain a data block from the cache
-	/// @param      i       Index of the block to retrieve
-	/// @param[out] created On return, tells whether the returned block was created during the operation
-	/// @return A pointer to the block in cache
-	///
-	/// It is legal to pass 0 (null) for created, in this case nothing is returned in it.
-	BlockT& Get(size_t i, bool *created = nullptr)
-	{
-		size_t mbi = i >> MacroblockExponent;
-		assert(mbi < data.size());
-
-		auto &mb = data[mbi];
-
-		// Move this macroblock to the front of the age list
-		if (mb.blocks.empty())
-		{
-			mb.blocks.resize(macroblock_size);
-			age.push_front(&mb);
-		}
-		else if (mb.position != begin(age))
-			age.splice(begin(age), age, mb.position);
-
-		mb.position = age.begin();
-
-		size_t block_index = i & macroblock_index_mask;
-		assert(block_index < mb.blocks.size());
-
-		BlockT *b = mb.blocks[block_index].get();
-
-		if (!b)
-		{
-			mb.blocks[block_index] = factory.ProduceBlock(i);
-			b = mb.blocks[block_index].get();
-			assert(b != nullptr);
-			size += factory.GetBlockSize();
-
-			if (created) *created = true;
-		}
-		else
-			if (created) *created = false;
-
-		return *b;
-	}
+    /// @brief Constructor
+    /// @param block_count Total number of blocks the cache will manage
+    /// @param factory     Factory object to use for producing blocks
+    ///
+    /// Note that the block_count is the maximum block index the cache will ever see,
+    /// it is an error to request a block number greater than block_count.
+    ///
+    /// The factory object passed must respond well to copying.
+    DataBlockCache(size_t block_count, BlockFactoryT factory = BlockFactoryT())
+        : factory(std::move(factory)) {
+        SetBlockCount(block_count);
+    }
+
+    DataBlockCache(DataBlockCache &&) = default;
+    DataBlockCache &operator=(DataBlockCache &&) = default;
+
+    /// @brief Change the number of blocks in cache
+    /// @param block_count New number of blocks to hold
+    ///
+    /// This will completely de-allocate the cache and re-allocate it with the new block count.
+    void SetBlockCount(size_t block_count) {
+        if (data.size() > 0)
+            Age(0);
+
+        macroblock_size = 1UL << MacroblockExponent;
+        macroblock_index_mask = macroblock_size - 1;
+
+        data.resize((block_count + macroblock_size - 1) >> MacroblockExponent);
+        size = 0;
+    }
+
+    /// @brief Clean up the cache
+    /// @param max_size Target maximum size of the cache in bytes
+    ///
+    /// Passing a max_size of 0 (zero) causes the cache to be completely flushed
+    /// in a fast manner.
+    ///
+    /// The max_size is not a hard limit, the cache size might somewhat exceed the max
+    /// after the aging operation, though it shouldn't be by much.
+    void Age(size_t max_size) {
+        // Quick way out: get rid of everything
+        if (max_size == 0) {
+            size_t block_count = data.size();
+            data.clear();
+            data.resize(block_count);
+            age.clear();
+            size = 0;
+            return;
+        }
+
+        // Remove old entries until we're under the max size
+        for (auto it = age.rbegin(); size > max_size && it != age.rend(); it++)
+            KillMacroBlock(**it);
+    }
+
+    /// @brief Obtain a data block from the cache
+    /// @param      i       Index of the block to retrieve
+    /// @param[out] created On return, tells whether the returned block was created during the operation
+    /// @return A pointer to the block in cache
+    ///
+    /// It is legal to pass 0 (null) for created, in this case nothing is returned in it.
+    BlockT &Get(size_t i, bool *created = nullptr) {
+        size_t mbi = i >> MacroblockExponent;
+        assert(mbi < data.size());
+
+        auto &mb = data[mbi];
+
+        // Move this macroblock to the front of the age list
+        if (mb.blocks.empty()) {
+            mb.blocks.resize(macroblock_size);
+            age.push_front(&mb);
+        }
+        else if (mb.position != begin(age))
+            age.splice(begin(age), age, mb.position);
+
+        mb.position = age.begin();
+
+        size_t block_index = i & macroblock_index_mask;
+        assert(block_index < mb.blocks.size());
+
+        BlockT *b = mb.blocks[block_index].get();
+
+        if (!b) {
+            mb.blocks[block_index] = factory.ProduceBlock(i);
+            b = mb.blocks[block_index].get();
+            assert(b != nullptr);
+            size += factory.GetBlockSize();
+
+            if (created) *created = true;
+        }
+        else if (created) *created = false;
+
+        return *b;
+    }
 };
diff --git a/src/charset_detect.cpp b/src/charset_detect.cpp
index 0ece3b6dd3296fdd213998ac0a1e1692cc655fed..a8e89d12f0bcefbdeb280d39f8d3f28db3b7a778 100644
--- a/src/charset_detect.cpp
+++ b/src/charset_detect.cpp
@@ -45,21 +45,22 @@
 
 namespace CharSetDetect {
 
-std::string GetEncoding(agi::fs::path const& filename) {
-	auto encoding = agi::charset::Detect(filename);
-	if (!encoding.empty()) {
-		if (!encoding.compare("ASCII") || !encoding.compare("UTF-8"))
-			encoding = "utf-8";
-		return encoding;
-	}
+std::string GetEncoding(agi::fs::path const &filename)
+{
+    auto encoding = agi::charset::Detect(filename);
+    if (!encoding.empty()) {
+        if (!encoding.compare("ASCII") || !encoding.compare("UTF-8"))
+            encoding = "utf-8";
+        return encoding;
+    }
 
-	auto choices = agi::charset::GetEncodingsList<wxArrayString>();
-	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 from_wx(choices[choice]);
+    auto choices = agi::charset::GetEncodingsList<wxArrayString>();
+    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 from_wx(choices[choice]);
 }
 
 }
diff --git a/src/charset_detect.h b/src/charset_detect.h
index ec528a598d2e8585687cec7fe383075bff74f3ea..2ad77f76f8bac7f6ef06ab6b1a9ac5727fdc45e4 100644
--- a/src/charset_detect.h
+++ b/src/charset_detect.h
@@ -36,8 +36,8 @@
 #include <string>
 
 namespace CharSetDetect {
-	/// @brief Get character set name.
-	/// @param filename File to check
-	/// @return Character set name
-	std::string GetEncoding(agi::fs::path const& filename);
+/// @brief Get character set name.
+/// @param filename File to check
+/// @return Character set name
+std::string GetEncoding(agi::fs::path const &filename);
 }
diff --git a/src/colorspace.cpp b/src/colorspace.cpp
index bd3dafd68078617e967d79029f821853f44735b2..a0722cbb3a61c9e14310694713a99c58af910dec 100644
--- a/src/colorspace.cpp
+++ b/src/colorspace.cpp
@@ -37,311 +37,323 @@
 
 static inline unsigned int clip_colorval(int val)
 {
-	return mid(0, val, 255);
+    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)
 {
-	if (S == 0) {
-		*R = L;
-		*G = L;
-		*B = L;
-		return;
-	}
-
-	if (L == 128 && S == 255) {
-		switch (H) {
-			case 0:
-			case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
-				*R = 255;
-				*G = 0;
-				*B = 0;
-				return;
-			case 43:
-				*R = 255;
-				*G = 255;
-				*B = 0;
-				return;
-			case 85:
-				*R = 0;
-				*G = 255;
-				*B = 0;
-				return;
-			case 128:
-				*R = 0;
-				*G = 255;
-				*B = 255;
-				return;
-			case 171:
-				*R = 0;
-				*G = 0;
-				*B = 255;
-				return;
-			case 213:
-				*R = 255;
-				*G = 0;
-				*B = 255;
-				return;
-		}
-	}
-
-	float h, s, l, r, g, b;
-	h = H / 255.f;
-	s = S / 255.f;
-	l = L / 255.f;
-
-	float temp2;
-	if (l < .5f) {
-		temp2 = l * (1.f + s);
-	} else {
-		temp2 = l + s - l*s;
-	}
-
-	float temp1 = 2.f * l - temp2;
-
-	// assume h is in range [0;1]
-	float temp3[3];
-	temp3[0] = h + 1.f/3.f;
-	if (temp3[0] > 1.f) temp3[0] -= 1.f;
-	temp3[1] = h;
-	temp3[2] = h - 1.f/3.f;
-	if (temp3[2] < 0.f) temp3[2] += 1.f;
-
-	if (6.f * temp3[0] < 1.f)
-		r = temp1 + (temp2 - temp1) * 6.f * temp3[0];
-	else if (2.f * temp3[0] < 1.f)
-		r = temp2;
-	else if (3.f * temp3[0] < 2.f)
-		r = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[0]) * 6.f;
-	else
-		r = temp1;
-
-	if (6.f * temp3[1] < 1.f)
-		g = temp1 + (temp2 - temp1) * 6.f * temp3[1];
-	else if (2.f * temp3[1] < 1.f)
-		g = temp2;
-	else if (3.f * temp3[1] < 2.f)
-		g = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[1]) * 6.f;
-	else
-		g = temp1;
-
-	if (6.f * temp3[2] < 1.f)
-		b = temp1 + (temp2 - temp1) * 6.f * temp3[2];
-	else if (2.f * temp3[2] < 1.f)
-		b = temp2;
-	else if (3.f * temp3[2] < 2.f)
-		b = temp1 + (temp2 - temp1) * ((2.f/3.f) - temp3[2]) * 6.f;
-	else
-		b = temp1;
-
-	*R = clip_colorval((int)(r*255));
-	*G = clip_colorval((int)(g*255));
-	*B = clip_colorval((int)(b*255));
+    if (S == 0) {
+        *R = L;
+        *G = L;
+        *B = L;
+        return;
+    }
+
+    if (L == 128 && S == 255) {
+        switch (H) {
+        case 0:
+        case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
+            *R = 255;
+            *G = 0;
+            *B = 0;
+            return;
+        case 43:
+            *R = 255;
+            *G = 255;
+            *B = 0;
+            return;
+        case 85:
+            *R = 0;
+            *G = 255;
+            *B = 0;
+            return;
+        case 128:
+            *R = 0;
+            *G = 255;
+            *B = 255;
+            return;
+        case 171:
+            *R = 0;
+            *G = 0;
+            *B = 255;
+            return;
+        case 213:
+            *R = 255;
+            *G = 0;
+            *B = 255;
+            return;
+        }
+    }
+
+    float h, s, l, r, g, b;
+    h = H / 255.f;
+    s = S / 255.f;
+    l = L / 255.f;
+
+    float temp2;
+    if (l < .5f) {
+        temp2 = l * (1.f + s);
+    }
+    else {
+        temp2 = l + s - l * s;
+    }
+
+    float temp1 = 2.f * l - temp2;
+
+    // assume h is in range [0;1]
+    float temp3[3];
+    temp3[0] = h + 1.f / 3.f;
+    if (temp3[0] > 1.f) temp3[0] -= 1.f;
+    temp3[1] = h;
+    temp3[2] = h - 1.f / 3.f;
+    if (temp3[2] < 0.f) temp3[2] += 1.f;
+
+    if (6.f * temp3[0] < 1.f)
+        r = temp1 + (temp2 - temp1) * 6.f * temp3[0];
+    else if (2.f * temp3[0] < 1.f)
+        r = temp2;
+    else if (3.f * temp3[0] < 2.f)
+        r = temp1 + (temp2 - temp1) * ((2.f / 3.f) - temp3[0]) * 6.f;
+    else
+        r = temp1;
+
+    if (6.f * temp3[1] < 1.f)
+        g = temp1 + (temp2 - temp1) * 6.f * temp3[1];
+    else if (2.f * temp3[1] < 1.f)
+        g = temp2;
+    else if (3.f * temp3[1] < 2.f)
+        g = temp1 + (temp2 - temp1) * ((2.f / 3.f) - temp3[1]) * 6.f;
+    else
+        g = temp1;
+
+    if (6.f * temp3[2] < 1.f)
+        b = temp1 + (temp2 - temp1) * 6.f * temp3[2];
+    else if (2.f * temp3[2] < 1.f)
+        b = temp2;
+    else if (3.f * temp3[2] < 2.f)
+        b = temp1 + (temp2 - temp1) * ((2.f / 3.f) - temp3[2]) * 6.f;
+    else
+        b = temp1;
+
+    *R = clip_colorval((int)(r * 255));
+    *G = clip_colorval((int)(g * 255));
+    *B = clip_colorval((int)(b * 255));
 }
 
 // Formulas taken from wikipedia: http://en.wikipedia.org/wiki/HSV_color_space
 // The range for H is 0..255 instead of 0..359, so 60 degrees has been translated to 256/6 here
 void hsv_to_rgb(int H, int S, int V, unsigned char *R, unsigned char *G, unsigned char *B)
 {
-	*R = *G = *B = 0;
-
-	// some special cases... oh yeah baby!
-	if (S == 255) {
-		switch (H) {
-			case 0:
-			case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
-				*R = V;
-				*G = 0;
-				*B = 0;
-				return;
-			case 43:
-				*R = V;
-				*G = V;
-				*B = 0;
-				return;
-			case 85:
-				*R = 0;
-				*G = V;
-				*B = 0;
-				return;
-			case 128:
-				*R = 0;
-				*G = V;
-				*B = V;
-				return;
-			case 171:
-				*R = 0;
-				*G = 0;
-				*B = V;
-				return;
-			case 213:
-				*R = V;
-				*G = 0;
-				*B = V;
-				return;
-		}
-	}
-
-	// Cap values
-	unsigned int h = H * 360;
-	unsigned int s = clip_colorval(S)*256;
-	unsigned int v = clip_colorval(V)*256;
-
-	// Saturation is zero, make grey
-	if (S == 0) {
-		*R = V;
-		*G = V;
-		*B = V;
-		return;
-	}
-
-	unsigned int r, g, b;
-
-	// Else, calculate color
-	unsigned int Hi = h / 60 / 256;
-	unsigned int f  = h / 60 - Hi * 256;
-	unsigned int p  = v * (65535 - s) / 65536;
-	unsigned int q  = v * (65535 - (f * s)/256) / 65536;
-	unsigned int t  = v * (65535 - ((255 - f) * s)/256) / 65536;
-	switch (Hi) {
-		case 0:
-			r = v;
-			g = t;
-			b = p;
-			break;
-		case 1:
-			r = q;
-			g = v;
-			b = p;
-			break;
-		case 2:
-			r = p;
-			g = v;
-			b = t;
-			break;
-		case 3:
-			r = p;
-			g = q;
-			b = v;
-			break;
-		case 4:
-			r = t;
-			g = p;
-			b = v;
-			break;
-		case 5:
-		default:
-			r = v;
-			g = p;
-			b = q;
-			break;
-	}
-
-	*R = clip_colorval(r/256);
-	*G = clip_colorval(g/256);
-	*B = clip_colorval(b/256);
+    *R = *G = *B = 0;
+
+    // some special cases... oh yeah baby!
+    if (S == 255) {
+        switch (H) {
+        case 0:
+        case 255: // actually this is wrong, since this is more like 359 degrees... but it's what you'd expect (sadly :)
+            *R = V;
+            *G = 0;
+            *B = 0;
+            return;
+        case 43:
+            *R = V;
+            *G = V;
+            *B = 0;
+            return;
+        case 85:
+            *R = 0;
+            *G = V;
+            *B = 0;
+            return;
+        case 128:
+            *R = 0;
+            *G = V;
+            *B = V;
+            return;
+        case 171:
+            *R = 0;
+            *G = 0;
+            *B = V;
+            return;
+        case 213:
+            *R = V;
+            *G = 0;
+            *B = V;
+            return;
+        }
+    }
+
+    // Cap values
+    unsigned int h = H * 360;
+    unsigned int s = clip_colorval(S) * 256;
+    unsigned int v = clip_colorval(V) * 256;
+
+    // Saturation is zero, make grey
+    if (S == 0) {
+        *R = V;
+        *G = V;
+        *B = V;
+        return;
+    }
+
+    unsigned int r, g, b;
+
+    // Else, calculate color
+    unsigned int Hi = h / 60 / 256;
+    unsigned int f  = h / 60 - Hi * 256;
+    unsigned int p  = v * (65535 - s) / 65536;
+    unsigned int q  = v * (65535 - (f * s) / 256) / 65536;
+    unsigned int t  = v * (65535 - ((255 - f) * s) / 256) / 65536;
+    switch (Hi) {
+    case 0:
+        r = v;
+        g = t;
+        b = p;
+        break;
+    case 1:
+        r = q;
+        g = v;
+        b = p;
+        break;
+    case 2:
+        r = p;
+        g = v;
+        b = t;
+        break;
+    case 3:
+        r = p;
+        g = q;
+        b = v;
+        break;
+    case 4:
+        r = t;
+        g = p;
+        b = v;
+        break;
+    case 5:
+    default:
+        r = v;
+        g = p;
+        b = q;
+        break;
+    }
+
+    *R = clip_colorval(r / 256);
+    *G = clip_colorval(g / 256);
+    *B = clip_colorval(b / 256);
 }
 
 /// 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)
 {
-	float r = R/255.f, g = G/255.f, b = B/255.f;
-	float h, s, l;
-
-	float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
-
-	l = (minrgb + maxrgb) / 2;
-
-	if (minrgb == maxrgb) {
-		h = 0;
-		s = 0;
-	} else {
-		if (l < .5f) {
-			s = (maxrgb - minrgb) / (maxrgb + minrgb);
-		} else {
-			s = (maxrgb - minrgb) / (2.f - maxrgb - minrgb);
-		}
-		if (r == maxrgb) {
-			h = (g-b) / (maxrgb-minrgb) + 0;
-		} else if (g == maxrgb) {
-			h = (b-r) / (maxrgb-minrgb) + 2;
-		} else { // if b == maxrgb
-			h = (r-g) / (maxrgb-minrgb) + 4;
-		}
-	}
-
-	if (h < 0) h += 6;
-	if (h >= 6) h -= 6;
-
-	*H = clip_colorval(int(h*256/6));
-	*S = clip_colorval(int(s*255));
-	*L = clip_colorval(int(l*255));
+    float r = R / 255.f, g = G / 255.f, b = B / 255.f;
+    float h, s, l;
+
+    float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
+
+    l = (minrgb + maxrgb) / 2;
+
+    if (minrgb == maxrgb) {
+        h = 0;
+        s = 0;
+    }
+    else {
+        if (l < .5f) {
+            s = (maxrgb - minrgb) / (maxrgb + minrgb);
+        }
+        else {
+            s = (maxrgb - minrgb) / (2.f - maxrgb - minrgb);
+        }
+        if (r == maxrgb) {
+            h = (g - b) / (maxrgb - minrgb) + 0;
+        }
+        else if (g == maxrgb) {
+            h = (b - r) / (maxrgb - minrgb) + 2;
+        }
+        else {   // if b == maxrgb
+            h = (r - g) / (maxrgb - minrgb) + 4;
+        }
+    }
+
+    if (h < 0) h += 6;
+    if (h >= 6) h -= 6;
+
+    *H = clip_colorval(int(h * 256 / 6));
+    *S = clip_colorval(int(s * 255));
+    *L = clip_colorval(int(l * 255));
 }
 
 /// Formulas from http://en.wikipedia.org/wiki/HSV_color_space
 void rgb_to_hsv(int R, int G, int B, unsigned char *H, unsigned char *S, unsigned char *V)
 {
-	float r = R/255.f, g = G/255.f, b = B/255.f;
-	float h, s, v;
-
-	float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
-
-	v = maxrgb;
-
-	if (maxrgb < .001f) {
-		s = 1;
-	} else {
-		s = (maxrgb - minrgb) / maxrgb;
-	}
-
-	if (minrgb == maxrgb) {
-		h = 0;
-	} else if (maxrgb == r) {
-		h = (g-b) / (maxrgb-minrgb) + 0;
-	} else if (maxrgb == g) {
-		h = (b-r) / (maxrgb-minrgb) + 2;
-	} else { // if maxrgb == b
-		h = (r-g) / (maxrgb-minrgb) + 4;
-	}
-
-	if (h < 0) h += 6;
-	if (h >= 6) h -= 6;
-
-	*H = clip_colorval(int(h*256/6));
-	*S = clip_colorval(int(s*255));
-	*V = clip_colorval(int(v*255));
+    float r = R / 255.f, g = G / 255.f, b = B / 255.f;
+    float h, s, v;
+
+    float maxrgb = std::max(r, std::max(g, b)), minrgb = std::min(r, std::min(g, b));
+
+    v = maxrgb;
+
+    if (maxrgb < .001f) {
+        s = 1;
+    }
+    else {
+        s = (maxrgb - minrgb) / maxrgb;
+    }
+
+    if (minrgb == maxrgb) {
+        h = 0;
+    }
+    else if (maxrgb == r) {
+        h = (g - b) / (maxrgb - minrgb) + 0;
+    }
+    else if (maxrgb == g) {
+        h = (b - r) / (maxrgb - minrgb) + 2;
+    }
+    else {   // if maxrgb == b
+        h = (r - g) / (maxrgb - minrgb) + 4;
+    }
+
+    if (h < 0) h += 6;
+    if (h >= 6) h -= 6;
+
+    *H = clip_colorval(int(h * 256 / 6));
+    *S = clip_colorval(int(s * 255));
+    *V = clip_colorval(int(v * 255));
 }
 
 void hsv_to_hsl(int iH, int iS, int iV, unsigned char *oH, unsigned char *oS, unsigned char *oL)
 {
-	int p = iV * (255 - iS);
-	*oH = iH;
-	*oL = clip_colorval((p + iV*255)/255/2);
-	if (*oL == 0) {
-		*oS = iS; // oS is actually undefined, so any value should work ;)
-	} else if (*oL <= 128) {
-		*oS = clip_colorval((iV*255 - p) / (2 * *oL));
-	} else {
-		*oS = clip_colorval((iV*255 - p) / (511 - 2 * *oL));
-	}
+    int p = iV * (255 - iS);
+    *oH = iH;
+    *oL = clip_colorval((p + iV * 255) / 255 / 2);
+    if (*oL == 0) {
+        *oS = iS; // oS is actually undefined, so any value should work ;)
+    }
+    else if (*oL <= 128) {
+        *oS = clip_colorval((iV * 255 - p) / (2 * *oL));
+    }
+    else {
+        *oS = clip_colorval((iV * 255 - p) / (511 - 2 * *oL));
+    }
 }
 
 void hsl_to_hsv(int iH, int iS, int iL, unsigned char *oH, unsigned char *oS, unsigned char *oV)
 {
-	*oH = iH;
-
-	if (iS == 0) {
-		*oS = 0;
-		*oV = iL;
-		return;
-	}
-
-	if (iL < 128) {
-		*oV = iL * (255 + iS) / 255;
-		*oS = 2 * 255 * iS / (255 + iS);
-	} else {
-		*oV = (iL*255 + iS*255 - iL*iS)/255;
-		*oS = 2 * 255 * iS * (255 - iL) / (iL*255 + iS*255 - iL*iS);
-	}
+    *oH = iH;
+
+    if (iS == 0) {
+        *oS = 0;
+        *oV = iL;
+        return;
+    }
+
+    if (iL < 128) {
+        *oV = iL * (255 + iS) / 255;
+        *oS = 2 * 255 * iS / (255 + iS);
+    }
+    else {
+        *oV = (iL * 255 + iS * 255 - iL * iS) / 255;
+        *oS = 2 * 255 * iS * (255 - iL) / (iL * 255 + iS * 255 - iL * iS);
+    }
 }
diff --git a/src/colour_button.cpp b/src/colour_button.cpp
index f7ff956b415b700a2bf29036469443513961341b..1002662a7a074a5ed646687dc0b124e03afe022a 100644
--- a/src/colour_button.cpp
+++ b/src/colour_button.cpp
@@ -26,28 +26,29 @@
 
 AGI_DEFINE_EVENT(EVT_COLOR, agi::Color);
 
-ColourButton::ColourButton(wxWindow *parent, wxSize const& size, bool alpha, agi::Color col, wxValidator const& validator)
-: wxButton(parent, -1, "", wxDefaultPosition, wxSize(size.GetWidth() + 6, size.GetHeight() + 6), 0, validator)
-, bmp(size)
-, colour(std::move(col))
+ColourButton::ColourButton(wxWindow *parent, wxSize const &size, bool alpha, agi::Color col, wxValidator const &validator)
+    : wxButton(parent, -1, "", wxDefaultPosition, wxSize(size.GetWidth() + 6, size.GetHeight() + 6), 0, validator)
+    , bmp(size)
+    , colour(std::move(col))
 {
-	UpdateBitmap();
-	Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-		GetColorFromUser(GetParent(), colour, alpha, [=](agi::Color new_color) {
-			colour = new_color;
-			UpdateBitmap();
+    UpdateBitmap();
+    Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+        GetColorFromUser(GetParent(), colour, alpha, [ = ](agi::Color new_color) {
+            colour = new_color;
+            UpdateBitmap();
 
-			ValueEvent<agi::Color> evt(EVT_COLOR, GetId(), colour);
-			evt.SetEventObject(this);
-			AddPendingEvent(evt);
-		});
-	});
+            ValueEvent<agi::Color> evt(EVT_COLOR, GetId(), colour);
+            evt.SetEventObject(this);
+            AddPendingEvent(evt);
+        });
+    });
 }
 
-void ColourButton::UpdateBitmap() {
-	using namespace boost::gil;
-	fill_pixels(interleaved_view(bmp.GetWidth(), bmp.GetHeight(),
-		(bgr8_pixel_t*)bmp.GetData(), 3 * bmp.GetWidth()),
-		bgr8_pixel_t(colour.r, colour.g, colour.b));
-	SetBitmapLabel(bmp);
+void ColourButton::UpdateBitmap()
+{
+    using namespace boost::gil;
+    fill_pixels(interleaved_view(bmp.GetWidth(), bmp.GetHeight(),
+                                 (bgr8_pixel_t *)bmp.GetData(), 3 * bmp.GetWidth()),
+                bgr8_pixel_t(colour.r, colour.g, colour.b));
+    SetBitmapLabel(bmp);
 }
diff --git a/src/colour_button.h b/src/colour_button.h
index b2e0f9335e42e6329ff374a4d626b3d10f13d370..8b4116f5e729d28ff9ebf2c84e403e26d38944d7 100644
--- a/src/colour_button.h
+++ b/src/colour_button.h
@@ -27,33 +27,33 @@ AGI_DECLARE_EVENT(EVT_COLOR, agi::Color);
 /// A button which displays a currently-selected color and lets the user pick
 /// a new color when clicked
 class ColourButton: public wxButton {
-	wxImage bmp;       ///< The button's bitmap label
-	agi::Color colour; ///< The current colour
+    wxImage bmp;       ///< The button's bitmap label
+    agi::Color colour; ///< The current colour
 
-	/// Update the bitmap label after the color is changed
-	void UpdateBitmap();
+    /// Update the bitmap label after the color is changed
+    void UpdateBitmap();
 
 public:
-	/// Constructor
-	/// @param parent Parent window
-	/// @param size Size of the bitmap (note: not the size of the button)
-	/// @param alpha Let the user adjust the color's alpha
-	/// @param color Initial color to display
-	ColourButton(wxWindow *parent, wxSize const& size, bool alpha, agi::Color color = agi::Color(), wxValidator const& validator = wxDefaultValidator);
-
-	/// Get the currently selected color
-	agi::Color GetColor() { return colour; }
+    /// Constructor
+    /// @param parent Parent window
+    /// @param size Size of the bitmap (note: not the size of the button)
+    /// @param alpha Let the user adjust the color's alpha
+    /// @param color Initial color to display
+    ColourButton(wxWindow *parent, wxSize const &size, bool alpha, agi::Color color = agi::Color(), wxValidator const &validator = wxDefaultValidator);
+
+    /// Get the currently selected color
+    agi::Color GetColor() { return colour; }
 };
 
 struct ColorValidator final : public wxValidator {
-	agi::Color *color;
-	ColorValidator(agi::Color *color) : color(color) { }
-	wxValidator *Clone() const override { return new ColorValidator(color); }
-	bool Validate(wxWindow*) override { return true; }
-	bool TransferToWindow() override { return true; }
-
-	bool TransferFromWindow() override {
-		*color = static_cast<ColourButton*>(GetWindow())->GetColor();
-		return true;
-	}
+    agi::Color *color;
+    ColorValidator(agi::Color *color) : color(color) { }
+    wxValidator *Clone() const override { return new ColorValidator(color); }
+    bool Validate(wxWindow *) override { return true; }
+    bool TransferToWindow() override { return true; }
+
+    bool TransferFromWindow() override {
+        *color = static_cast<ColourButton *>(GetWindow())->GetColor();
+        return true;
+    }
 };
diff --git a/src/command/app.cpp b/src/command/app.cpp
index 1239242bad52d9fe5defc7e98c24162c38e13718..9abaff84ab459d7ba3a8517552689c82156ac368 100644
--- a/src/command/app.cpp
+++ b/src/command/app.cpp
@@ -49,280 +49,282 @@
 #include "../utils.h"
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct app_about final : public Command {
-	CMD_NAME("app/about")
-	CMD_ICON(about_menu)
-	STR_MENU("&About")
-	STR_DISP("About")
-	STR_HELP("About Aegisub")
-
-	void operator()(agi::Context *c) override {
-		ShowAboutDialog(c->parent);
-	}
+    CMD_NAME("app/about")
+    CMD_ICON(about_menu)
+    STR_MENU("&About")
+    STR_DISP("About")
+    STR_HELP("About Aegisub")
+
+    void operator()(agi::Context *c) override {
+        ShowAboutDialog(c->parent);
+    }
 };
 
 struct app_display_audio_subs final : public Command {
-	CMD_NAME("app/display/audio_subs")
-	STR_MENU("&Audio+Subs View")
-	STR_DISP("Audio+Subs View")
-	STR_HELP("Display audio and the subtitles grid only")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-	void operator()(agi::Context *c) override {
-		c->frame->SetDisplayMode(0,1);
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return !!c->project->AudioProvider();
-	}
-
-	bool IsActive(const agi::Context *c) override {
-		return c->frame->IsAudioShown() && !c->frame->IsVideoShown();
-	}
+    CMD_NAME("app/display/audio_subs")
+    STR_MENU("&Audio+Subs View")
+    STR_DISP("Audio+Subs View")
+    STR_HELP("Display audio and the subtitles grid only")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    void operator()(agi::Context *c) override {
+        c->frame->SetDisplayMode(0, 1);
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->AudioProvider();
+    }
+
+    bool IsActive(const agi::Context *c) override {
+        return c->frame->IsAudioShown() && !c->frame->IsVideoShown();
+    }
 };
 
 struct app_display_full final : public Command {
-	CMD_NAME("app/display/full")
-	STR_MENU("&Full view")
-	STR_DISP("Full view")
-	STR_HELP("Display audio, video and then subtitles grid")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-	void operator()(agi::Context *c) override {
-		c->frame->SetDisplayMode(1,1);
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return c->project->AudioProvider() && c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
-	}
-
-	bool IsActive(const agi::Context *c) override {
-		return c->frame->IsAudioShown() && c->frame->IsVideoShown();
-	}
+    CMD_NAME("app/display/full")
+    STR_MENU("&Full view")
+    STR_DISP("Full view")
+    STR_HELP("Display audio, video and then subtitles grid")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    void operator()(agi::Context *c) override {
+        c->frame->SetDisplayMode(1, 1);
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return c->project->AudioProvider() && c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
+    }
+
+    bool IsActive(const agi::Context *c) override {
+        return c->frame->IsAudioShown() && c->frame->IsVideoShown();
+    }
 };
 
 struct app_display_subs final : public Command {
-	CMD_NAME("app/display/subs")
-	STR_MENU("S&ubs Only View")
-	STR_DISP("Subs Only View")
-	STR_HELP("Display the subtitles grid only")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-	void operator()(agi::Context *c) override {
-		c->frame->SetDisplayMode(0, 0);
-	}
-
-	bool IsActive(const agi::Context *c) override {
-		return !c->frame->IsAudioShown() && !c->frame->IsVideoShown();
-	}
+    CMD_NAME("app/display/subs")
+    STR_MENU("S&ubs Only View")
+    STR_DISP("Subs Only View")
+    STR_HELP("Display the subtitles grid only")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    void operator()(agi::Context *c) override {
+        c->frame->SetDisplayMode(0, 0);
+    }
+
+    bool IsActive(const agi::Context *c) override {
+        return !c->frame->IsAudioShown() && !c->frame->IsVideoShown();
+    }
 };
 
 struct app_display_video_subs final : public Command {
-	CMD_NAME("app/display/video_subs")
-	STR_MENU("&Video+Subs View")
-	STR_DISP("Video+Subs View")
-	STR_HELP("Display video and the subtitles grid only")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-	void operator()(agi::Context *c) override {
-		c->frame->SetDisplayMode(1, 0);
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
-	}
-
-	bool IsActive(const agi::Context *c) override {
-		return !c->frame->IsAudioShown() && c->frame->IsVideoShown();
-	}
+    CMD_NAME("app/display/video_subs")
+    STR_MENU("&Video+Subs View")
+    STR_DISP("Video+Subs View")
+    STR_HELP("Display video and the subtitles grid only")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    void operator()(agi::Context *c) override {
+        c->frame->SetDisplayMode(1, 0);
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
+    }
+
+    bool IsActive(const agi::Context *c) override {
+        return !c->frame->IsAudioShown() && c->frame->IsVideoShown();
+    }
 };
 
 struct app_exit final : public Command {
-	CMD_NAME("app/exit")
-	STR_MENU("E&xit")
-	STR_DISP("Exit")
-	STR_HELP("Exit the application")
-
-	void operator()(agi::Context *) override {
-		wxGetApp().CloseAll();
-	}
+    CMD_NAME("app/exit")
+    STR_MENU("E&xit")
+    STR_DISP("Exit")
+    STR_HELP("Exit the application")
+
+    void operator()(agi::Context *) override {
+        wxGetApp().CloseAll();
+    }
 };
 
 struct app_language final : public Command {
-	CMD_NAME("app/language")
-	CMD_ICON(languages_menu)
-	STR_MENU("&Language...")
-	STR_DISP("Language")
-	STR_HELP("Select Aegisub interface language")
-
-	void operator()(agi::Context *c) override {
-		// Get language
-		auto new_language = wxGetApp().locale.PickLanguage();
-		if (new_language.empty()) return;
-
-		OPT_SET("App/Language")->SetString(new_language);
-
-		// Ask to restart program
-		int result = wxMessageBox("Aegisub needs to be restarted so that the new language can be applied. Restart now?", "Restart Aegisub?", wxYES_NO | wxICON_QUESTION |  wxCENTER);
-		if (result == wxYES) {
-			// Restart Aegisub
-			if (c->frame->Close()) {
-				RestartAegisub();
-			}
-		}
-	}
+    CMD_NAME("app/language")
+    CMD_ICON(languages_menu)
+    STR_MENU("&Language...")
+    STR_DISP("Language")
+    STR_HELP("Select Aegisub interface language")
+
+    void operator()(agi::Context *c) override {
+        // Get language
+        auto new_language = wxGetApp().locale.PickLanguage();
+        if (new_language.empty()) return;
+
+        OPT_SET("App/Language")->SetString(new_language);
+
+        // Ask to restart program
+        int result = wxMessageBox("Aegisub needs to be restarted so that the new language can be applied. Restart now?", "Restart Aegisub?", wxYES_NO | wxICON_QUESTION |  wxCENTER);
+        if (result == wxYES) {
+            // Restart Aegisub
+            if (c->frame->Close()) {
+                RestartAegisub();
+            }
+        }
+    }
 };
 
 struct app_log final : public Command {
-	CMD_NAME("app/log")
-	CMD_ICON(about_menu)
-	STR_MENU("&Log window")
-	STR_DISP("Log window")
-	STR_HELP("View the event log")
-
-	void operator()(agi::Context *c) override {
-		ShowLogWindow(c);
-	}
+    CMD_NAME("app/log")
+    CMD_ICON(about_menu)
+    STR_MENU("&Log window")
+    STR_DISP("Log window")
+    STR_HELP("View the event log")
+
+    void operator()(agi::Context *c) override {
+        ShowLogWindow(c);
+    }
 };
 
 struct app_new_window final : public Command {
-	CMD_NAME("app/new_window")
-	CMD_ICON(new_window_menu)
-	STR_MENU("New &Window")
-	STR_DISP("New Window")
-	STR_HELP("Open a new application window")
-
-	void operator()(agi::Context *) override {
-		wxGetApp().NewProjectContext();
-	}
+    CMD_NAME("app/new_window")
+    CMD_ICON(new_window_menu)
+    STR_MENU("New &Window")
+    STR_DISP("New Window")
+    STR_HELP("Open a new application window")
+
+    void operator()(agi::Context *) override {
+        wxGetApp().NewProjectContext();
+    }
 };
 
 struct app_options final : public Command {
-	CMD_NAME("app/options")
-	CMD_ICON(options_button)
-	STR_MENU("&Options...")
-	STR_DISP("Options")
-	STR_HELP("Configure Aegisub")
-
-	void operator()(agi::Context *c) override {
-		try {
-			ShowPreferences(c->parent);
-		} catch (agi::Exception& e) {
-			LOG_E("config/init") << "Caught exception: " << e.GetMessage();
-		}
-	}
+    CMD_NAME("app/options")
+    CMD_ICON(options_button)
+    STR_MENU("&Options...")
+    STR_DISP("Options")
+    STR_HELP("Configure Aegisub")
+
+    void operator()(agi::Context *c) override {
+        try {
+            ShowPreferences(c->parent);
+        }
+        catch (agi::Exception &e) {
+            LOG_E("config/init") << "Caught exception: " << e.GetMessage();
+        }
+    }
 };
 
 struct app_toggle_global_hotkeys final : public Command {
-	CMD_NAME("app/toggle/global_hotkeys")
-	CMD_ICON(toggle_audio_medusa)
-	STR_MENU("Toggle global hotkey overrides")
-	STR_DISP("Toggle global hotkey overrides")
-	STR_HELP("Toggle global hotkey overrides (Medusa Mode)")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *c) override {
-		return OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool();
-	}
-
-	void operator()(agi::Context *c) override {
-		agi::OptionValue *opt = OPT_SET("Audio/Medusa Timing Hotkeys");
-		opt->SetBool(!opt->GetBool());
-	}
+    CMD_NAME("app/toggle/global_hotkeys")
+    CMD_ICON(toggle_audio_medusa)
+    STR_MENU("Toggle global hotkey overrides")
+    STR_DISP("Toggle global hotkey overrides")
+    STR_HELP("Toggle global hotkey overrides (Medusa Mode)")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *c) override {
+        return OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool();
+    }
+
+    void operator()(agi::Context *c) override {
+        agi::OptionValue *opt = OPT_SET("Audio/Medusa Timing Hotkeys");
+        opt->SetBool(!opt->GetBool());
+    }
 };
 
 struct app_toggle_toolbar final : public Command {
-	CMD_NAME("app/toggle/toolbar")
-	STR_HELP("Toggle the main toolbar")
-	CMD_TYPE(COMMAND_DYNAMIC_NAME)
-
-	wxString StrMenu(const agi::Context *c) const override {
-		return OPT_GET("App/Show Toolbar")->GetBool() ?
-			_("Hide Toolbar") :
-			_("Show Toolbar");
-	}
-
-	wxString StrDisplay(const agi::Context *c) const override {
-		return StrMenu(nullptr);
-	}
-
-	void operator()(agi::Context *c) override {
-		agi::OptionValue *opt = OPT_SET("App/Show Toolbar");
-		opt->SetBool(!opt->GetBool());
-	}
+    CMD_NAME("app/toggle/toolbar")
+    STR_HELP("Toggle the main toolbar")
+    CMD_TYPE(COMMAND_DYNAMIC_NAME)
+
+    wxString StrMenu(const agi::Context *c) const override {
+        return OPT_GET("App/Show Toolbar")->GetBool() ?
+               _("Hide Toolbar") :
+               _("Show Toolbar");
+    }
+
+    wxString StrDisplay(const agi::Context *c) const override {
+        return StrMenu(nullptr);
+    }
+
+    void operator()(agi::Context *c) override {
+        agi::OptionValue *opt = OPT_SET("App/Show Toolbar");
+        opt->SetBool(!opt->GetBool());
+    }
 };
 
 struct app_updates final : public Command {
-	CMD_NAME("app/updates")
-	STR_MENU("&Check for Updates...")
-	STR_DISP("Check for Updates")
-	STR_HELP("Check to see if there is a new version of Aegisub available")
-
-	void operator()(agi::Context *c) override {
-		PerformVersionCheck(true);
-	}
+    CMD_NAME("app/updates")
+    STR_MENU("&Check for Updates...")
+    STR_DISP("Check for Updates")
+    STR_HELP("Check to see if there is a new version of Aegisub available")
+
+    void operator()(agi::Context *c) override {
+        PerformVersionCheck(true);
+    }
 };
 
 #ifdef __WXMAC__
 struct app_minimize final : public Command {
-	CMD_NAME("app/minimize")
-	STR_MENU("Minimize")
-	STR_DISP("Minimize")
-	STR_HELP("Minimize the active window")
-
-	void operator()(agi::Context *c) override {
-		c->frame->Iconize();
-	}
+    CMD_NAME("app/minimize")
+    STR_MENU("Minimize")
+    STR_DISP("Minimize")
+    STR_HELP("Minimize the active window")
+
+    void operator()(agi::Context *c) override {
+        c->frame->Iconize();
+    }
 };
 
 struct app_maximize final : public Command {
-	CMD_NAME("app/maximize")
-	STR_MENU("Zoom")
-	STR_DISP("Zoom")
-	STR_HELP("Maximize the active window")
-
-	void operator()(agi::Context *c) override {
-		c->frame->Maximize(!c->frame->IsMaximized());
-	}
+    CMD_NAME("app/maximize")
+    STR_MENU("Zoom")
+    STR_DISP("Zoom")
+    STR_HELP("Maximize the active window")
+
+    void operator()(agi::Context *c) override {
+        c->frame->Maximize(!c->frame->IsMaximized());
+    }
 };
 
 struct app_bring_to_front final : public Command {
-	CMD_NAME("app/bring_to_front")
-	STR_MENU("Bring All to Front")
-	STR_DISP("Bring All to Front")
-	STR_HELP("Bring forward all open documents to the front")
-
-	void operator()(agi::Context *) override {
-		osx::bring_to_front();
-	}
+    CMD_NAME("app/bring_to_front")
+    STR_MENU("Bring All to Front")
+    STR_DISP("Bring All to Front")
+    STR_HELP("Bring forward all open documents to the front")
+
+    void operator()(agi::Context *) override {
+        osx::bring_to_front();
+    }
 };
 #endif
 
 }
 
 namespace cmd {
-	void init_app() {
-		reg(agi::make_unique<app_about>());
-		reg(agi::make_unique<app_display_audio_subs>());
-		reg(agi::make_unique<app_display_full>());
-		reg(agi::make_unique<app_display_subs>());
-		reg(agi::make_unique<app_display_video_subs>());
-		reg(agi::make_unique<app_exit>());
-		reg(agi::make_unique<app_language>());
-		reg(agi::make_unique<app_log>());
-		reg(agi::make_unique<app_new_window>());
-		reg(agi::make_unique<app_options>());
-		reg(agi::make_unique<app_toggle_global_hotkeys>());
-		reg(agi::make_unique<app_toggle_toolbar>());
+void init_app()
+{
+    reg(agi::make_unique<app_about>());
+    reg(agi::make_unique<app_display_audio_subs>());
+    reg(agi::make_unique<app_display_full>());
+    reg(agi::make_unique<app_display_subs>());
+    reg(agi::make_unique<app_display_video_subs>());
+    reg(agi::make_unique<app_exit>());
+    reg(agi::make_unique<app_language>());
+    reg(agi::make_unique<app_log>());
+    reg(agi::make_unique<app_new_window>());
+    reg(agi::make_unique<app_options>());
+    reg(agi::make_unique<app_toggle_global_hotkeys>());
+    reg(agi::make_unique<app_toggle_toolbar>());
 #ifdef __WXMAC__
-		reg(agi::make_unique<app_minimize>());
-		reg(agi::make_unique<app_maximize>());
-		reg(agi::make_unique<app_bring_to_front>());
+    reg(agi::make_unique<app_minimize>());
+    reg(agi::make_unique<app_maximize>());
+    reg(agi::make_unique<app_bring_to_front>());
 #endif
 #ifdef WITH_UPDATE_CHECKER
-		reg(agi::make_unique<app_updates>());
+    reg(agi::make_unique<app_updates>());
 #endif
-	}
+}
 }
diff --git a/src/command/audio.cpp b/src/command/audio.cpp
index 3868667755554ab3f471a97f20380b835b67c04a..b1d389aef01f997be3d5250d0edcba13056fe561 100644
--- a/src/command/audio.cpp
+++ b/src/command/audio.cpp
@@ -51,520 +51,522 @@
 #include <libaegisub/io.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
-	struct validate_audio_open : public Command {
-		CMD_TYPE(COMMAND_VALIDATE)
-		bool Validate(const agi::Context *c) override {
-			return !!c->project->AudioProvider();
-		}
-	};
+struct validate_audio_open : public Command {
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->AudioProvider();
+    }
+};
 
 struct audio_close final : public validate_audio_open {
-	CMD_NAME("audio/close")
-	CMD_ICON(close_audio_menu)
-	STR_MENU("&Close Audio")
-	STR_DISP("Close Audio")
-	STR_HELP("Close the currently open audio file")
-
-	void operator()(agi::Context *c) override {
-		c->project->CloseAudio();
-	}
+    CMD_NAME("audio/close")
+    CMD_ICON(close_audio_menu)
+    STR_MENU("&Close Audio")
+    STR_DISP("Close Audio")
+    STR_HELP("Close the currently open audio file")
+
+    void operator()(agi::Context *c) override {
+        c->project->CloseAudio();
+    }
 };
 
 struct audio_open final : public Command {
-	CMD_NAME("audio/open")
-	CMD_ICON(open_audio_menu)
-	STR_MENU("&Open Audio File...")
-	STR_DISP("Open Audio File")
-	STR_HELP("Open an audio file")
-
-	void operator()(agi::Context *c) override {
-		auto str = from_wx(_("Audio Formats") + " (*.aac,*.ac3,*.ape,*.dts,*.eac3,*.flac,*.m4a,*.mka,*.mp3,*.mp4,*.ogg,*.opus,*.w64,*.wav,*.wma)|*.aac;*.ac3;*.ape;*.dts;*.eac3;*.flac;*.m4a;*.mka;*.mp3;*.mp4;*.ogg;*.opus;*.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") + " (*.*)|*.*");
-		auto filename = OpenFileSelector(_("Open Audio File"), "Path/Last/Audio", "", "", str, c->parent);
-		if (!filename.empty())
-			c->project->LoadAudio(filename);
-	}
+    CMD_NAME("audio/open")
+    CMD_ICON(open_audio_menu)
+    STR_MENU("&Open Audio File...")
+    STR_DISP("Open Audio File")
+    STR_HELP("Open an audio file")
+
+    void operator()(agi::Context *c) override {
+        auto str = from_wx(_("Audio Formats") + " (*.aac,*.ac3,*.ape,*.dts,*.eac3,*.flac,*.m4a,*.mka,*.mp3,*.mp4,*.ogg,*.opus,*.w64,*.wav,*.wma)|*.aac;*.ac3;*.ape;*.dts;*.eac3;*.flac;*.m4a;*.mka;*.mp3;*.mp4;*.ogg;*.opus;*.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") + " (*.*)|*.*");
+        auto filename = OpenFileSelector(_("Open Audio File"), "Path/Last/Audio", "", "", str, c->parent);
+        if (!filename.empty())
+            c->project->LoadAudio(filename);
+    }
 };
 
 struct audio_open_blank final : public Command {
-	CMD_NAME("audio/open/blank")
-	STR_MENU("Open 2h30 Blank Audio")
-	STR_DISP("Open 2h30 Blank Audio")
-	STR_HELP("Open a 150 minutes blank audio clip, for debugging")
-
-	void operator()(agi::Context *c) override {
-		c->project->LoadAudio("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000");
-	}
+    CMD_NAME("audio/open/blank")
+    STR_MENU("Open 2h30 Blank Audio")
+    STR_DISP("Open 2h30 Blank Audio")
+    STR_HELP("Open a 150 minutes blank audio clip, for debugging")
+
+    void operator()(agi::Context *c) override {
+        c->project->LoadAudio("dummy-audio:silence?sr=44100&bd=16&ch=1&ln=396900000");
+    }
 };
 
 struct audio_open_noise final : public Command {
-	CMD_NAME("audio/open/noise")
-	STR_MENU("Open 2h30 Noise Audio")
-	STR_DISP("Open 2h30 Noise Audio")
-	STR_HELP("Open a 150 minutes noise-filled audio clip, for debugging")
-
-	void operator()(agi::Context *c) override {
-		c->project->LoadAudio("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000");
-	}
+    CMD_NAME("audio/open/noise")
+    STR_MENU("Open 2h30 Noise Audio")
+    STR_DISP("Open 2h30 Noise Audio")
+    STR_HELP("Open a 150 minutes noise-filled audio clip, for debugging")
+
+    void operator()(agi::Context *c) override {
+        c->project->LoadAudio("dummy-audio:noise?sr=44100&bd=16&ch=1&ln=396900000");
+    }
 };
 
 struct audio_open_video final : public Command {
-	CMD_NAME("audio/open/video")
-	CMD_ICON(open_audio_from_video_menu)
-	STR_MENU("Open Audio from &Video")
-	STR_DISP("Open Audio from Video")
-	STR_HELP("Open the audio from the current video file")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->project->VideoProvider() && c->project->VideoProvider()->HasAudio();
-	}
-
-	void operator()(agi::Context *c) override {
-		c->project->LoadAudio(c->project->VideoName());
-	}
+    CMD_NAME("audio/open/video")
+    CMD_ICON(open_audio_from_video_menu)
+    STR_MENU("Open Audio from &Video")
+    STR_DISP("Open Audio from Video")
+    STR_HELP("Open the audio from the current video file")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->project->VideoProvider() && c->project->VideoProvider()->HasAudio();
+    }
+
+    void operator()(agi::Context *c) override {
+        c->project->LoadAudio(c->project->VideoName());
+    }
 };
 
 struct audio_view_spectrum final : public Command {
-	CMD_NAME("audio/view/spectrum")
-	STR_MENU("&Spectrum Display")
-	STR_DISP("Spectrum Display")
-	STR_HELP("Display audio as a frequency-power spectrograph")
-	CMD_TYPE(COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Spectrum")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		OPT_SET("Audio/Spectrum")->SetBool(true);
-	}
+    CMD_NAME("audio/view/spectrum")
+    STR_MENU("&Spectrum Display")
+    STR_DISP("Spectrum Display")
+    STR_HELP("Display audio as a frequency-power spectrograph")
+    CMD_TYPE(COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Spectrum")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        OPT_SET("Audio/Spectrum")->SetBool(true);
+    }
 };
 
 struct audio_view_waveform final : public Command {
-	CMD_NAME("audio/view/waveform")
-	STR_MENU("&Waveform Display")
-	STR_DISP("Waveform Display")
-	STR_HELP("Display audio as a linear amplitude graph")
-	CMD_TYPE(COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *) override {
-		return !OPT_GET("Audio/Spectrum")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		OPT_SET("Audio/Spectrum")->SetBool(false);
-	}
+    CMD_NAME("audio/view/waveform")
+    STR_MENU("&Waveform Display")
+    STR_DISP("Waveform Display")
+    STR_HELP("Display audio as a linear amplitude graph")
+    CMD_TYPE(COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *) override {
+        return !OPT_GET("Audio/Spectrum")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        OPT_SET("Audio/Spectrum")->SetBool(false);
+    }
 };
 
 struct audio_save_clip final : public Command {
-	CMD_NAME("audio/save/clip")
-	STR_MENU("Create audio clip")
-	STR_DISP("Create audio clip")
-	STR_HELP("Save an audio clip of the selected line")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->project->AudioProvider() && !c->selectionController->GetSelectedSet().empty();
-	}
-
-	void operator()(agi::Context *c) override {
-		auto const& sel = c->selectionController->GetSelectedSet();
-		if (sel.empty()) return;
-
-		auto filename = SaveFileSelector(_("Save audio clip"), "", "", "wav", "", c->parent);
-		if (filename.empty()) return;
-
-		agi::Time start = INT_MAX, end = 0;
-		for (auto line : sel) {
-			start = std::min(start, line->Start);
-			end = std::max(end, line->End);
-		}
-
-		agi::SaveAudioClip(*c->project->AudioProvider(), filename, start, end);
-	}
+    CMD_NAME("audio/save/clip")
+    STR_MENU("Create audio clip")
+    STR_DISP("Create audio clip")
+    STR_HELP("Save an audio clip of the selected line")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->project->AudioProvider() && !c->selectionController->GetSelectedSet().empty();
+    }
+
+    void operator()(agi::Context *c) override {
+        auto const &sel = c->selectionController->GetSelectedSet();
+        if (sel.empty()) return;
+
+        auto filename = SaveFileSelector(_("Save audio clip"), "", "", "wav", "", c->parent);
+        if (filename.empty()) return;
+
+        agi::Time start = INT_MAX, end = 0;
+        for (auto line : sel) {
+            start = std::min(start, line->Start);
+            end = std::max(end, line->End);
+        }
+
+        agi::SaveAudioClip(*c->project->AudioProvider(), filename, start, end);
+    }
 };
 
 struct audio_play_current_selection final : public validate_audio_open {
-	CMD_NAME("audio/play/current")
-	STR_MENU("Play current audio selection")
-	STR_DISP("Play current audio selection")
-	STR_HELP("Play the current audio selection, ignoring changes made while playing")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->audioController->PlayRange(c->audioController->GetPrimaryPlaybackRange());
-	}
+    CMD_NAME("audio/play/current")
+    STR_MENU("Play current audio selection")
+    STR_DISP("Play current audio selection")
+    STR_HELP("Play the current audio selection, ignoring changes made while playing")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->audioController->PlayRange(c->audioController->GetPrimaryPlaybackRange());
+    }
 };
 
 struct audio_play_current_line final : public validate_audio_open {
-	CMD_NAME("audio/play/line")
-	CMD_ICON(button_playline)
-	STR_MENU("Play current line")
-	STR_DISP("Play current line")
-	STR_HELP("Play the audio for the current line")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc)
-			c->audioController->PlayRange(tc->GetActiveLineRange());
-	}
+    CMD_NAME("audio/play/line")
+    CMD_ICON(button_playline)
+    STR_MENU("Play current line")
+    STR_DISP("Play current line")
+    STR_HELP("Play the audio for the current line")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc)
+            c->audioController->PlayRange(tc->GetActiveLineRange());
+    }
 };
 
 struct audio_play_selection final : public validate_audio_open {
-	CMD_NAME("audio/play/selection")
-	CMD_ICON(button_playsel)
-	STR_MENU("Play audio selection")
-	STR_DISP("Play audio selection")
-	STR_HELP("Play audio until the end of the selection is reached")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->audioController->PlayPrimaryRange();
-	}
+    CMD_NAME("audio/play/selection")
+    CMD_ICON(button_playsel)
+    STR_MENU("Play audio selection")
+    STR_DISP("Play audio selection")
+    STR_HELP("Play audio until the end of the selection is reached")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->audioController->PlayPrimaryRange();
+    }
 };
 
 struct audio_play_toggle final : public validate_audio_open {
-	CMD_NAME("audio/play/toggle")
-	STR_MENU("Play audio selection or stop")
-	STR_DISP("Play audio selection or stop")
-	STR_HELP("Play selection, or stop playback if it's already playing")
-
-	void operator()(agi::Context *c) override {
-		if (c->audioController->IsPlaying())
-			c->audioController->Stop();
-		else {
-			c->videoController->Stop();
-			c->audioController->PlayPrimaryRange();
-		}
-	}
+    CMD_NAME("audio/play/toggle")
+    STR_MENU("Play audio selection or stop")
+    STR_DISP("Play audio selection or stop")
+    STR_HELP("Play selection, or stop playback if it's already playing")
+
+    void operator()(agi::Context *c) override {
+        if (c->audioController->IsPlaying())
+            c->audioController->Stop();
+        else {
+            c->videoController->Stop();
+            c->audioController->PlayPrimaryRange();
+        }
+    }
 };
 
 struct audio_stop final : public Command {
-	CMD_NAME("audio/stop")
-	CMD_ICON(button_stop)
-	STR_MENU("Stop playing")
-	STR_DISP("Stop playing")
-	STR_HELP("Stop audio and video playback")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->audioController->IsPlaying();
-	}
-
-	void operator()(agi::Context *c) override {
-		c->audioController->Stop();
-		c->videoController->Stop();
-	}
+    CMD_NAME("audio/stop")
+    CMD_ICON(button_stop)
+    STR_MENU("Stop playing")
+    STR_DISP("Stop playing")
+    STR_HELP("Stop audio and video playback")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->audioController->IsPlaying();
+    }
+
+    void operator()(agi::Context *c) override {
+        c->audioController->Stop();
+        c->videoController->Stop();
+    }
 };
 
 struct audio_play_before final : public validate_audio_open {
-	CMD_NAME("audio/play/selection/before")
-	CMD_ICON(button_playfivehbefore)
-	STR_MENU("Play 500 ms before selection")
-	STR_DISP("Play 500 ms before selection")
-	STR_HELP("Play 500 ms before selection")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		int begin = c->audioController->GetPrimaryPlaybackRange().begin();
-		c->audioController->PlayRange(TimeRange(begin - 500, begin));
-	}
+    CMD_NAME("audio/play/selection/before")
+    CMD_ICON(button_playfivehbefore)
+    STR_MENU("Play 500 ms before selection")
+    STR_DISP("Play 500 ms before selection")
+    STR_HELP("Play 500 ms before selection")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        int begin = c->audioController->GetPrimaryPlaybackRange().begin();
+        c->audioController->PlayRange(TimeRange(begin - 500, begin));
+    }
 };
 
 struct audio_play_after final : public validate_audio_open {
-	CMD_NAME("audio/play/selection/after")
-	CMD_ICON(button_playfivehafter)
-	STR_MENU("Play 500 ms after selection")
-	STR_DISP("Play 500 ms after selection")
-	STR_HELP("Play 500 ms after selection")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		int end = c->audioController->GetPrimaryPlaybackRange().end();
-		c->audioController->PlayRange(TimeRange(end, end + 500));
-	}
+    CMD_NAME("audio/play/selection/after")
+    CMD_ICON(button_playfivehafter)
+    STR_MENU("Play 500 ms after selection")
+    STR_DISP("Play 500 ms after selection")
+    STR_HELP("Play 500 ms after selection")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        int end = c->audioController->GetPrimaryPlaybackRange().end();
+        c->audioController->PlayRange(TimeRange(end, end + 500));
+    }
 };
 
 struct audio_play_end final : public validate_audio_open {
-	CMD_NAME("audio/play/selection/end")
-	CMD_ICON(button_playlastfiveh)
-	STR_MENU("Play last 500 ms of selection")
-	STR_DISP("Play last 500 ms of selection")
-	STR_HELP("Play last 500 ms of selection")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		TimeRange times(c->audioController->GetPrimaryPlaybackRange());
-		c->audioController->PlayToEndOfPrimary(times.end() - std::min(500, times.length()));
-	}
+    CMD_NAME("audio/play/selection/end")
+    CMD_ICON(button_playlastfiveh)
+    STR_MENU("Play last 500 ms of selection")
+    STR_DISP("Play last 500 ms of selection")
+    STR_HELP("Play last 500 ms of selection")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        TimeRange times(c->audioController->GetPrimaryPlaybackRange());
+        c->audioController->PlayToEndOfPrimary(times.end() - std::min(500, times.length()));
+    }
 };
 
 struct audio_play_begin final : public validate_audio_open {
-	CMD_NAME("audio/play/selection/begin")
-	CMD_ICON(button_playfirstfiveh)
-	STR_MENU("Play first 500 ms of selection")
-	STR_DISP("Play first 500 ms of selection")
-	STR_HELP("Play first 500 ms of selection")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		TimeRange times(c->audioController->GetPrimaryPlaybackRange());
-		c->audioController->PlayRange(TimeRange(
-			times.begin(),
-			times.begin() + std::min(500, times.length())));
-	}
+    CMD_NAME("audio/play/selection/begin")
+    CMD_ICON(button_playfirstfiveh)
+    STR_MENU("Play first 500 ms of selection")
+    STR_DISP("Play first 500 ms of selection")
+    STR_HELP("Play first 500 ms of selection")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        TimeRange times(c->audioController->GetPrimaryPlaybackRange());
+        c->audioController->PlayRange(TimeRange(
+                                          times.begin(),
+                                          times.begin() + std::min(500, times.length())));
+    }
 };
 
 struct audio_play_to_end final : public validate_audio_open {
-	CMD_NAME("audio/play/to_end")
-	CMD_ICON(button_playtoend)
-	STR_MENU("Play from selection start to end of file")
-	STR_DISP("Play from selection start to end of file")
-	STR_HELP("Play from selection start to end of file")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->audioController->PlayToEnd(c->audioController->GetPrimaryPlaybackRange().begin());
-	}
+    CMD_NAME("audio/play/to_end")
+    CMD_ICON(button_playtoend)
+    STR_MENU("Play from selection start to end of file")
+    STR_DISP("Play from selection start to end of file")
+    STR_HELP("Play from selection start to end of file")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->audioController->PlayToEnd(c->audioController->GetPrimaryPlaybackRange().begin());
+    }
 };
 
 struct audio_commit final : public validate_audio_open {
-	CMD_NAME("audio/commit")
-	CMD_ICON(button_audio_commit)
-	STR_MENU("Commit")
-	STR_DISP("Commit")
-	STR_HELP("Commit any pending audio timing changes")
-
-	void operator()(agi::Context *c) override {
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc) {
-			tc->Commit();
-			if(OPT_GET("Audio/Next Line on Commit")->GetBool())
-				tc->Next(AudioTimingController::LINE);
-		}
-	}
+    CMD_NAME("audio/commit")
+    CMD_ICON(button_audio_commit)
+    STR_MENU("Commit")
+    STR_DISP("Commit")
+    STR_HELP("Commit any pending audio timing changes")
+
+    void operator()(agi::Context *c) override {
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc) {
+            tc->Commit();
+            if (OPT_GET("Audio/Next Line on Commit")->GetBool())
+                tc->Next(AudioTimingController::LINE);
+        }
+    }
 };
 
 struct audio_commit_default final : public validate_audio_open {
-	CMD_NAME("audio/commit/default")
-	STR_MENU("Commit and use default timing for next line")
-	STR_DISP("Commit and use default timing for next line")
-	STR_HELP("Commit any pending audio timing changes and reset the next line's times to the default")
-
-	void operator()(agi::Context *c) override {
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc) {
-			tc->Commit();
-			tc->Next(AudioTimingController::LINE_RESET_DEFAULT);
-		}
-	}
+    CMD_NAME("audio/commit/default")
+    STR_MENU("Commit and use default timing for next line")
+    STR_DISP("Commit and use default timing for next line")
+    STR_HELP("Commit any pending audio timing changes and reset the next line's times to the default")
+
+    void operator()(agi::Context *c) override {
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc) {
+            tc->Commit();
+            tc->Next(AudioTimingController::LINE_RESET_DEFAULT);
+        }
+    }
 };
 
 struct audio_commit_next final : public validate_audio_open {
-	CMD_NAME("audio/commit/next")
-	STR_MENU("Commit and move to next line")
-	STR_DISP("Commit and move to next line")
-	STR_HELP("Commit any pending audio timing changes and move to the next line")
-
-	void operator()(agi::Context *c) override {
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc) {
-			tc->Commit();
-			tc->Next(AudioTimingController::LINE);
-		}
-	}
+    CMD_NAME("audio/commit/next")
+    STR_MENU("Commit and move to next line")
+    STR_DISP("Commit and move to next line")
+    STR_HELP("Commit any pending audio timing changes and move to the next line")
+
+    void operator()(agi::Context *c) override {
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc) {
+            tc->Commit();
+            tc->Next(AudioTimingController::LINE);
+        }
+    }
 };
 
 struct audio_commit_stay final : public validate_audio_open {
-	CMD_NAME("audio/commit/stay")
-	STR_MENU("Commit and stay on current line")
-	STR_DISP("Commit and stay on current line")
-	STR_HELP("Commit any pending audio timing changes and stay on the current line")
-
-	void operator()(agi::Context *c) override {
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc) tc->Commit();
-	}
+    CMD_NAME("audio/commit/stay")
+    STR_MENU("Commit and stay on current line")
+    STR_DISP("Commit and stay on current line")
+    STR_HELP("Commit any pending audio timing changes and stay on the current line")
+
+    void operator()(agi::Context *c) override {
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc) tc->Commit();
+    }
 };
 
 struct audio_go_to final : public validate_audio_open {
-	CMD_NAME("audio/go_to")
-	CMD_ICON(button_audio_goto)
-	STR_MENU("Go to selection")
-	STR_DISP("Go to selection")
-	STR_HELP("Scroll the audio display to center on the current audio selection")
-
-	void operator()(agi::Context *c) override {
-		c->audioBox->ScrollToActiveLine();
-	}
+    CMD_NAME("audio/go_to")
+    CMD_ICON(button_audio_goto)
+    STR_MENU("Go to selection")
+    STR_DISP("Go to selection")
+    STR_HELP("Scroll the audio display to center on the current audio selection")
+
+    void operator()(agi::Context *c) override {
+        c->audioBox->ScrollToActiveLine();
+    }
 };
 
 struct audio_scroll_left final : public validate_audio_open {
-	CMD_NAME("audio/scroll/left")
-		STR_MENU("Scroll left")
-		STR_DISP("Scroll left")
-		STR_HELP("Scroll the audio display left")
-
-		void operator()(agi::Context *c) override {
-			c->audioBox->ScrollAudioBy(-128);
-	}
+    CMD_NAME("audio/scroll/left")
+    STR_MENU("Scroll left")
+    STR_DISP("Scroll left")
+    STR_HELP("Scroll the audio display left")
+
+    void operator()(agi::Context *c) override {
+        c->audioBox->ScrollAudioBy(-128);
+    }
 };
 
 struct audio_scroll_right final : public validate_audio_open {
-	CMD_NAME("audio/scroll/right")
-		STR_MENU("Scroll right")
-		STR_DISP("Scroll right")
-		STR_HELP("Scroll the audio display right")
-
-		void operator()(agi::Context *c) override {
-			c->audioBox->ScrollAudioBy(128);
-	}
+    CMD_NAME("audio/scroll/right")
+    STR_MENU("Scroll right")
+    STR_DISP("Scroll right")
+    STR_HELP("Scroll the audio display right")
+
+    void operator()(agi::Context *c) override {
+        c->audioBox->ScrollAudioBy(128);
+    }
 };
 
-static inline void toggle(const char *opt) {
-	OPT_SET(opt)->SetBool(!OPT_GET(opt)->GetBool());
+static inline void toggle(const char *opt)
+{
+    OPT_SET(opt)->SetBool(!OPT_GET(opt)->GetBool());
 }
 
 struct audio_autoscroll final : public Command {
-	CMD_NAME("audio/opt/autoscroll")
-	CMD_ICON(toggle_audio_autoscroll)
-	STR_MENU("Auto scroll audio display to selected line")
-	STR_DISP("Auto scroll audio display to selected line")
-	STR_HELP("Auto scroll audio display to selected line")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Auto/Scroll")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Audio/Auto/Scroll");
-	}
+    CMD_NAME("audio/opt/autoscroll")
+    CMD_ICON(toggle_audio_autoscroll)
+    STR_MENU("Auto scroll audio display to selected line")
+    STR_DISP("Auto scroll audio display to selected line")
+    STR_HELP("Auto scroll audio display to selected line")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Auto/Scroll")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Audio/Auto/Scroll");
+    }
 };
 
 struct audio_autocommit final : public Command {
-	CMD_NAME("audio/opt/autocommit")
-	CMD_ICON(toggle_audio_autocommit)
-	STR_MENU("Automatically commit all changes")
-	STR_DISP("Automatically commit all changes")
-	STR_HELP("Automatically commit all changes")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Auto/Commit")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Audio/Auto/Commit");
-	}
+    CMD_NAME("audio/opt/autocommit")
+    CMD_ICON(toggle_audio_autocommit)
+    STR_MENU("Automatically commit all changes")
+    STR_DISP("Automatically commit all changes")
+    STR_HELP("Automatically commit all changes")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Auto/Commit")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Audio/Auto/Commit");
+    }
 };
 
 struct audio_autonext final : public Command {
-	CMD_NAME("audio/opt/autonext")
-	CMD_ICON(toggle_audio_nextcommit)
-	STR_MENU("Auto go to next line on commit")
-	STR_DISP("Auto go to next line on commit")
-	STR_HELP("Automatically go to next line on commit")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Next Line on Commit")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Audio/Next Line on Commit");
-	}
+    CMD_NAME("audio/opt/autonext")
+    CMD_ICON(toggle_audio_nextcommit)
+    STR_MENU("Auto go to next line on commit")
+    STR_DISP("Auto go to next line on commit")
+    STR_HELP("Automatically go to next line on commit")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Next Line on Commit")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Audio/Next Line on Commit");
+    }
 };
 
 struct audio_toggle_spectrum final : public Command {
-	CMD_NAME("audio/opt/spectrum")
-	CMD_ICON(toggle_audio_spectrum)
-	STR_MENU("Spectrum analyzer mode")
-	STR_DISP("Spectrum analyzer mode")
-	STR_HELP("Spectrum analyzer mode")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Spectrum")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Audio/Spectrum");
-	}
+    CMD_NAME("audio/opt/spectrum")
+    CMD_ICON(toggle_audio_spectrum)
+    STR_MENU("Spectrum analyzer mode")
+    STR_DISP("Spectrum analyzer mode")
+    STR_HELP("Spectrum analyzer mode")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Spectrum")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Audio/Spectrum");
+    }
 };
 
 struct audio_vertical_link final : public Command {
-	CMD_NAME("audio/opt/vertical_link")
-	CMD_ICON(toggle_audio_link)
-	STR_MENU("Link vertical zoom and volume sliders")
-	STR_DISP("Link vertical zoom and volume sliders")
-	STR_HELP("Link vertical zoom and volume sliders")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Audio/Link")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Audio/Link");
-	}
+    CMD_NAME("audio/opt/vertical_link")
+    CMD_ICON(toggle_audio_link)
+    STR_MENU("Link vertical zoom and volume sliders")
+    STR_DISP("Link vertical zoom and volume sliders")
+    STR_HELP("Link vertical zoom and volume sliders")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Audio/Link")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Audio/Link");
+    }
 };
 
 struct audio_karaoke final : public Command {
-	CMD_NAME("audio/karaoke")
-	CMD_ICON(kara_mode)
-	STR_MENU("Toggle karaoke mode")
-	STR_DISP("Toggle karaoke mode")
-	STR_HELP("Toggle karaoke mode")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *c) override {
-		return c->karaoke->IsEnabled();
-	}
-	void operator()(agi::Context *c) override {
-		c->karaoke->SetEnabled(!c->karaoke->IsEnabled());
-	}
+    CMD_NAME("audio/karaoke")
+    CMD_ICON(kara_mode)
+    STR_MENU("Toggle karaoke mode")
+    STR_DISP("Toggle karaoke mode")
+    STR_HELP("Toggle karaoke mode")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *c) override {
+        return c->karaoke->IsEnabled();
+    }
+    void operator()(agi::Context *c) override {
+        c->karaoke->SetEnabled(!c->karaoke->IsEnabled());
+    }
 };
 
 }
 
 namespace cmd {
-	void init_audio() {
-		reg(agi::make_unique<audio_autocommit>());
-		reg(agi::make_unique<audio_autonext>());
-		reg(agi::make_unique<audio_autoscroll>());
-		reg(agi::make_unique<audio_close>());
-		reg(agi::make_unique<audio_commit>());
-		reg(agi::make_unique<audio_commit_default>());
-		reg(agi::make_unique<audio_commit_next>());
-		reg(agi::make_unique<audio_commit_stay>());
-		reg(agi::make_unique<audio_go_to>());
-		reg(agi::make_unique<audio_karaoke>());
-		reg(agi::make_unique<audio_open>());
-		reg(agi::make_unique<audio_open_blank>());
-		reg(agi::make_unique<audio_open_noise>());
-		reg(agi::make_unique<audio_open_video>());
-		reg(agi::make_unique<audio_play_after>());
-		reg(agi::make_unique<audio_play_before>());
-		reg(agi::make_unique<audio_play_begin>());
-		reg(agi::make_unique<audio_play_end>());
-		reg(agi::make_unique<audio_play_current_selection>());
-		reg(agi::make_unique<audio_play_current_line>());
-		reg(agi::make_unique<audio_play_selection>());
-		reg(agi::make_unique<audio_play_to_end>());
-		reg(agi::make_unique<audio_play_toggle>());
-		reg(agi::make_unique<audio_save_clip>());
-		reg(agi::make_unique<audio_scroll_left>());
-		reg(agi::make_unique<audio_scroll_right>());
-		reg(agi::make_unique<audio_stop>());
-		reg(agi::make_unique<audio_toggle_spectrum>());
-		reg(agi::make_unique<audio_vertical_link>());
-		reg(agi::make_unique<audio_view_spectrum>());
-		reg(agi::make_unique<audio_view_waveform>());
-	}
+void init_audio()
+{
+    reg(agi::make_unique<audio_autocommit>());
+    reg(agi::make_unique<audio_autonext>());
+    reg(agi::make_unique<audio_autoscroll>());
+    reg(agi::make_unique<audio_close>());
+    reg(agi::make_unique<audio_commit>());
+    reg(agi::make_unique<audio_commit_default>());
+    reg(agi::make_unique<audio_commit_next>());
+    reg(agi::make_unique<audio_commit_stay>());
+    reg(agi::make_unique<audio_go_to>());
+    reg(agi::make_unique<audio_karaoke>());
+    reg(agi::make_unique<audio_open>());
+    reg(agi::make_unique<audio_open_blank>());
+    reg(agi::make_unique<audio_open_noise>());
+    reg(agi::make_unique<audio_open_video>());
+    reg(agi::make_unique<audio_play_after>());
+    reg(agi::make_unique<audio_play_before>());
+    reg(agi::make_unique<audio_play_begin>());
+    reg(agi::make_unique<audio_play_end>());
+    reg(agi::make_unique<audio_play_current_selection>());
+    reg(agi::make_unique<audio_play_current_line>());
+    reg(agi::make_unique<audio_play_selection>());
+    reg(agi::make_unique<audio_play_to_end>());
+    reg(agi::make_unique<audio_play_toggle>());
+    reg(agi::make_unique<audio_save_clip>());
+    reg(agi::make_unique<audio_scroll_left>());
+    reg(agi::make_unique<audio_scroll_right>());
+    reg(agi::make_unique<audio_stop>());
+    reg(agi::make_unique<audio_toggle_spectrum>());
+    reg(agi::make_unique<audio_vertical_link>());
+    reg(agi::make_unique<audio_view_spectrum>());
+    reg(agi::make_unique<audio_view_waveform>());
+}
 }
diff --git a/src/command/automation.cpp b/src/command/automation.cpp
index 96669940bf71a95b2877bbbdcfc43073b30f8f68..5d89ed11c1ee0405a5ef26b34ff54ad56cb24b81 100644
--- a/src/command/automation.cpp
+++ b/src/command/automation.cpp
@@ -41,71 +41,72 @@
 #include <libaegisub/make_unique.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct reload_all final : public Command {
-	CMD_NAME("am/reload")
-	STR_MENU("&Reload Automation scripts")
-	STR_DISP("Reload Automation scripts")
-	STR_HELP("Reload all Automation scripts and rescan the autoload folder")
+    CMD_NAME("am/reload")
+    STR_MENU("&Reload Automation scripts")
+    STR_DISP("Reload Automation scripts")
+    STR_HELP("Reload all Automation scripts and rescan the autoload folder")
 
-	void operator()(agi::Context *c) override {
-		config::global_scripts->Reload();
-		c->local_scripts->Reload();
-		c->frame->StatusTimeout(_("Reloaded all Automation scripts"));
-	}
+    void operator()(agi::Context *c) override {
+        config::global_scripts->Reload();
+        c->local_scripts->Reload();
+        c->frame->StatusTimeout(_("Reloaded all Automation scripts"));
+    }
 };
 
 struct reload_autoload final : public Command {
-	CMD_NAME("am/reload/autoload")
-	STR_MENU("R&eload autoload Automation scripts")
-	STR_DISP("Reload autoload Automation scripts")
-	STR_HELP("Rescan the Automation autoload folder")
+    CMD_NAME("am/reload/autoload")
+    STR_MENU("R&eload autoload Automation scripts")
+    STR_DISP("Reload autoload Automation scripts")
+    STR_HELP("Rescan the Automation autoload folder")
 
-	void operator()(agi::Context *c) override {
-		config::global_scripts->Reload();
-		c->frame->StatusTimeout(_("Reloaded autoload Automation scripts"));
-	}
+    void operator()(agi::Context *c) override {
+        config::global_scripts->Reload();
+        c->frame->StatusTimeout(_("Reloaded autoload Automation scripts"));
+    }
 };
 
 struct open_manager final : public Command {
-	CMD_NAME("am/manager")
-	CMD_ICON(automation_toolbutton)
-	STR_MENU("&Automation...")
-	STR_DISP("Automation")
-	STR_HELP("Open automation manager")
+    CMD_NAME("am/manager")
+    CMD_ICON(automation_toolbutton)
+    STR_MENU("&Automation...")
+    STR_DISP("Automation")
+    STR_HELP("Open automation manager")
 
-	void operator()(agi::Context *c) override {
-		ShowAutomationDialog(c);
-	}
+    void operator()(agi::Context *c) override {
+        ShowAutomationDialog(c);
+    }
 };
 
 struct meta final : public Command {
-	CMD_NAME("am/meta")
-	CMD_ICON(automation_toolbutton)
-	STR_MENU("&Automation...")
-	STR_DISP("Automation")
-	STR_HELP("Open automation manager. Ctrl: Rescan autoload folder. Ctrl+Shift: Rescan autoload folder and reload all automation scripts")
+    CMD_NAME("am/meta")
+    CMD_ICON(automation_toolbutton)
+    STR_MENU("&Automation...")
+    STR_DISP("Automation")
+    STR_HELP("Open automation manager. Ctrl: Rescan autoload folder. Ctrl+Shift: Rescan autoload folder and reload all automation scripts")
 
-	void operator()(agi::Context *c) override {
-		if (wxGetMouseState().CmdDown()) {
-			if (wxGetMouseState().ShiftDown())
-				cmd::call("am/reload", c);
-			else
-				cmd::call("am/reload/autoload", c);
-		}
-		else
-			cmd::call("am/manager", c);
-	}
+    void operator()(agi::Context *c) override {
+        if (wxGetMouseState().CmdDown()) {
+            if (wxGetMouseState().ShiftDown())
+                cmd::call("am/reload", c);
+            else
+                cmd::call("am/reload/autoload", c);
+        }
+        else
+            cmd::call("am/manager", c);
+    }
 };
 
 }
 
 namespace cmd {
-	void init_automation() {
-		reg(agi::make_unique<meta>());
-		reg(agi::make_unique<open_manager>());
-		reg(agi::make_unique<reload_all>());
-		reg(agi::make_unique<reload_autoload>());
-	}
+void init_automation()
+{
+    reg(agi::make_unique<meta>());
+    reg(agi::make_unique<open_manager>());
+    reg(agi::make_unique<reload_all>());
+    reg(agi::make_unique<reload_autoload>());
+}
 }
diff --git a/src/command/command.cpp b/src/command/command.cpp
index edc041b6a25ea62341de8b7e9a7de1f6fd1a8b9e..cc19d8064ef191965a3cb457a5a033db11669ccf 100644
--- a/src/command/command.cpp
+++ b/src/command/command.cpp
@@ -22,79 +22,87 @@
 #include <wx/intl.h>
 
 namespace cmd {
-	static std::map<std::string, std::unique_ptr<Command>> cmd_map;
-	typedef std::map<std::string, std::unique_ptr<Command>>::iterator iterator;
+static std::map<std::string, std::unique_ptr<Command>> cmd_map;
+typedef std::map<std::string, std::unique_ptr<Command>>::iterator iterator;
 
-	static iterator find_command(std::string const& name) {
-		auto it = cmd_map.find(name);
-		if (it == cmd_map.end())
-			throw CommandNotFound(agi::format(_("'%s' is not a valid command name"), name));
-		return it;
-	}
+static iterator find_command(std::string const &name)
+{
+    auto it = cmd_map.find(name);
+    if (it == cmd_map.end())
+        throw CommandNotFound(agi::format(_("'%s' is not a valid command name"), name));
+    return it;
+}
 
-	void reg(std::unique_ptr<Command> cmd) {
-		cmd_map[cmd->name()] = std::move(cmd);
-	}
+void reg(std::unique_ptr<Command> cmd)
+{
+    cmd_map[cmd->name()] = std::move(cmd);
+}
 
-	void unreg(std::string const& name) {
-		cmd_map.erase(find_command(name));
-	}
+void unreg(std::string const &name)
+{
+    cmd_map.erase(find_command(name));
+}
 
-	Command *get(std::string const& name) {
-		return find_command(name)->second.get();
-	}
+Command *get(std::string const &name)
+{
+    return find_command(name)->second.get();
+}
 
-	void call(std::string const& name, agi::Context*c) {
-		Command &cmd = *find_command(name)->second;
-		if (cmd.Validate(c))
-			cmd(c);
-	}
+void call(std::string const &name, agi::Context *c)
+{
+    Command &cmd = *find_command(name)->second;
+    if (cmd.Validate(c))
+        cmd(c);
+}
 
-	std::vector<std::string> get_registered_commands() {
-		std::vector<std::string> ret;
-		ret.reserve(cmd_map.size());
-		for (auto const& it : cmd_map)
-			ret.push_back(it.first);
-		return ret;
-	}
+std::vector<std::string> get_registered_commands()
+{
+    std::vector<std::string> ret;
+    ret.reserve(cmd_map.size());
+    for (auto const &it : cmd_map)
+        ret.push_back(it.first);
+    return ret;
+}
 
-	// These forward declarations exist here since we don't want to expose
-	// them in a header, they're strictly internal-use.
-	void init_app();
-	void init_audio();
-	void init_automation();
-	void init_command();
-	void init_edit();
-	void init_grid();
-	void init_help();
-	void init_keyframe();
-	void init_recent();
-	void init_subtitle();
-	void init_time();
-	void init_timecode();
-	void init_tool();
-	void init_video();
-	void init_visual_tools();
+// These forward declarations exist here since we don't want to expose
+// them in a header, they're strictly internal-use.
+void init_app();
+void init_audio();
+void init_automation();
+void init_command();
+void init_edit();
+void init_grid();
+void init_help();
+void init_keyframe();
+void init_recent();
+void init_subtitle();
+void init_time();
+void init_timecode();
+void init_tool();
+void init_video();
+void init_visual_tools();
 
-	void init_builtin_commands() {
-		LOG_D("command/init") << "Populating command map";
-		init_app();
-		init_audio();
-		init_automation();
-		init_edit();
-		init_grid();
-		init_help();
-		init_keyframe();
-		init_recent();
-		init_subtitle();
-		init_time();
-		init_timecode();
-		init_tool();
-		init_video();
-		init_visual_tools();
-	}
+void init_builtin_commands()
+{
+    LOG_D("command/init") << "Populating command map";
+    init_app();
+    init_audio();
+    init_automation();
+    init_edit();
+    init_grid();
+    init_help();
+    init_keyframe();
+    init_recent();
+    init_subtitle();
+    init_time();
+    init_timecode();
+    init_tool();
+    init_video();
+    init_visual_tools();
+}
 
-	void clear() {
-		cmd_map.clear();
-	}
+void clear()
+{
+    cmd_map.clear();
+}
 }
diff --git a/src/command/command.h b/src/command/command.h
index 38ea38f8af7354084a304315d9c10c0f571fd1d6..23b6c075b3ba7c96cc57a5eec2e4a6faa8e2611c 100644
--- a/src/command/command.h
+++ b/src/command/command.h
@@ -56,112 +56,112 @@ namespace cmd {
 DEFINE_EXCEPTION(CommandError, agi::Exception);
 DEFINE_EXCEPTION(CommandNotFound, CommandError);
 
-	enum CommandFlags {
-		/// Default command type
-		COMMAND_NORMAL       = 0,
-
-		/// Invoking this command toggles a setting of some sort. Any command
-		/// of this type should have IsActive implemented to signal the
-		/// current state of the thing being toggled, and invoking the command
-		/// twice should be a no-op
-		///
-		/// This is mutually exclusive with COMMAND_RADIO
-		COMMAND_TOGGLE       = 1,
-
-		/// Invoking this command sets a setting to a specific value. Any
-		/// command of this type should have IsActive implemented, and if
-		/// IsActive returns true, invoking the command should have no effect
-		///
-		/// This is mutually exclusive with COMMAND_TOGGLE
-		COMMAND_RADIO        = 2,
-
-		/// This command has an overridden Validate method
-		COMMAND_VALIDATE     = 4,
-
-		/// This command's name may change based on the state of the project
-		COMMAND_DYNAMIC_NAME = 8,
-
-		/// This command's help string may change
-		COMMAND_DYNAMIC_HELP = 16,
-
-		/// This command's icon may change based on the state of the project
-		COMMAND_DYNAMIC_ICON = 32
-	};
-
-	/// Holds an individual Command
-	class Command {
-	public:
-		/// Command name
-		virtual const char* name() const=0;
-		/// String for menu purposes including accelerators, but not hotkeys
-		virtual wxString StrMenu(const agi::Context *) const=0;
-		/// Plain string for display purposes; should normally be the same as StrMenu
-		/// but without accelerators
-		virtual wxString StrDisplay(const agi::Context *) const=0;
-		/// Short help string describing what the command does
-		virtual wxString StrHelp() const=0;
-
-		/// Get this command's type flags
-		/// @return Bitmask of CommandFlags
-		virtual int Type() const { return COMMAND_NORMAL; }
-
-		/// Request icon.
-		/// @param size Icon size.
-		virtual wxBitmap Icon(int size, wxLayoutDirection = wxLayout_LeftToRight) const { return wxBitmap{}; }
-
-		/// Command function
-		virtual void operator()(agi::Context *c)=0;
-
-		/// Check whether or not it makes sense to call this command at this time
-		/// @param c Project context
-		///
-		/// This function should be very fast, as it is called whenever a menu
-		/// containing this command is opened and is called periodically for
-		/// any commands used in a toolbar
-		///
-		/// Note that it is still legal to call commands when this returns
-		/// false. In this situation, commands should do nothing.
-		///
-		/// This function should be overridden iff the command's type flags
-		/// include COMMAND_VALIDATE
-		virtual bool Validate(const agi::Context *c) { return true; }
-
-		/// Is the selectable value represented by this command currently selected?
-		/// @param c Project context
-		///
-		/// As with Validate, this function should be very fast.
-		///
-		/// This function should be overridden iff the command's type flags
-		/// include COMMAND_TOGGLE or COMMAND_RADIO
-		virtual bool IsActive(const agi::Context *c) { return false; }
-
-		/// Destructor
-		virtual ~Command() = default;
-	};
-
-	/// Init all builtin commands.
-	void init_builtin_commands();
-
-	/// Register a command.
-	/// @param cmd Command object to register.
-	void reg(std::unique_ptr<Command> cmd);
-
-	/// Unregister a command.
-	/// @param cmd Command name to unregister. The associated command object is deleted.
-	void unreg(std::string const& name);
-
-	/// Call a command.
-	/// @param name Name of the command to call.
-	/// @param c  Current Context.
-	void call(std::string const& name, agi::Context *c);
-
-	/// Retrieve a Command object.
-	/// @param Command object.
-	Command* get(std::string const& name);
-
-	/// Get a list of registered command names
-	std::vector<std::string> get_registered_commands();
-
-	/// Unregister and deletes all commands
-	void clear();
+enum CommandFlags {
+    /// Default command type
+    COMMAND_NORMAL       = 0,
+
+    /// Invoking this command toggles a setting of some sort. Any command
+    /// of this type should have IsActive implemented to signal the
+    /// current state of the thing being toggled, and invoking the command
+    /// twice should be a no-op
+    ///
+    /// This is mutually exclusive with COMMAND_RADIO
+    COMMAND_TOGGLE       = 1,
+
+    /// Invoking this command sets a setting to a specific value. Any
+    /// command of this type should have IsActive implemented, and if
+    /// IsActive returns true, invoking the command should have no effect
+    ///
+    /// This is mutually exclusive with COMMAND_TOGGLE
+    COMMAND_RADIO        = 2,
+
+    /// This command has an overridden Validate method
+    COMMAND_VALIDATE     = 4,
+
+    /// This command's name may change based on the state of the project
+    COMMAND_DYNAMIC_NAME = 8,
+
+    /// This command's help string may change
+    COMMAND_DYNAMIC_HELP = 16,
+
+    /// This command's icon may change based on the state of the project
+    COMMAND_DYNAMIC_ICON = 32
+};
+
+/// Holds an individual Command
+class Command {
+public:
+    /// Command name
+    virtual const char *name() const = 0;
+    /// String for menu purposes including accelerators, but not hotkeys
+    virtual wxString StrMenu(const agi::Context *) const = 0;
+    /// Plain string for display purposes; should normally be the same as StrMenu
+    /// but without accelerators
+    virtual wxString StrDisplay(const agi::Context *) const = 0;
+    /// Short help string describing what the command does
+    virtual wxString StrHelp() const = 0;
+
+    /// Get this command's type flags
+    /// @return Bitmask of CommandFlags
+    virtual int Type() const { return COMMAND_NORMAL; }
+
+    /// Request icon.
+    /// @param size Icon size.
+    virtual wxBitmap Icon(int size, wxLayoutDirection = wxLayout_LeftToRight) const { return wxBitmap{}; }
+
+    /// Command function
+    virtual void operator()(agi::Context *c) = 0;
+
+    /// Check whether or not it makes sense to call this command at this time
+    /// @param c Project context
+    ///
+    /// This function should be very fast, as it is called whenever a menu
+    /// containing this command is opened and is called periodically for
+    /// any commands used in a toolbar
+    ///
+    /// Note that it is still legal to call commands when this returns
+    /// false. In this situation, commands should do nothing.
+    ///
+    /// This function should be overridden iff the command's type flags
+    /// include COMMAND_VALIDATE
+    virtual bool Validate(const agi::Context *c) { return true; }
+
+    /// Is the selectable value represented by this command currently selected?
+    /// @param c Project context
+    ///
+    /// As with Validate, this function should be very fast.
+    ///
+    /// This function should be overridden iff the command's type flags
+    /// include COMMAND_TOGGLE or COMMAND_RADIO
+    virtual bool IsActive(const agi::Context *c) { return false; }
+
+    /// Destructor
+    virtual ~Command() = default;
+};
+
+/// Init all builtin commands.
+void init_builtin_commands();
+
+/// Register a command.
+/// @param cmd Command object to register.
+void reg(std::unique_ptr<Command> cmd);
+
+/// Unregister a command.
+/// @param cmd Command name to unregister. The associated command object is deleted.
+void unreg(std::string const &name);
+
+/// Call a command.
+/// @param name Name of the command to call.
+/// @param c  Current Context.
+void call(std::string const &name, agi::Context *c);
+
+/// Retrieve a Command object.
+/// @param Command object.
+Command *get(std::string const &name);
+
+/// Get a list of registered command names
+std::vector<std::string> get_registered_commands();
+
+/// Unregister and deletes all commands
+void clear();
 } // namespace cmd
diff --git a/src/command/edit.cpp b/src/command/edit.cpp
index 333cdc6d5decdf00b29a4575923f75488fb9fc03..27daec8b8ad2aac13439395939936d6a60381f78 100644
--- a/src/command/edit.cpp
+++ b/src/command/edit.cpp
@@ -70,1236 +70,1257 @@
 #include <wx/textentry.h>
 
 namespace {
-	using namespace boost::adaptors;
-	using cmd::Command;
+using namespace boost::adaptors;
+using cmd::Command;
 
 struct validate_sel_nonempty : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() > 0;
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() > 0;
+    }
 };
 
 struct validate_video_and_sel_nonempty : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return c->project->VideoProvider() && !c->selectionController->GetSelectedSet().empty();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return c->project->VideoProvider() && !c->selectionController->GetSelectedSet().empty();
+    }
 };
 
 struct validate_sel_multiple : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() > 1;
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() > 1;
+    }
 };
 
 template<typename String>
-AssDialogue *get_dialogue(String data) {
-	boost::trim(data);
-	try {
-		// Try to interpret the line as an ASS line
-		return new AssDialogue(data);
-	}
-	catch (...) {
-		// Line didn't parse correctly, assume it's plain text that
-		// should be pasted in the Text field only
-		auto d = new AssDialogue;
-		d->End = 0;
-		d->Text = data;
-		return d;
-	}
+AssDialogue *get_dialogue(String data)
+{
+    boost::trim(data);
+    try {
+        // Try to interpret the line as an ASS line
+        return new AssDialogue(data);
+    }
+    catch (...) {
+        // Line didn't parse correctly, assume it's plain text that
+        // should be pasted in the Text field only
+        auto d = new AssDialogue;
+        d->End = 0;
+        d->Text = data;
+        return d;
+    }
 }
 
 template<typename Paster>
-void paste_lines(agi::Context *c, bool paste_over, Paster&& paste_line) {
-	std::string data = GetClipboard();
-	if (data.empty()) return;
-
-	AssDialogue *first = nullptr;
-	Selection newsel;
-
-	boost::char_separator<char> sep("\r\n");
-	for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
-		AssDialogue *inserted = paste_line(get_dialogue(curdata));
-		if (!inserted)
-			break;
-
-		newsel.insert(inserted);
-		if (!first)
-			first = inserted;
-	}
-
-	if (first) {
-		c->ass->Commit(_("paste"), paste_over ? AssFile::COMMIT_DIAG_FULL : AssFile::COMMIT_DIAG_ADDREM);
-
-		if (!paste_over)
-			c->selectionController->SetSelectionAndActive(std::move(newsel), first);
-	}
+void paste_lines(agi::Context *c, bool paste_over, Paster &&paste_line)
+{
+    std::string data = GetClipboard();
+    if (data.empty()) return;
+
+    AssDialogue *first = nullptr;
+    Selection newsel;
+
+    boost::char_separator<char> sep("\r\n");
+    for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
+        AssDialogue *inserted = paste_line(get_dialogue(curdata));
+        if (!inserted)
+            break;
+
+        newsel.insert(inserted);
+        if (!first)
+            first = inserted;
+    }
+
+    if (first) {
+        c->ass->Commit(_("paste"), paste_over ? AssFile::COMMIT_DIAG_FULL : AssFile::COMMIT_DIAG_ADDREM);
+
+        if (!paste_over)
+            c->selectionController->SetSelectionAndActive(std::move(newsel), first);
+    }
 }
 
-AssDialogue *paste_over(wxWindow *parent, std::vector<bool>& pasteOverOptions, AssDialogue *new_line, AssDialogue *old_line) {
-	if (pasteOverOptions.empty()) {
-		if (!ShowPasteOverDialog(parent)) return nullptr;
-		pasteOverOptions = OPT_GET("Tool/Paste Lines Over/Fields")->GetListBool();
-	}
-
-	if (pasteOverOptions[0])  old_line->Comment   = new_line->Comment;
-	if (pasteOverOptions[1])  old_line->Layer     = new_line->Layer;
-	if (pasteOverOptions[2])  old_line->Start     = new_line->Start;
-	if (pasteOverOptions[3])  old_line->End       = new_line->End;
-	if (pasteOverOptions[4])  old_line->Style     = new_line->Style;
-	if (pasteOverOptions[5])  old_line->Actor     = new_line->Actor;
-	if (pasteOverOptions[6])  old_line->Margin[0] = new_line->Margin[0];
-	if (pasteOverOptions[7])  old_line->Margin[1] = new_line->Margin[1];
-	if (pasteOverOptions[8])  old_line->Margin[2] = new_line->Margin[2];
-	if (pasteOverOptions[9])  old_line->Effect    = new_line->Effect;
-	if (pasteOverOptions[10]) old_line->Text      = new_line->Text;
-
-	return old_line;
+AssDialogue *paste_over(wxWindow *parent, std::vector<bool> &pasteOverOptions, AssDialogue *new_line, AssDialogue *old_line)
+{
+    if (pasteOverOptions.empty()) {
+        if (!ShowPasteOverDialog(parent)) return nullptr;
+        pasteOverOptions = OPT_GET("Tool/Paste Lines Over/Fields")->GetListBool();
+    }
+
+    if (pasteOverOptions[0])  old_line->Comment   = new_line->Comment;
+    if (pasteOverOptions[1])  old_line->Layer     = new_line->Layer;
+    if (pasteOverOptions[2])  old_line->Start     = new_line->Start;
+    if (pasteOverOptions[3])  old_line->End       = new_line->End;
+    if (pasteOverOptions[4])  old_line->Style     = new_line->Style;
+    if (pasteOverOptions[5])  old_line->Actor     = new_line->Actor;
+    if (pasteOverOptions[6])  old_line->Margin[0] = new_line->Margin[0];
+    if (pasteOverOptions[7])  old_line->Margin[1] = new_line->Margin[1];
+    if (pasteOverOptions[8])  old_line->Margin[2] = new_line->Margin[2];
+    if (pasteOverOptions[9])  old_line->Effect    = new_line->Effect;
+    if (pasteOverOptions[10]) old_line->Text      = new_line->Text;
+
+    return old_line;
 }
 
 struct parsed_line {
-	AssDialogue *line;
-	std::vector<std::unique_ptr<AssDialogueBlock>> blocks;
-
-	parsed_line(AssDialogue *line) : line(line), blocks(line->ParseTags()) { }
-	parsed_line(parsed_line&& r) = default;
-
-	const AssOverrideTag *find_tag(int blockn, std::string const& tag_name, std::string const& alt) const {
-		for (auto ovr : blocks | sliced(0, blockn + 1) | reversed | agi::of_type<AssDialogueBlockOverride>()) {
-			for (auto const& tag : ovr->Tags | reversed) {
-				if (tag.Name == tag_name || tag.Name == alt)
-					return &tag;
-			}
-		}
-		return nullptr;
-	}
-
-	template<typename T>
-	T get_value(int blockn, T initial, std::string const& tag_name, std::string const& alt = "") const {
-		auto tag = find_tag(blockn, tag_name, alt);
-		if (tag)
-			return tag->Params[0].template Get<T>(initial);
-		return initial;
-	}
-
-	int block_at_pos(int pos) const {
-		auto const& text = line->Text.get();
-		int n = 0;
-		int max = text.size() - 1;
-		bool in_block = false;
-
-		for (int i = 0; i <= max; ++i) {
-			if (text[i] == '{') {
-				if (!in_block && i > 0 && pos >= 0)
-					++n;
-				in_block = true;
-			}
-			else if (text[i] == '}' && in_block) {
-				in_block = false;
-				if (pos > 0 && (i + 1 == max || text[i + 1] != '{'))
-					n++;
-			}
-			else if (!in_block) {
-				if (--pos == 0)
-					return n + (i < max && text[i + 1] == '{');
-			}
-		}
-
-		return n - in_block;
-	}
-
-	int set_tag(std::string const& tag, std::string const& value, int norm_pos, int orig_pos) {
-		int blockn = block_at_pos(norm_pos);
-
-		AssDialogueBlockPlain *plain = nullptr;
-		AssDialogueBlockOverride *ovr = nullptr;
-		while (blockn >= 0 && !plain && !ovr) {
-			AssDialogueBlock *block = blocks[blockn].get();
-			switch (block->GetType()) {
-			case AssBlockType::PLAIN:
-				plain = static_cast<AssDialogueBlockPlain *>(block);
-				break;
-			case AssBlockType::DRAWING:
-				--blockn;
-				break;
-			case AssBlockType::COMMENT:
-				--blockn;
-				orig_pos = line->Text.get().rfind('{', orig_pos);
-				break;
-			case AssBlockType::OVERRIDE:
-				ovr = static_cast<AssDialogueBlockOverride*>(block);
-				break;
-			}
-		}
-
-		// If we didn't hit a suitable block for inserting the override just put
-		// it at the beginning of the line
-		if (blockn < 0)
-			orig_pos = 0;
-
-		std::string insert(tag + value);
-		int shift = insert.size();
-		if (plain || blockn < 0) {
-			line->Text = line->Text.get().substr(0, orig_pos) + "{" + insert + "}" + line->Text.get().substr(orig_pos);
-			shift += 2;
-			blocks = line->ParseTags();
-		}
-		else if (ovr) {
-			std::string alt;
-			if (tag == "\\c") alt = "\\1c";
-			// Remove old of same
-			bool found = false;
-			for (size_t i = 0; i < ovr->Tags.size(); i++) {
-				std::string const& name = ovr->Tags[i].Name;
-				if (tag == name || alt == name) {
-					shift -= ((std::string)ovr->Tags[i]).size();
-					if (found) {
-						ovr->Tags.erase(ovr->Tags.begin() + i);
-						i--;
-					}
-					else {
-						ovr->Tags[i].Params[0].Set(value);
-						found = true;
-					}
-				}
-			}
-			if (!found)
-				ovr->AddTag(insert);
-
-			line->UpdateText(blocks);
-		}
-		else
-			assert(false);
-
-		return shift;
-	}
+    AssDialogue *line;
+    std::vector<std::unique_ptr<AssDialogueBlock>> blocks;
+
+    parsed_line(AssDialogue *line) : line(line), blocks(line->ParseTags()) { }
+    parsed_line(parsed_line &&r) = default;
+
+    const AssOverrideTag *find_tag(int blockn, std::string const &tag_name, std::string const &alt) const {
+        for (auto ovr : blocks | sliced(0, blockn + 1) | reversed | agi::of_type<AssDialogueBlockOverride>()) {
+            for (auto const &tag : ovr->Tags | reversed) {
+                if (tag.Name == tag_name || tag.Name == alt)
+                    return &tag;
+            }
+        }
+        return nullptr;
+    }
+
+    template<typename T>
+    T get_value(int blockn, T initial, std::string const &tag_name, std::string const &alt = "") const {
+        auto tag = find_tag(blockn, tag_name, alt);
+        if (tag)
+            return tag->Params[0].template Get<T>(initial);
+        return initial;
+    }
+
+    int block_at_pos(int pos) const {
+        auto const &text = line->Text.get();
+        int n = 0;
+        int max = text.size() - 1;
+        bool in_block = false;
+
+        for (int i = 0; i <= max; ++i) {
+            if (text[i] == '{') {
+                if (!in_block && i > 0 && pos >= 0)
+                    ++n;
+                in_block = true;
+            }
+            else if (text[i] == '}' && in_block) {
+                in_block = false;
+                if (pos > 0 && (i + 1 == max || text[i + 1] != '{'))
+                    n++;
+            }
+            else if (!in_block) {
+                if (--pos == 0)
+                    return n + (i < max && text[i + 1] == '{');
+            }
+        }
+
+        return n - in_block;
+    }
+
+    int set_tag(std::string const &tag, std::string const &value, int norm_pos, int orig_pos) {
+        int blockn = block_at_pos(norm_pos);
+
+        AssDialogueBlockPlain *plain = nullptr;
+        AssDialogueBlockOverride *ovr = nullptr;
+        while (blockn >= 0 && !plain && !ovr) {
+            AssDialogueBlock *block = blocks[blockn].get();
+            switch (block->GetType()) {
+            case AssBlockType::PLAIN:
+                plain = static_cast<AssDialogueBlockPlain *>(block);
+                break;
+            case AssBlockType::DRAWING:
+                --blockn;
+                break;
+            case AssBlockType::COMMENT:
+                --blockn;
+                orig_pos = line->Text.get().rfind('{', orig_pos);
+                break;
+            case AssBlockType::OVERRIDE:
+                ovr = static_cast<AssDialogueBlockOverride *>(block);
+                break;
+            }
+        }
+
+        // If we didn't hit a suitable block for inserting the override just put
+        // it at the beginning of the line
+        if (blockn < 0)
+            orig_pos = 0;
+
+        std::string insert(tag + value);
+        int shift = insert.size();
+        if (plain || blockn < 0) {
+            line->Text = line->Text.get().substr(0, orig_pos) + "{" + insert + "}" + line->Text.get().substr(orig_pos);
+            shift += 2;
+            blocks = line->ParseTags();
+        }
+        else if (ovr) {
+            std::string alt;
+            if (tag == "\\c") alt = "\\1c";
+            // Remove old of same
+            bool found = false;
+            for (size_t i = 0; i < ovr->Tags.size(); i++) {
+                std::string const &name = ovr->Tags[i].Name;
+                if (tag == name || alt == name) {
+                    shift -= ((std::string)ovr->Tags[i]).size();
+                    if (found) {
+                        ovr->Tags.erase(ovr->Tags.begin() + i);
+                        i--;
+                    }
+                    else {
+                        ovr->Tags[i].Params[0].Set(value);
+                        found = true;
+                    }
+                }
+            }
+            if (!found)
+                ovr->AddTag(insert);
+
+            line->UpdateText(blocks);
+        }
+        else
+            assert(false);
+
+        return shift;
+    }
 };
 
-int normalize_pos(std::string const& text, int pos) {
-	int plain_len = 0;
-	bool in_block = false;
-
-	for (int i = 0, max = text.size() - 1; i < pos && i <= max; ++i) {
-		if (text[i] == '{')
-			in_block = true;
-		if (!in_block)
-			++plain_len;
-		if (text[i] == '}' && in_block)
-			in_block = false;
-	}
-
-	return plain_len;
+int normalize_pos(std::string const &text, int pos)
+{
+    int plain_len = 0;
+    bool in_block = false;
+
+    for (int i = 0, max = text.size() - 1; i < pos && i <= max; ++i) {
+        if (text[i] == '{')
+            in_block = true;
+        if (!in_block)
+            ++plain_len;
+        if (text[i] == '}' && in_block)
+            in_block = false;
+    }
+
+    return plain_len;
 }
 
 template<typename Func>
-void update_lines(const agi::Context *c, wxString const& undo_msg, Func&& f) {
-	const auto active_line = c->selectionController->GetActiveLine();
-	const int sel_start = c->textSelectionController->GetSelectionStart();
-	const int sel_end = c->textSelectionController->GetSelectionEnd();
-	const int norm_sel_start = normalize_pos(active_line->Text, sel_start);
-	const int norm_sel_end = normalize_pos(active_line->Text, sel_end);
-	int active_sel_shift = 0;
-
-	for (const auto line : c->selectionController->GetSelectedSet()) {
-		int shift = f(line, sel_start, sel_end, norm_sel_start, norm_sel_end);
-		if (line == active_line)
-			active_sel_shift = shift;
-	}
-
-	auto const& sel = c->selectionController->GetSelectedSet();
-	c->ass->Commit(undo_msg, AssFile::COMMIT_DIAG_TEXT, -1, sel.size() == 1 ? *sel.begin() : nullptr);
-	if (active_sel_shift != 0)
-		c->textSelectionController->SetSelection(sel_start + active_sel_shift, sel_end + active_sel_shift);
+void update_lines(const agi::Context *c, wxString const &undo_msg, Func &&f)
+{
+    const auto active_line = c->selectionController->GetActiveLine();
+    const int sel_start = c->textSelectionController->GetSelectionStart();
+    const int sel_end = c->textSelectionController->GetSelectionEnd();
+    const int norm_sel_start = normalize_pos(active_line->Text, sel_start);
+    const int norm_sel_end = normalize_pos(active_line->Text, sel_end);
+    int active_sel_shift = 0;
+
+    for (const auto line : c->selectionController->GetSelectedSet()) {
+        int shift = f(line, sel_start, sel_end, norm_sel_start, norm_sel_end);
+        if (line == active_line)
+            active_sel_shift = shift;
+    }
+
+    auto const &sel = c->selectionController->GetSelectedSet();
+    c->ass->Commit(undo_msg, AssFile::COMMIT_DIAG_TEXT, -1, sel.size() == 1 ? *sel.begin() : nullptr);
+    if (active_sel_shift != 0)
+        c->textSelectionController->SetSelection(sel_start + active_sel_shift, sel_end + active_sel_shift);
 }
 
-void toggle_override_tag(const agi::Context *c, bool (AssStyle::*field), const char *tag, wxString const& undo_msg) {
-	update_lines(c, undo_msg, [&](AssDialogue *line, int sel_start, int sel_end, int norm_sel_start, int norm_sel_end) {
-		AssStyle const* const style = c->ass->GetStyle(line->Style);
-		bool state = style ? style->*field : AssStyle().*field;
+void toggle_override_tag(const agi::Context *c, bool (AssStyle::*field), const char *tag, wxString const &undo_msg)
+{
+    update_lines(c, undo_msg, [&](AssDialogue * line, int sel_start, int sel_end, int norm_sel_start, int norm_sel_end) {
+        AssStyle const *const style = c->ass->GetStyle(line->Style);
+        bool state = style ? style->*field : AssStyle().*field;
 
-		parsed_line parsed(line);
-		int blockn = parsed.block_at_pos(norm_sel_start);
+        parsed_line parsed(line);
+        int blockn = parsed.block_at_pos(norm_sel_start);
 
-		state = parsed.get_value(blockn, state, tag);
+        state = parsed.get_value(blockn, state, tag);
 
-		int shift = parsed.set_tag(tag, state ? "0" : "1", norm_sel_start, sel_start);
-		if (sel_start != sel_end)
-			parsed.set_tag(tag, state ? "1" : "0", norm_sel_end, sel_end + shift);
-		return shift;
-	});
+        int shift = parsed.set_tag(tag, state ? "0" : "1", norm_sel_start, sel_start);
+        if (sel_start != sel_end)
+            parsed.set_tag(tag, state ? "1" : "0", norm_sel_end, sel_end + shift);
+        return shift;
+    });
 }
 
-void show_color_picker(const agi::Context *c, agi::Color (AssStyle::*field), const char *tag, const char *alt, const char *alpha) {
-	agi::Color initial_color;
-	const auto active_line = c->selectionController->GetActiveLine();
-	const int sel_start = c->textSelectionController->GetSelectionStart();
-	const int sel_end = c->textSelectionController->GetSelectionStart();
-	const int norm_sel_start = normalize_pos(active_line->Text, sel_start);
-
-	auto const& sel = c->selectionController->GetSelectedSet();
-	using line_info = std::pair<agi::Color, parsed_line>;
-	std::vector<line_info> lines;
-	for (auto line : sel) {
-		AssStyle const* const style = c->ass->GetStyle(line->Style);
-		agi::Color color = (style ? style->*field : AssStyle().*field);
-
-		parsed_line parsed(line);
-		int blockn = parsed.block_at_pos(norm_sel_start);
-
-		int a = parsed.get_value(blockn, (int)color.a, alpha, "\\alpha");
-		color = parsed.get_value(blockn, color, tag, alt);
-		color.a = a;
-
-		if (line == active_line)
-			initial_color = color;
-
-		lines.emplace_back(color, std::move(parsed));
-	}
-
-	int active_shift = 0;
-	int commit_id = -1;
-	bool ok = GetColorFromUser(c->parent, initial_color, true, [&](agi::Color new_color) {
-		for (auto& line : lines) {
-			int shift = line.second.set_tag(tag, new_color.GetAssOverrideFormatted(), norm_sel_start, sel_start);
-			if (new_color.a != line.first.a) {
-				shift += line.second.set_tag(alpha, agi::format("&H%02X&", (int)new_color.a), norm_sel_start, sel_start + shift);
-				line.first.a = new_color.a;
-			}
-
-			if (line.second.line == active_line)
-				active_shift = shift;
-		}
-
-		commit_id = c->ass->Commit(_("set color"), AssFile::COMMIT_DIAG_TEXT, commit_id, sel.size() == 1 ? *sel.begin() : nullptr);
-		if (active_shift)
-			c->textSelectionController->SetSelection(sel_start + active_shift, sel_start + active_shift);
-	});
-
-	if (!ok && commit_id != -1) {
-		c->subsController->Undo();
-		c->textSelectionController->SetSelection(sel_start, sel_end);
-	}
+void show_color_picker(const agi::Context *c, agi::Color (AssStyle::*field), const char *tag, const char *alt, const char *alpha)
+{
+    agi::Color initial_color;
+    const auto active_line = c->selectionController->GetActiveLine();
+    const int sel_start = c->textSelectionController->GetSelectionStart();
+    const int sel_end = c->textSelectionController->GetSelectionStart();
+    const int norm_sel_start = normalize_pos(active_line->Text, sel_start);
+
+    auto const &sel = c->selectionController->GetSelectedSet();
+    using line_info = std::pair<agi::Color, parsed_line>;
+    std::vector<line_info> lines;
+    for (auto line : sel) {
+        AssStyle const *const style = c->ass->GetStyle(line->Style);
+        agi::Color color = (style ? style->*field : AssStyle().*field);
+
+        parsed_line parsed(line);
+        int blockn = parsed.block_at_pos(norm_sel_start);
+
+        int a = parsed.get_value(blockn, (int)color.a, alpha, "\\alpha");
+        color = parsed.get_value(blockn, color, tag, alt);
+        color.a = a;
+
+        if (line == active_line)
+            initial_color = color;
+
+        lines.emplace_back(color, std::move(parsed));
+    }
+
+    int active_shift = 0;
+    int commit_id = -1;
+    bool ok = GetColorFromUser(c->parent, initial_color, true, [&](agi::Color new_color) {
+        for (auto &line : lines) {
+            int shift = line.second.set_tag(tag, new_color.GetAssOverrideFormatted(), norm_sel_start, sel_start);
+            if (new_color.a != line.first.a) {
+                shift += line.second.set_tag(alpha, agi::format("&H%02X&", (int)new_color.a), norm_sel_start, sel_start + shift);
+                line.first.a = new_color.a;
+            }
+
+            if (line.second.line == active_line)
+                active_shift = shift;
+        }
+
+        commit_id = c->ass->Commit(_("set color"), AssFile::COMMIT_DIAG_TEXT, commit_id, sel.size() == 1 ? *sel.begin() : nullptr);
+        if (active_shift)
+            c->textSelectionController->SetSelection(sel_start + active_shift, sel_start + active_shift);
+    });
+
+    if (!ok && commit_id != -1) {
+        c->subsController->Undo();
+        c->textSelectionController->SetSelection(sel_start, sel_end);
+    }
 }
 
 struct edit_color_primary final : public Command {
-	CMD_NAME("edit/color/primary")
-	CMD_ICON(button_color_one)
-	STR_MENU("Primary Color...")
-	STR_DISP("Primary Color")
-	STR_HELP("Set the primary fill color (\\c) at the cursor position")
-
-	void operator()(agi::Context *c) override {
-		show_color_picker(c, &AssStyle::primary, "\\c", "\\1c", "\\1a");
-	}
+    CMD_NAME("edit/color/primary")
+    CMD_ICON(button_color_one)
+    STR_MENU("Primary Color...")
+    STR_DISP("Primary Color")
+    STR_HELP("Set the primary fill color (\\c) at the cursor position")
+
+    void operator()(agi::Context *c) override {
+        show_color_picker(c, &AssStyle::primary, "\\c", "\\1c", "\\1a");
+    }
 };
 
 struct edit_color_secondary final : public Command {
-	CMD_NAME("edit/color/secondary")
-	CMD_ICON(button_color_two)
-	STR_MENU("Secondary Color...")
-	STR_DISP("Secondary Color")
-	STR_HELP("Set the secondary (karaoke) fill color (\\2c) at the cursor position")
-
-	void operator()(agi::Context *c) override {
-		show_color_picker(c, &AssStyle::secondary, "\\2c", "", "\\2a");
-	}
+    CMD_NAME("edit/color/secondary")
+    CMD_ICON(button_color_two)
+    STR_MENU("Secondary Color...")
+    STR_DISP("Secondary Color")
+    STR_HELP("Set the secondary (karaoke) fill color (\\2c) at the cursor position")
+
+    void operator()(agi::Context *c) override {
+        show_color_picker(c, &AssStyle::secondary, "\\2c", "", "\\2a");
+    }
 };
 
 struct edit_color_outline final : public Command {
-	CMD_NAME("edit/color/outline")
-	CMD_ICON(button_color_three)
-	STR_MENU("Outline Color...")
-	STR_DISP("Outline Color")
-	STR_HELP("Set the outline color (\\3c) at the cursor position")
-
-	void operator()(agi::Context *c) override {
-		show_color_picker(c, &AssStyle::outline, "\\3c", "", "\\3a");
-	}
+    CMD_NAME("edit/color/outline")
+    CMD_ICON(button_color_three)
+    STR_MENU("Outline Color...")
+    STR_DISP("Outline Color")
+    STR_HELP("Set the outline color (\\3c) at the cursor position")
+
+    void operator()(agi::Context *c) override {
+        show_color_picker(c, &AssStyle::outline, "\\3c", "", "\\3a");
+    }
 };
 
 struct edit_color_shadow final : public Command {
-	CMD_NAME("edit/color/shadow")
-	CMD_ICON(button_color_four)
-	STR_MENU("Shadow Color...")
-	STR_DISP("Shadow Color")
-	STR_HELP("Set the shadow color (\\4c) at the cursor position")
-
-	void operator()(agi::Context *c) override {
-		show_color_picker(c, &AssStyle::shadow, "\\4c", "", "\\4a");
-	}
+    CMD_NAME("edit/color/shadow")
+    CMD_ICON(button_color_four)
+    STR_MENU("Shadow Color...")
+    STR_DISP("Shadow Color")
+    STR_HELP("Set the shadow color (\\4c) at the cursor position")
+
+    void operator()(agi::Context *c) override {
+        show_color_picker(c, &AssStyle::shadow, "\\4c", "", "\\4a");
+    }
 };
 
 struct edit_style_bold final : public Command {
-	CMD_NAME("edit/style/bold")
-	CMD_ICON(button_bold)
-	STR_MENU("Toggle Bold")
-	STR_DISP("Toggle Bold")
-	STR_HELP("Toggle bold (\\b) for the current selection or at the current cursor position")
-
-	void operator()(agi::Context *c) override {
-		toggle_override_tag(c, &AssStyle::bold, "\\b", _("toggle bold"));
-	}
+    CMD_NAME("edit/style/bold")
+    CMD_ICON(button_bold)
+    STR_MENU("Toggle Bold")
+    STR_DISP("Toggle Bold")
+    STR_HELP("Toggle bold (\\b) for the current selection or at the current cursor position")
+
+    void operator()(agi::Context *c) override {
+        toggle_override_tag(c, &AssStyle::bold, "\\b", _("toggle bold"));
+    }
 };
 
 struct edit_style_italic final : public Command {
-	CMD_NAME("edit/style/italic")
-	CMD_ICON(button_italics)
-	STR_MENU("Toggle Italics")
-	STR_DISP("Toggle Italics")
-	STR_HELP("Toggle italics (\\i) for the current selection or at the current cursor position")
-
-	void operator()(agi::Context *c) override {
-		toggle_override_tag(c, &AssStyle::italic, "\\i", _("toggle italic"));
-	}
+    CMD_NAME("edit/style/italic")
+    CMD_ICON(button_italics)
+    STR_MENU("Toggle Italics")
+    STR_DISP("Toggle Italics")
+    STR_HELP("Toggle italics (\\i) for the current selection or at the current cursor position")
+
+    void operator()(agi::Context *c) override {
+        toggle_override_tag(c, &AssStyle::italic, "\\i", _("toggle italic"));
+    }
 };
 
 struct edit_style_underline final : public Command {
-	CMD_NAME("edit/style/underline")
-	CMD_ICON(button_underline)
-	STR_MENU("Toggle Underline")
-	STR_DISP("Toggle Underline")
-	STR_HELP("Toggle underline (\\u) for the current selection or at the current cursor position")
-
-	void operator()(agi::Context *c) override {
-		toggle_override_tag(c, &AssStyle::underline, "\\u", _("toggle underline"));
-	}
+    CMD_NAME("edit/style/underline")
+    CMD_ICON(button_underline)
+    STR_MENU("Toggle Underline")
+    STR_DISP("Toggle Underline")
+    STR_HELP("Toggle underline (\\u) for the current selection or at the current cursor position")
+
+    void operator()(agi::Context *c) override {
+        toggle_override_tag(c, &AssStyle::underline, "\\u", _("toggle underline"));
+    }
 };
 
 struct edit_style_strikeout final : public Command {
-	CMD_NAME("edit/style/strikeout")
-	CMD_ICON(button_strikeout)
-	STR_MENU("Toggle Strikeout")
-	STR_DISP("Toggle Strikeout")
-	STR_HELP("Toggle strikeout (\\s) for the current selection or at the current cursor position")
-
-	void operator()(agi::Context *c) override {
-		toggle_override_tag(c, &AssStyle::strikeout, "\\s", _("toggle strikeout"));
-	}
+    CMD_NAME("edit/style/strikeout")
+    CMD_ICON(button_strikeout)
+    STR_MENU("Toggle Strikeout")
+    STR_DISP("Toggle Strikeout")
+    STR_HELP("Toggle strikeout (\\s) for the current selection or at the current cursor position")
+
+    void operator()(agi::Context *c) override {
+        toggle_override_tag(c, &AssStyle::strikeout, "\\s", _("toggle strikeout"));
+    }
 };
 
 struct edit_font final : public Command {
-	CMD_NAME("edit/font")
-	CMD_ICON(button_fontname)
-	STR_MENU("Font Face...")
-	STR_DISP("Font Face")
-	STR_HELP("Select a font face and size")
-
-	void operator()(agi::Context *c) override {
-		const parsed_line active(c->selectionController->GetActiveLine());
-		const int insertion_point = normalize_pos(active.line->Text, c->textSelectionController->GetInsertionPoint());
-
-		auto font_for_line = [&](parsed_line const& line) -> wxFont {
-			const int blockn = line.block_at_pos(insertion_point);
-
-			const AssStyle *style = c->ass->GetStyle(line.line->Style);
-			const AssStyle default_style;
-			if (!style)
-				style = &default_style;
-
-			return wxFont(
-				line.get_value(blockn, (int)style->fontsize, "\\fs"),
-				wxFONTFAMILY_DEFAULT,
-				line.get_value(blockn, style->italic, "\\i") ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
-				line.get_value(blockn, style->bold, "\\b") ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
-				line.get_value(blockn, style->underline, "\\u"),
-				to_wx(line.get_value(blockn, style->font, "\\fn")));
-		};
-
-		const wxFont initial = font_for_line(active);
-		const wxFont font = wxGetFontFromUser(c->parent, initial);
-		if (!font.Ok() || font == initial) return;
-
-		update_lines(c, _("set font"), [&](AssDialogue *line, int sel_start, int sel_end, int norm_sel_start, int norm_sel_end) {
-			parsed_line parsed(line);
-			const wxFont startfont = font_for_line(parsed);
-			int shift = 0;
-			auto do_set_tag = [&](const char *tag_name, std::string const& value) {
-				shift += parsed.set_tag(tag_name, value, norm_sel_start, sel_start + shift);
-			};
-
-			if (font.GetFaceName() != startfont.GetFaceName())
-				do_set_tag("\\fn", from_wx(font.GetFaceName()));
-			if (font.GetPointSize() != startfont.GetPointSize())
-				do_set_tag("\\fs", std::to_string(font.GetPointSize()));
-			if (font.GetWeight() != startfont.GetWeight())
-				do_set_tag("\\b", std::to_string(font.GetWeight() == wxFONTWEIGHT_BOLD));
-			if (font.GetStyle() != startfont.GetStyle())
-				do_set_tag("\\i", std::to_string(font.GetStyle() == wxFONTSTYLE_ITALIC));
-			if (font.GetUnderlined() != startfont.GetUnderlined())
-				do_set_tag("\\i", std::to_string(font.GetUnderlined()));
-
-			return shift;
-		});
-	}
+    CMD_NAME("edit/font")
+    CMD_ICON(button_fontname)
+    STR_MENU("Font Face...")
+    STR_DISP("Font Face")
+    STR_HELP("Select a font face and size")
+
+    void operator()(agi::Context *c) override {
+        const parsed_line active(c->selectionController->GetActiveLine());
+        const int insertion_point = normalize_pos(active.line->Text, c->textSelectionController->GetInsertionPoint());
+
+        auto font_for_line = [&](parsed_line const & line) -> wxFont {
+            const int blockn = line.block_at_pos(insertion_point);
+
+            const AssStyle *style = c->ass->GetStyle(line.line->Style);
+            const AssStyle default_style;
+            if (!style)
+                style = &default_style;
+
+            return wxFont(
+                line.get_value(blockn, (int)style->fontsize, "\\fs"),
+                wxFONTFAMILY_DEFAULT,
+                line.get_value(blockn, style->italic, "\\i") ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
+                line.get_value(blockn, style->bold, "\\b") ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
+                line.get_value(blockn, style->underline, "\\u"),
+                to_wx(line.get_value(blockn, style->font, "\\fn")));
+        };
+
+        const wxFont initial = font_for_line(active);
+        const wxFont font = wxGetFontFromUser(c->parent, initial);
+        if (!font.Ok() || font == initial) return;
+
+        update_lines(c, _("set font"), [&](AssDialogue * line, int sel_start, int sel_end, int norm_sel_start, int norm_sel_end) {
+            parsed_line parsed(line);
+            const wxFont startfont = font_for_line(parsed);
+            int shift = 0;
+            auto do_set_tag = [&](const char *tag_name, std::string const & value) {
+                shift += parsed.set_tag(tag_name, value, norm_sel_start, sel_start + shift);
+            };
+
+            if (font.GetFaceName() != startfont.GetFaceName())
+                do_set_tag("\\fn", from_wx(font.GetFaceName()));
+            if (font.GetPointSize() != startfont.GetPointSize())
+                do_set_tag("\\fs", std::to_string(font.GetPointSize()));
+            if (font.GetWeight() != startfont.GetWeight())
+                do_set_tag("\\b", std::to_string(font.GetWeight() == wxFONTWEIGHT_BOLD));
+            if (font.GetStyle() != startfont.GetStyle())
+                do_set_tag("\\i", std::to_string(font.GetStyle() == wxFONTSTYLE_ITALIC));
+            if (font.GetUnderlined() != startfont.GetUnderlined())
+                do_set_tag("\\i", std::to_string(font.GetUnderlined()));
+
+            return shift;
+        });
+    }
 };
 
 struct edit_find_replace final : public Command {
-	CMD_NAME("edit/find_replace")
-	CMD_ICON(find_replace_menu)
-	STR_MENU("Find and R&eplace...")
-	STR_DISP("Find and Replace")
-	STR_HELP("Find and replace words in subtitles")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		DialogSearchReplace::Show(c, true);
-	}
+    CMD_NAME("edit/find_replace")
+    CMD_ICON(find_replace_menu)
+    STR_MENU("Find and R&eplace...")
+    STR_DISP("Find and Replace")
+    STR_HELP("Find and replace words in subtitles")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        DialogSearchReplace::Show(c, true);
+    }
 };
 
-static void copy_lines(agi::Context *c) {
-	SetClipboard(join(c->selectionController->GetSortedSelection()
-		| transformed(static_cast<std::string(*)(AssDialogue*)>([](AssDialogue *d) { return d->GetEntryData(); })),
-		"\r\n"));
+static void copy_lines(agi::Context *c)
+{
+    SetClipboard(join(c->selectionController->GetSortedSelection()
+    | transformed(static_cast<std::string(*)(AssDialogue *)>([](AssDialogue * d) { return d->GetEntryData(); })),
+    "\r\n"));
 }
 
-static void delete_lines(agi::Context *c, wxString const& commit_message) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-
-	// Find a line near the active line not being deleted to make the new active line
-	AssDialogue *pre_sel = nullptr;
-	AssDialogue *post_sel = nullptr;
-	bool hit_selection = false;
-
-	for (auto& diag : c->ass->Events) {
-		if (sel.count(&diag))
-			hit_selection = true;
-		else if (hit_selection && !post_sel) {
-			post_sel = &diag;
-			break;
-		}
-		else
-			pre_sel = &diag;
-	}
-
-	// Remove the selected lines, but defer the deletion until after we select
-	// different lines. We can't just change the selection first because we may
-	// need to create a new dialogue line for it, and we can't select dialogue
-	// lines until after they're committed.
-	std::vector<std::unique_ptr<AssDialogue>> to_delete;
-	c->ass->Events.remove_and_dispose_if([&sel](AssDialogue const& e) {
-		return sel.count(const_cast<AssDialogue *>(&e));
-	}, [&](AssDialogue *e) {
-		to_delete.emplace_back(e);
-	});
-
-	AssDialogue *new_active = post_sel;
-	if (!new_active)
-		new_active = pre_sel;
-	// If we didn't get a new active line then we just deleted all the dialogue
-	// lines, so make a new one
-	if (!new_active) {
-		new_active = new AssDialogue;
-		c->ass->Events.push_back(*new_active);
-	}
-
-	c->ass->Commit(commit_message, AssFile::COMMIT_DIAG_ADDREM);
-	c->selectionController->SetSelectionAndActive({ new_active }, new_active);
+static void delete_lines(agi::Context *c, wxString const &commit_message)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+
+    // Find a line near the active line not being deleted to make the new active line
+    AssDialogue *pre_sel = nullptr;
+    AssDialogue *post_sel = nullptr;
+    bool hit_selection = false;
+
+    for (auto &diag : c->ass->Events) {
+        if (sel.count(&diag))
+            hit_selection = true;
+        else if (hit_selection && !post_sel) {
+            post_sel = &diag;
+            break;
+        }
+        else
+            pre_sel = &diag;
+    }
+
+    // Remove the selected lines, but defer the deletion until after we select
+    // different lines. We can't just change the selection first because we may
+    // need to create a new dialogue line for it, and we can't select dialogue
+    // lines until after they're committed.
+    std::vector<std::unique_ptr<AssDialogue>> to_delete;
+    c->ass->Events.remove_and_dispose_if([&sel](AssDialogue const & e) {
+        return sel.count(const_cast<AssDialogue *>(&e));
+    }, [&](AssDialogue * e) {
+        to_delete.emplace_back(e);
+    });
+
+    AssDialogue *new_active = post_sel;
+    if (!new_active)
+        new_active = pre_sel;
+    // If we didn't get a new active line then we just deleted all the dialogue
+    // lines, so make a new one
+    if (!new_active) {
+        new_active = new AssDialogue;
+        c->ass->Events.push_back(*new_active);
+    }
+
+    c->ass->Commit(commit_message, AssFile::COMMIT_DIAG_ADDREM);
+    c->selectionController->SetSelectionAndActive({ new_active }, new_active);
 }
 
 struct edit_line_copy final : public validate_sel_nonempty {
-	CMD_NAME("edit/line/copy")
-	CMD_ICON(copy_button)
-	STR_MENU("&Copy Lines")
-	STR_DISP("Copy Lines")
-	STR_HELP("Copy subtitles to the clipboard")
-
-	void operator()(agi::Context *c) override {
-		// Ideally we'd let the control's keydown handler run and only deal
-		// with the events not processed by it, but that doesn't seem to be
-		// possible with how wx implements key event handling - the native
-		// platform processing is evoked only if the wx event is unprocessed,
-		// and there's no way to do something if the native platform code leaves
-		// it unprocessed
-
-		if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase*>(c->parent->FindFocus()))
-			ctrl->Copy();
-		else {
-			copy_lines(c);
-		}
-	}
+    CMD_NAME("edit/line/copy")
+    CMD_ICON(copy_button)
+    STR_MENU("&Copy Lines")
+    STR_DISP("Copy Lines")
+    STR_HELP("Copy subtitles to the clipboard")
+
+    void operator()(agi::Context *c) override {
+        // Ideally we'd let the control's keydown handler run and only deal
+        // with the events not processed by it, but that doesn't seem to be
+        // possible with how wx implements key event handling - the native
+        // platform processing is evoked only if the wx event is unprocessed,
+        // and there's no way to do something if the native platform code leaves
+        // it unprocessed
+
+        if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase *>(c->parent->FindFocus()))
+            ctrl->Copy();
+        else {
+            copy_lines(c);
+        }
+    }
 };
 
 struct edit_line_cut: public validate_sel_nonempty {
-	CMD_NAME("edit/line/cut")
-	CMD_ICON(cut_button)
-	STR_MENU("Cu&t Lines")
-	STR_DISP("Cut Lines")
-	STR_HELP("Cut subtitles")
-
-	void operator()(agi::Context *c) override {
-		if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase*>(c->parent->FindFocus()))
-			ctrl->Cut();
-		else {
-			copy_lines(c);
-			delete_lines(c, _("cut lines"));
-		}
-	}
+    CMD_NAME("edit/line/cut")
+    CMD_ICON(cut_button)
+    STR_MENU("Cu&t Lines")
+    STR_DISP("Cut Lines")
+    STR_HELP("Cut subtitles")
+
+    void operator()(agi::Context *c) override {
+        if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase *>(c->parent->FindFocus()))
+            ctrl->Cut();
+        else {
+            copy_lines(c);
+            delete_lines(c, _("cut lines"));
+        }
+    }
 };
 
 struct edit_line_delete final : public validate_sel_nonempty {
-	CMD_NAME("edit/line/delete")
-	CMD_ICON(delete_button)
-	STR_MENU("De&lete Lines")
-	STR_DISP("Delete Lines")
-	STR_HELP("Delete currently selected lines")
-
-	void operator()(agi::Context *c) override {
-		delete_lines(c, _("delete lines"));
-	}
+    CMD_NAME("edit/line/delete")
+    CMD_ICON(delete_button)
+    STR_MENU("De&lete Lines")
+    STR_DISP("Delete Lines")
+    STR_HELP("Delete currently selected lines")
+
+    void operator()(agi::Context *c) override {
+        delete_lines(c, _("delete lines"));
+    }
 };
 
-static void duplicate_lines(agi::Context *c, int shift) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-	auto in_selection = [&](AssDialogue const& d) { return sel.count(const_cast<AssDialogue *>(&d)); };
-
-	Selection new_sel;
-	AssDialogue *new_active = nullptr;
-
-	auto start = c->ass->Events.begin();
-	auto end = c->ass->Events.end();
-	while (start != end) {
-		// Find the first line in the selection
-		start = std::find_if(start, end, in_selection);
-		if (start == end) break;
-
-		// And the last line in this contiguous selection
-		auto insert_pos = std::find_if_not(start, end, in_selection);
-		auto last = std::prev(insert_pos);
-
-		// Duplicate each of the selected lines, inserting them in a block
-		// after the selected block
-		do {
-			auto old_diag = &*start;
-			auto new_diag = new AssDialogue(*old_diag);
-
-			c->ass->Events.insert(insert_pos, *new_diag);
-			new_sel.insert(new_diag);
-			if (!new_active)
-				new_active = new_diag;
-
-			if (shift) {
-				int cur_frame = c->videoController->GetFrameN();
-				int old_start = c->videoController->FrameAtTime(new_diag->Start, agi::vfr::START);
-				int old_end = c->videoController->FrameAtTime(new_diag->End, agi::vfr::END);
-
-				// If the current frame isn't within the range of the line then
-				// splitting doesn't make any sense, so instead just duplicate
-				// the line and set the new one to just this frame
-				if (cur_frame < old_start || cur_frame > old_end) {
-					new_diag->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::START);
-					new_diag->End = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
-				}
-				/// @todo This does dumb things when old_start == old_end
-				else if (shift < 0) {
-					old_diag->End = c->videoController->TimeAtFrame(cur_frame - 1, agi::vfr::END);
-					new_diag->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::START);
-				}
-				else {
-					old_diag->End = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
-					new_diag->Start = c->videoController->TimeAtFrame(cur_frame + 1, agi::vfr::START);
-				}
-
-				/// @todo also split \t and \move?
-			}
-		} while (start++ != last);
-
-		// Skip over the lines we just made
-		start = insert_pos;
-	}
-
-	if (new_sel.empty()) return;
-
-	c->ass->Commit(shift ? _("split") : _("duplicate lines"), AssFile::COMMIT_DIAG_ADDREM);
-
-	c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
+static void duplicate_lines(agi::Context *c, int shift)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    auto in_selection = [&](AssDialogue const & d) { return sel.count(const_cast<AssDialogue *>(&d)); };
+
+    Selection new_sel;
+    AssDialogue *new_active = nullptr;
+
+    auto start = c->ass->Events.begin();
+    auto end = c->ass->Events.end();
+    while (start != end) {
+        // Find the first line in the selection
+        start = std::find_if(start, end, in_selection);
+        if (start == end) break;
+
+        // And the last line in this contiguous selection
+        auto insert_pos = std::find_if_not(start, end, in_selection);
+        auto last = std::prev(insert_pos);
+
+        // Duplicate each of the selected lines, inserting them in a block
+        // after the selected block
+        do {
+            auto old_diag = &*start;
+            auto new_diag = new AssDialogue(*old_diag);
+
+            c->ass->Events.insert(insert_pos, *new_diag);
+            new_sel.insert(new_diag);
+            if (!new_active)
+                new_active = new_diag;
+
+            if (shift) {
+                int cur_frame = c->videoController->GetFrameN();
+                int old_start = c->videoController->FrameAtTime(new_diag->Start, agi::vfr::START);
+                int old_end = c->videoController->FrameAtTime(new_diag->End, agi::vfr::END);
+
+                // If the current frame isn't within the range of the line then
+                // splitting doesn't make any sense, so instead just duplicate
+                // the line and set the new one to just this frame
+                if (cur_frame < old_start || cur_frame > old_end) {
+                    new_diag->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::START);
+                    new_diag->End = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
+                }
+                /// @todo This does dumb things when old_start == old_end
+                else if (shift < 0) {
+                    old_diag->End = c->videoController->TimeAtFrame(cur_frame - 1, agi::vfr::END);
+                    new_diag->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::START);
+                }
+                else {
+                    old_diag->End = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
+                    new_diag->Start = c->videoController->TimeAtFrame(cur_frame + 1, agi::vfr::START);
+                }
+
+                /// @todo also split \t and \move?
+            }
+        } while (start++ != last);
+
+        // Skip over the lines we just made
+        start = insert_pos;
+    }
+
+    if (new_sel.empty()) return;
+
+    c->ass->Commit(shift ? _("split") : _("duplicate lines"), AssFile::COMMIT_DIAG_ADDREM);
+
+    c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
 }
 
 struct edit_line_duplicate final : public validate_sel_nonempty {
-	CMD_NAME("edit/line/duplicate")
-	STR_MENU("&Duplicate Lines")
-	STR_DISP("Duplicate Lines")
-	STR_HELP("Duplicate the selected lines")
-
-	void operator()(agi::Context *c) override {
-		duplicate_lines(c, 0);
-	}
+    CMD_NAME("edit/line/duplicate")
+    STR_MENU("&Duplicate Lines")
+    STR_DISP("Duplicate Lines")
+    STR_HELP("Duplicate the selected lines")
+
+    void operator()(agi::Context *c) override {
+        duplicate_lines(c, 0);
+    }
 };
 
 struct edit_line_duplicate_shift final : public validate_video_and_sel_nonempty {
-	CMD_NAME("edit/line/split/after")
-	STR_MENU("Split lines after current frame")
-	STR_DISP("Split lines after current frame")
-	STR_HELP("Split the current line into a line which ends on the current frame and a line which starts on the next frame")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	void operator()(agi::Context *c) override {
-		duplicate_lines(c, 1);
-	}
+    CMD_NAME("edit/line/split/after")
+    STR_MENU("Split lines after current frame")
+    STR_DISP("Split lines after current frame")
+    STR_HELP("Split the current line into a line which ends on the current frame and a line which starts on the next frame")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    void operator()(agi::Context *c) override {
+        duplicate_lines(c, 1);
+    }
 };
 
 struct edit_line_duplicate_shift_back final : public validate_video_and_sel_nonempty {
-	CMD_NAME("edit/line/split/before")
-	STR_MENU("Split lines before current frame")
-	STR_DISP("Split lines before current frame")
-	STR_HELP("Split the current line into a line which ends on the previous frame and a line which starts on the current frame")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	void operator()(agi::Context *c) override {
-		duplicate_lines(c, -1);
-	}
+    CMD_NAME("edit/line/split/before")
+    STR_MENU("Split lines before current frame")
+    STR_DISP("Split lines before current frame")
+    STR_HELP("Split the current line into a line which ends on the previous frame and a line which starts on the current frame")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    void operator()(agi::Context *c) override {
+        duplicate_lines(c, -1);
+    }
 };
 
-static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDialogue *), wxString const& message) {
-	auto sel = c->selectionController->GetSortedSelection();
+static void combine_lines(agi::Context *c, void (*combiner)(AssDialogue *, AssDialogue *), wxString const &message)
+{
+    auto sel = c->selectionController->GetSortedSelection();
 
-	AssDialogue *first = sel[0];
-	combiner(first, nullptr);
-	for (size_t i = 1; i < sel.size(); ++i) {
-		combiner(first, sel[i]);
-		first->End = std::max(first->End, sel[i]->End);
-		delete sel[i];
-	}
+    AssDialogue *first = sel[0];
+    combiner(first, nullptr);
+    for (size_t i = 1; i < sel.size(); ++i) {
+        combiner(first, sel[i]);
+        first->End = std::max(first->End, sel[i]->End);
+        delete sel[i];
+    }
 
-	c->selectionController->SetSelectionAndActive({first}, first);
+    c->selectionController->SetSelectionAndActive({first}, first);
 
-	c->ass->Commit(message, AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
+    c->ass->Commit(message, AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
 }
 
-static void combine_karaoke(AssDialogue *first, AssDialogue *second) {
-	if (second)
-		first->Text = first->Text.get() + "{\\k" + std::to_string((second->End - second->Start) / 10) + "}" + second->Text.get();
-	else
-		first->Text = "{\\k" + std::to_string((first->End - first->Start) / 10) + "}" + first->Text.get();
+static void combine_karaoke(AssDialogue *first, AssDialogue *second)
+{
+    if (second)
+        first->Text = first->Text.get() + "{\\k" + std::to_string((second->End - second->Start) / 10) + "}" + second->Text.get();
+    else
+        first->Text = "{\\k" + std::to_string((first->End - first->Start) / 10) + "}" + first->Text.get();
 }
 
-static void combine_concat(AssDialogue *first, AssDialogue *second) {
-	if (second)
-		first->Text = first->Text.get() + " " + second->Text.get();
+static void combine_concat(AssDialogue *first, AssDialogue *second)
+{
+    if (second)
+        first->Text = first->Text.get() + " " + second->Text.get();
 }
 
 static void combine_drop(AssDialogue *, AssDialogue *) { }
 
 struct edit_line_join_as_karaoke final : public validate_sel_multiple {
-	CMD_NAME("edit/line/join/as_karaoke")
-	STR_MENU("As &Karaoke")
-	STR_DISP("As Karaoke")
-	STR_HELP("Join selected lines in a single one, as karaoke")
-
-	void operator()(agi::Context *c) override {
-		combine_lines(c, combine_karaoke, _("join as karaoke"));
-	}
+    CMD_NAME("edit/line/join/as_karaoke")
+    STR_MENU("As &Karaoke")
+    STR_DISP("As Karaoke")
+    STR_HELP("Join selected lines in a single one, as karaoke")
+
+    void operator()(agi::Context *c) override {
+        combine_lines(c, combine_karaoke, _("join as karaoke"));
+    }
 };
 
 struct edit_line_join_concatenate final : public validate_sel_multiple {
-	CMD_NAME("edit/line/join/concatenate")
-	STR_MENU("&Concatenate")
-	STR_DISP("Concatenate")
-	STR_HELP("Join selected lines in a single one, concatenating text together")
-
-	void operator()(agi::Context *c) override {
-		combine_lines(c, combine_concat, _("join lines"));
-	}
+    CMD_NAME("edit/line/join/concatenate")
+    STR_MENU("&Concatenate")
+    STR_DISP("Concatenate")
+    STR_HELP("Join selected lines in a single one, concatenating text together")
+
+    void operator()(agi::Context *c) override {
+        combine_lines(c, combine_concat, _("join lines"));
+    }
 };
 
 struct edit_line_join_keep_first final : public validate_sel_multiple {
-	CMD_NAME("edit/line/join/keep_first")
-	STR_MENU("Keep &First")
-	STR_DISP("Keep First")
-	STR_HELP("Join selected lines in a single one, keeping text of first and discarding remaining")
-
-	void operator()(agi::Context *c) override {
-		combine_lines(c, combine_drop, _("join lines"));
-	}
+    CMD_NAME("edit/line/join/keep_first")
+    STR_MENU("Keep &First")
+    STR_DISP("Keep First")
+    STR_HELP("Join selected lines in a single one, keeping text of first and discarding remaining")
+
+    void operator()(agi::Context *c) override {
+        combine_lines(c, combine_drop, _("join lines"));
+    }
 };
 
-static bool try_paste_lines(agi::Context *c) {
-	std::string data = GetClipboard();
-	boost::trim_left(data);
-	if (!boost::starts_with(data, "Dialogue:")) return false;
-
-	EntryList<AssDialogue> parsed;
-	boost::char_separator<char> sep("\r\n");
-	for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
-		boost::trim(curdata);
-		try {
-			parsed.push_back(*new AssDialogue(curdata));
-		}
-		catch (...) {
-			parsed.clear_and_dispose([](AssDialogue *e) { delete e; });
-			return false;
-		}
-	}
-
-	AssDialogue *new_active = &*parsed.begin();
-	Selection new_selection;
-	for (auto& line : parsed)
-		new_selection.insert(&line);
-
-	auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
-	c->ass->Events.splice(pos, parsed, parsed.begin(), parsed.end());
-	c->ass->Commit(_("paste"), AssFile::COMMIT_DIAG_ADDREM);
-	c->selectionController->SetSelectionAndActive(std::move(new_selection), new_active);
-
-	return true;
+static bool try_paste_lines(agi::Context *c)
+{
+    std::string data = GetClipboard();
+    boost::trim_left(data);
+    if (!boost::starts_with(data, "Dialogue:")) return false;
+
+    EntryList<AssDialogue> parsed;
+    boost::char_separator<char> sep("\r\n");
+    for (auto curdata : boost::tokenizer<boost::char_separator<char>>(data, sep)) {
+        boost::trim(curdata);
+        try {
+            parsed.push_back(*new AssDialogue(curdata));
+        }
+        catch (...) {
+            parsed.clear_and_dispose([](AssDialogue * e) { delete e; });
+            return false;
+        }
+    }
+
+    AssDialogue *new_active = &*parsed.begin();
+    Selection new_selection;
+    for (auto &line : parsed)
+        new_selection.insert(&line);
+
+    auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
+    c->ass->Events.splice(pos, parsed, parsed.begin(), parsed.end());
+    c->ass->Commit(_("paste"), AssFile::COMMIT_DIAG_ADDREM);
+    c->selectionController->SetSelectionAndActive(std::move(new_selection), new_active);
+
+    return true;
 }
 
 struct edit_line_paste final : public Command {
-	CMD_NAME("edit/line/paste")
-	CMD_ICON(paste_button)
-	STR_MENU("&Paste Lines")
-	STR_DISP("Paste Lines")
-	STR_HELP("Paste subtitles")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *) override {
-		bool can_paste = false;
-		if (wxTheClipboard->Open()) {
-			can_paste = wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
-			wxTheClipboard->Close();
-		}
-		return can_paste;
-	}
-
-	void operator()(agi::Context *c) override {
-		if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase*>(c->parent->FindFocus())) {
-			if (!try_paste_lines(c))
-				ctrl->Paste();
-		}
-		else {
-			auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
-			paste_lines(c, false, [=](AssDialogue *new_line) -> AssDialogue * {
-				c->ass->Events.insert(pos, *new_line);
-				return new_line;
-			});
-		}
-	}
+    CMD_NAME("edit/line/paste")
+    CMD_ICON(paste_button)
+    STR_MENU("&Paste Lines")
+    STR_DISP("Paste Lines")
+    STR_HELP("Paste subtitles")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *) override {
+        bool can_paste = false;
+        if (wxTheClipboard->Open()) {
+            can_paste = wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
+            wxTheClipboard->Close();
+        }
+        return can_paste;
+    }
+
+    void operator()(agi::Context *c) override {
+        if (wxTextEntryBase *ctrl = dynamic_cast<wxTextEntryBase *>(c->parent->FindFocus())) {
+            if (!try_paste_lines(c))
+                ctrl->Paste();
+        }
+        else {
+            auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
+            paste_lines(c, false, [ = ](AssDialogue * new_line) -> AssDialogue * {
+                c->ass->Events.insert(pos, *new_line);
+                return new_line;
+            });
+        }
+    }
 };
 
 struct edit_line_paste_over final : public Command {
-	CMD_NAME("edit/line/paste/over")
-	STR_MENU("Paste Lines &Over...")
-	STR_DISP("Paste Lines Over")
-	STR_HELP("Paste subtitles over others")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		bool can_paste = !c->selectionController->GetSelectedSet().empty();
-		if (can_paste && wxTheClipboard->Open()) {
-			can_paste = wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
-			wxTheClipboard->Close();
-		}
-		return can_paste;
-	}
-
-	void operator()(agi::Context *c) override {
-		auto const& sel = c->selectionController->GetSelectedSet();
-		std::vector<bool> pasteOverOptions;
-
-		// Only one line selected, so paste over downwards from the active line
-		if (sel.size() < 2) {
-			auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
-
-			paste_lines(c, true, [&](AssDialogue *new_line) -> AssDialogue * {
-				std::unique_ptr<AssDialogue> deleter(new_line);
-				if (pos == c->ass->Events.end()) return nullptr;
-
-				AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, &*pos);
-				if (ret)
-					++pos;
-				return ret;
-			});
-		}
-		else {
-			// Multiple lines selected, so paste over the selection
-			auto sorted_selection = c->selectionController->GetSortedSelection();
-			auto pos = begin(sorted_selection);
-			paste_lines(c, true, [&](AssDialogue *new_line) -> AssDialogue * {
-				std::unique_ptr<AssDialogue> deleter(new_line);
-				if (pos == end(sorted_selection)) return nullptr;
-
-				AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, *pos);
-				if (ret) ++pos;
-				return ret;
-			});
-		}
-	}
+    CMD_NAME("edit/line/paste/over")
+    STR_MENU("Paste Lines &Over...")
+    STR_DISP("Paste Lines Over")
+    STR_HELP("Paste subtitles over others")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        bool can_paste = !c->selectionController->GetSelectedSet().empty();
+        if (can_paste && wxTheClipboard->Open()) {
+            can_paste = wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
+            wxTheClipboard->Close();
+        }
+        return can_paste;
+    }
+
+    void operator()(agi::Context *c) override {
+        auto const &sel = c->selectionController->GetSelectedSet();
+        std::vector<bool> pasteOverOptions;
+
+        // Only one line selected, so paste over downwards from the active line
+        if (sel.size() < 2) {
+            auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
+
+            paste_lines(c, true, [&](AssDialogue * new_line) -> AssDialogue * {
+                std::unique_ptr<AssDialogue> deleter(new_line);
+                if (pos == c->ass->Events.end()) return nullptr;
+
+                AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, &*pos);
+                if (ret)
+                    ++pos;
+                return ret;
+            });
+        }
+        else {
+            // Multiple lines selected, so paste over the selection
+            auto sorted_selection = c->selectionController->GetSortedSelection();
+            auto pos = begin(sorted_selection);
+            paste_lines(c, true, [&](AssDialogue * new_line) -> AssDialogue * {
+                std::unique_ptr<AssDialogue> deleter(new_line);
+                if (pos == end(sorted_selection)) return nullptr;
+
+                AssDialogue *ret = paste_over(c->parent, pasteOverOptions, new_line, *pos);
+                if (ret) ++pos;
+                return ret;
+            });
+        }
+    }
 };
 
 namespace {
-std::string trim_text(std::string text) {
-	boost::regex start(R"(^( |	|\\[nNh])+)");
-	boost::regex end(R"(( |	|\\[nNh])+$)");
-
-	text = regex_replace(text, start, "", boost::format_first_only);
-	text = regex_replace(text, end, "", boost::format_first_only);
-	return text;
+std::string trim_text(std::string text)
+{
+    boost::regex start(R"(^( |	|\\[nNh])+)");
+    boost::regex end(R"(( |	|\\[nNh])+$)");
+
+    text = regex_replace(text, start, "", boost::format_first_only);
+    text = regex_replace(text, end, "", boost::format_first_only);
+    return text;
 }
 
-void expand_times(AssDialogue *src, AssDialogue *dst) {
-	dst->Start = std::min(dst->Start, src->Start);
-	dst->End = std::max(dst->End, src->End);
+void expand_times(AssDialogue *src, AssDialogue *dst)
+{
+    dst->Start = std::min(dst->Start, src->Start);
+    dst->End = std::max(dst->End, src->End);
 }
 
-bool check_start(AssDialogue *d1, AssDialogue *d2) {
-	if (boost::starts_with(d1->Text.get(), d2->Text.get())) {
-		d1->Text = trim_text(d1->Text.get().substr(d2->Text.get().size()));
-		expand_times(d1, d2);
-		return true;
-	}
-	return false;
+bool check_start(AssDialogue *d1, AssDialogue *d2)
+{
+    if (boost::starts_with(d1->Text.get(), d2->Text.get())) {
+        d1->Text = trim_text(d1->Text.get().substr(d2->Text.get().size()));
+        expand_times(d1, d2);
+        return true;
+    }
+    return false;
 }
 
-bool check_end(AssDialogue *d1, AssDialogue *d2) {
-	if (boost::ends_with(d1->Text.get(), d2->Text.get())) {
-		d1->Text = trim_text(d1->Text.get().substr(0, d1->Text.get().size() - d2->Text.get().size()));
-		expand_times(d1, d2);
-		return true;
-	}
-	return false;
+bool check_end(AssDialogue *d1, AssDialogue *d2)
+{
+    if (boost::ends_with(d1->Text.get(), d2->Text.get())) {
+        d1->Text = trim_text(d1->Text.get().substr(0, d1->Text.get().size() - d2->Text.get().size()));
+        expand_times(d1, d2);
+        return true;
+    }
+    return false;
 }
 
 }
 
 struct edit_line_recombine final : public validate_sel_multiple {
-	CMD_NAME("edit/line/recombine")
-	STR_MENU("Recom&bine Lines")
-	STR_DISP("Recombine Lines")
-	STR_HELP("Recombine subtitles which have been split and merged")
-
-	void operator()(agi::Context *c) override {
-		auto const& sel_set = c->selectionController->GetSelectedSet();
-		if (sel_set.size() < 2) return;
-
-		auto active_line = c->selectionController->GetActiveLine();
-
-		std::vector<AssDialogue*> sel(sel_set.begin(), sel_set.end());
-		boost::sort(sel, [](const AssDialogue *a, const AssDialogue *b) {
-			return a->Start < b->Start;
-		});
-
-		for (auto &diag : sel)
-			diag->Text = trim_text(diag->Text);
-
-		auto end = sel.end() - 1;
-		for (auto cur = sel.begin(); cur != end; ++cur) {
-			auto d1 = *cur;
-			auto d2 = cur + 1;
-
-			// 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate
-			if (d1->Text == (*d2)->Text) {
-				expand_times(d1, *d2);
-				delete d1;
-				continue;
-			}
-
-			// 1, 1+2, 1 turns into 1, 2, [empty]
-			if (d1->Text.get().empty()) {
-				delete d1;
-				continue;
-			}
-
-			// If d2 is the last line in the selection it'll never hit the above test
-			if (d2 == end && (*d2)->Text.get().empty()) {
-				delete *d2;
-				continue;
-			}
-
-			// 1, 1+2
-			while (d2 <= end && check_start(*d2, d1))
-				++d2;
-
-			// 1, 2+1
-			while (d2 <= end && check_end(*d2, d1))
-				++d2;
-
-			// 1+2, 2
-			while (d2 <= end && check_end(d1, *d2))
-				++d2;
-
-			// 2+1, 2
-			while (d2 <= end && check_start(d1, *d2))
-				++d2;
-		}
-
-		// Remove now non-existent lines from the selection
-		Selection lines, new_sel;
-		boost::copy(c->ass->Events | agi::address_of, inserter(lines, lines.begin()));
-		boost::set_intersection(lines, sel_set, inserter(new_sel, new_sel.begin()));
-
-		if (new_sel.empty())
-			new_sel.insert(*lines.begin());
-
-		// Restore selection
-		if (!new_sel.count(active_line))
-			active_line = *new_sel.begin();
-		c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
-
-		c->ass->Commit(_("combining"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
-	}
+    CMD_NAME("edit/line/recombine")
+    STR_MENU("Recom&bine Lines")
+    STR_DISP("Recombine Lines")
+    STR_HELP("Recombine subtitles which have been split and merged")
+
+    void operator()(agi::Context *c) override {
+        auto const &sel_set = c->selectionController->GetSelectedSet();
+        if (sel_set.size() < 2) return;
+
+        auto active_line = c->selectionController->GetActiveLine();
+
+        std::vector<AssDialogue *> sel(sel_set.begin(), sel_set.end());
+        boost::sort(sel, [](const AssDialogue * a, const AssDialogue * b) {
+            return a->Start < b->Start;
+        });
+
+        for (auto &diag : sel)
+            diag->Text = trim_text(diag->Text);
+
+        auto end = sel.end() - 1;
+        for (auto cur = sel.begin(); cur != end; ++cur) {
+            auto d1 = *cur;
+            auto d2 = cur + 1;
+
+            // 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate
+            if (d1->Text == (*d2)->Text) {
+                expand_times(d1, *d2);
+                delete d1;
+                continue;
+            }
+
+            // 1, 1+2, 1 turns into 1, 2, [empty]
+            if (d1->Text.get().empty()) {
+                delete d1;
+                continue;
+            }
+
+            // If d2 is the last line in the selection it'll never hit the above test
+            if (d2 == end && (*d2)->Text.get().empty()) {
+                delete *d2;
+                continue;
+            }
+
+            // 1, 1+2
+            while (d2 <= end && check_start(*d2, d1))
+                ++d2;
+
+            // 1, 2+1
+            while (d2 <= end && check_end(*d2, d1))
+                ++d2;
+
+            // 1+2, 2
+            while (d2 <= end && check_end(d1, *d2))
+                ++d2;
+
+            // 2+1, 2
+            while (d2 <= end && check_start(d1, *d2))
+                ++d2;
+        }
+
+        // Remove now non-existent lines from the selection
+        Selection lines, new_sel;
+        boost::copy(c->ass->Events | agi::address_of, inserter(lines, lines.begin()));
+        boost::set_intersection(lines, sel_set, inserter(new_sel, new_sel.begin()));
+
+        if (new_sel.empty())
+            new_sel.insert(*lines.begin());
+
+        // Restore selection
+        if (!new_sel.count(active_line))
+            active_line = *new_sel.begin();
+        c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
+
+        c->ass->Commit(_("combining"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
+    }
 };
 
 struct edit_line_split_by_karaoke final : public validate_sel_nonempty {
-	CMD_NAME("edit/line/split/by_karaoke")
-	STR_MENU("Split Lines (by karaoke)")
-	STR_DISP("Split Lines (by karaoke)")
-	STR_HELP("Use karaoke timing to split line into multiple smaller lines")
+    CMD_NAME("edit/line/split/by_karaoke")
+    STR_MENU("Split Lines (by karaoke)")
+    STR_DISP("Split Lines (by karaoke)")
+    STR_HELP("Use karaoke timing to split line into multiple smaller lines")
 
-	void operator()(agi::Context *c) override {
-		auto sel = c->selectionController->GetSortedSelection();
-		if (sel.empty()) return;
+    void operator()(agi::Context *c) override {
+        auto sel = c->selectionController->GetSortedSelection();
+        if (sel.empty()) return;
 
-		Selection new_sel;
-		AssKaraoke kara;
+        Selection new_sel;
+        AssKaraoke kara;
 
-		std::vector<std::unique_ptr<AssDialogue>> to_delete;
-		for (auto line : sel) {
-			kara.SetLine(line);
+        std::vector<std::unique_ptr<AssDialogue>> to_delete;
+        for (auto line : sel) {
+            kara.SetLine(line);
 
-			// If there aren't at least two tags there's nothing to split
-			if (kara.size() < 2) continue;
+            // If there aren't at least two tags there's nothing to split
+            if (kara.size() < 2) continue;
 
-			for (auto const& syl : kara) {
-				auto new_line = new AssDialogue(*line);
+            for (auto const &syl : kara) {
+                auto new_line = new AssDialogue(*line);
 
-				new_line->Start = syl.start_time;
-				new_line->End = syl.start_time + syl.duration;
-				new_line->Text = syl.GetText(false);
+                new_line->Start = syl.start_time;
+                new_line->End = syl.start_time + syl.duration;
+                new_line->Text = syl.GetText(false);
 
-				c->ass->Events.insert(c->ass->iterator_to(*line), *new_line);
+                c->ass->Events.insert(c->ass->iterator_to(*line), *new_line);
 
-				new_sel.insert(new_line);
-			}
+                new_sel.insert(new_line);
+            }
 
-			c->ass->Events.erase(c->ass->iterator_to(*line));
-			to_delete.emplace_back(line);
-		}
+            c->ass->Events.erase(c->ass->iterator_to(*line));
+            to_delete.emplace_back(line);
+        }
 
-		if (to_delete.empty()) return;
+        if (to_delete.empty()) return;
 
-		c->ass->Commit(_("splitting"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
+        c->ass->Commit(_("splitting"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
 
-		AssDialogue *new_active = c->selectionController->GetActiveLine();
-		if (!new_sel.count(c->selectionController->GetActiveLine()))
-			new_active = *new_sel.begin();
-		c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
-	}
+        AssDialogue *new_active = c->selectionController->GetActiveLine();
+        if (!new_sel.count(c->selectionController->GetActiveLine()))
+            new_active = *new_sel.begin();
+        c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
+    }
 };
 
-void split_lines(agi::Context *c, AssDialogue *&n1, AssDialogue *&n2) {
-	int pos = c->textSelectionController->GetSelectionStart();
+void split_lines(agi::Context *c, AssDialogue *&n1, AssDialogue *&n2)
+{
+    int pos = c->textSelectionController->GetSelectionStart();
 
-	n1 = c->selectionController->GetActiveLine();
-	n2 = new AssDialogue(*n1);
-	c->ass->Events.insert(++c->ass->iterator_to(*n1), *n2);
+    n1 = c->selectionController->GetActiveLine();
+    n2 = new AssDialogue(*n1);
+    c->ass->Events.insert(++c->ass->iterator_to(*n1), *n2);
 
-	std::string orig = n1->Text;
-	n1->Text = boost::trim_right_copy(orig.substr(0, pos));
-	n2->Text = boost::trim_left_copy(orig.substr(pos));
+    std::string orig = n1->Text;
+    n1->Text = boost::trim_right_copy(orig.substr(0, pos));
+    n2->Text = boost::trim_left_copy(orig.substr(pos));
 }
 
 template<typename Func>
-void split_lines(agi::Context *c, Func&& set_time) {
-	AssDialogue *n1, *n2;
-	split_lines(c, n1, n2);
-	set_time(n1, n2);
+void split_lines(agi::Context *c, Func &&set_time)
+{
+    AssDialogue *n1, *n2;
+    split_lines(c, n1, n2);
+    set_time(n1, n2);
 
-	c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
+    c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
 }
 
 struct edit_line_split_estimate final : public validate_video_and_sel_nonempty {
-	CMD_NAME("edit/line/split/estimate")
-	STR_MENU("Split at cursor (estimate times)")
-	STR_DISP("Split at cursor (estimate times)")
-	STR_HELP("Split the current line at the cursor, dividing the original line's duration between the new ones")
-
-	void operator()(agi::Context *c) override {
-		split_lines(c, [](AssDialogue *n1, AssDialogue *n2) {
-			size_t len = n1->Text.get().size() + n2->Text.get().size();
-			if (!len) return;
-			double splitPos = double(n1->Text.get().size()) / len;
-			n2->Start = n1->End = (int)((n1->End - n1->Start) * splitPos) + n1->Start;
-		});
-	}
+    CMD_NAME("edit/line/split/estimate")
+    STR_MENU("Split at cursor (estimate times)")
+    STR_DISP("Split at cursor (estimate times)")
+    STR_HELP("Split the current line at the cursor, dividing the original line's duration between the new ones")
+
+    void operator()(agi::Context *c) override {
+        split_lines(c, [](AssDialogue * n1, AssDialogue * n2) {
+            size_t len = n1->Text.get().size() + n2->Text.get().size();
+            if (!len) return;
+            double splitPos = double(n1->Text.get().size()) / len;
+            n2->Start = n1->End = (int)((n1->End - n1->Start) * splitPos) + n1->Start;
+        });
+    }
 };
 
 struct edit_line_split_preserve final : public validate_sel_nonempty {
-	CMD_NAME("edit/line/split/preserve")
-	STR_MENU("Split at cursor (preserve times)")
-	STR_DISP("Split at cursor (preserve times)")
-	STR_HELP("Split the current line at the cursor, setting both lines to the original line's times")
-
-	void operator()(agi::Context *c) override {
-		split_lines(c, [](AssDialogue *, AssDialogue *) { });
-	}
+    CMD_NAME("edit/line/split/preserve")
+    STR_MENU("Split at cursor (preserve times)")
+    STR_DISP("Split at cursor (preserve times)")
+    STR_HELP("Split the current line at the cursor, setting both lines to the original line's times")
+
+    void operator()(agi::Context *c) override {
+        split_lines(c, [](AssDialogue *, AssDialogue *) { });
+    }
 };
 
 struct edit_line_split_video final : public validate_video_and_sel_nonempty {
-	CMD_NAME("edit/line/split/video")
-	STR_MENU("Split at cursor (at video frame)")
-	STR_DISP("Split at cursor (at video frame)")
-	STR_HELP("Split the current line at the cursor, dividing the line's duration at the current video frame")
-
-	void operator()(agi::Context *c) override {
-		split_lines(c, [&](AssDialogue *n1, AssDialogue *n2) {
-			int cur_frame = mid(
-				c->videoController->FrameAtTime(n1->Start, agi::vfr::START),
-				c->videoController->GetFrameN(),
-				c->videoController->FrameAtTime(n1->End, agi::vfr::END));
-			n1->End = n2->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
-		});
-	}
+    CMD_NAME("edit/line/split/video")
+    STR_MENU("Split at cursor (at video frame)")
+    STR_DISP("Split at cursor (at video frame)")
+    STR_HELP("Split the current line at the cursor, dividing the line's duration at the current video frame")
+
+    void operator()(agi::Context *c) override {
+        split_lines(c, [&](AssDialogue * n1, AssDialogue * n2) {
+            int cur_frame = mid(
+                                c->videoController->FrameAtTime(n1->Start, agi::vfr::START),
+                                c->videoController->GetFrameN(),
+                                c->videoController->FrameAtTime(n1->End, agi::vfr::END));
+            n1->End = n2->Start = c->videoController->TimeAtFrame(cur_frame, agi::vfr::END);
+        });
+    }
 };
 
 struct edit_redo final : public Command {
-	CMD_NAME("edit/redo")
-	CMD_ICON(redo_button)
-	STR_HELP("Redo last undone action")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
-
-	wxString StrMenu(const agi::Context *c) const override {
-		return c->subsController->IsRedoStackEmpty() ?
-			_("Nothing to &redo") :
-			fmt_tl("&Redo %s", c->subsController->GetRedoDescription());
-	}
-	wxString StrDisplay(const agi::Context *c) const override {
-		return c->subsController->IsRedoStackEmpty() ?
-			_("Nothing to redo") :
-			fmt_tl("Redo %s", c->subsController->GetRedoDescription());
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return !c->subsController->IsRedoStackEmpty();
-	}
-
-	void operator()(agi::Context *c) override {
-		c->subsController->Redo();
-	}
+    CMD_NAME("edit/redo")
+    CMD_ICON(redo_button)
+    STR_HELP("Redo last undone action")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
+
+    wxString StrMenu(const agi::Context *c) const override {
+        return c->subsController->IsRedoStackEmpty() ?
+               _("Nothing to &redo") :
+               fmt_tl("&Redo %s", c->subsController->GetRedoDescription());
+    }
+    wxString StrDisplay(const agi::Context *c) const override {
+        return c->subsController->IsRedoStackEmpty() ?
+               _("Nothing to redo") :
+               fmt_tl("Redo %s", c->subsController->GetRedoDescription());
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return !c->subsController->IsRedoStackEmpty();
+    }
+
+    void operator()(agi::Context *c) override {
+        c->subsController->Redo();
+    }
 };
 
 struct edit_undo final : public Command {
-	CMD_NAME("edit/undo")
-	CMD_ICON(undo_button)
-	STR_HELP("Undo last action")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
-
-	wxString StrMenu(const agi::Context *c) const override {
-		return c->subsController->IsUndoStackEmpty() ?
-			_("Nothing to &undo") :
-			fmt_tl("&Undo %s", c->subsController->GetUndoDescription());
-	}
-	wxString StrDisplay(const agi::Context *c) const override {
-		return c->subsController->IsUndoStackEmpty() ?
-			_("Nothing to undo") :
-			fmt_tl("Undo %s", c->subsController->GetUndoDescription());
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return !c->subsController->IsUndoStackEmpty();
-	}
-
-	void operator()(agi::Context *c) override {
-		c->subsController->Undo();
-	}
+    CMD_NAME("edit/undo")
+    CMD_ICON(undo_button)
+    STR_HELP("Undo last action")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_DYNAMIC_NAME)
+
+    wxString StrMenu(const agi::Context *c) const override {
+        return c->subsController->IsUndoStackEmpty() ?
+               _("Nothing to &undo") :
+               fmt_tl("&Undo %s", c->subsController->GetUndoDescription());
+    }
+    wxString StrDisplay(const agi::Context *c) const override {
+        return c->subsController->IsUndoStackEmpty() ?
+               _("Nothing to undo") :
+               fmt_tl("Undo %s", c->subsController->GetUndoDescription());
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return !c->subsController->IsUndoStackEmpty();
+    }
+
+    void operator()(agi::Context *c) override {
+        c->subsController->Undo();
+    }
 };
 
 struct edit_revert final : public Command {
-	CMD_NAME("edit/revert")
-	STR_DISP("Revert")
-	STR_MENU("Revert")
-	STR_HELP("Revert the active line to its initial state (shown in the upper editor)")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *line = c->selectionController->GetActiveLine();
-		line->Text = c->initialLineState->GetInitialText();
-		c->ass->Commit(_("revert line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
-	}
+    CMD_NAME("edit/revert")
+    STR_DISP("Revert")
+    STR_MENU("Revert")
+    STR_HELP("Revert the active line to its initial state (shown in the upper editor)")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *line = c->selectionController->GetActiveLine();
+        line->Text = c->initialLineState->GetInitialText();
+        c->ass->Commit(_("revert line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
+    }
 };
 
 struct edit_clear final : public Command {
-	CMD_NAME("edit/clear")
-	STR_DISP("Clear")
-	STR_MENU("Clear")
-	STR_HELP("Clear the current line's text")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *line = c->selectionController->GetActiveLine();
-		line->Text = "";
-		c->ass->Commit(_("clear line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
-	}
+    CMD_NAME("edit/clear")
+    STR_DISP("Clear")
+    STR_MENU("Clear")
+    STR_HELP("Clear the current line's text")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *line = c->selectionController->GetActiveLine();
+        line->Text = "";
+        c->ass->Commit(_("clear line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
+    }
 };
 
 std::string get_text(AssDialogueBlock &d) { return d.GetText(); }
 struct edit_clear_text final : public Command {
-	CMD_NAME("edit/clear/text")
-	STR_DISP("Clear Text")
-	STR_MENU("Clear Text")
-	STR_HELP("Clear the current line's text, leaving override tags")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *line = c->selectionController->GetActiveLine();
-		auto blocks = line->ParseTags();
-		line->Text = join(blocks
-			| indirected
-			| filtered([](AssDialogueBlock const& b) { return b.GetType() != AssBlockType::PLAIN; })
-			| transformed(get_text),
-			"");
-		c->ass->Commit(_("clear line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
-	}
+    CMD_NAME("edit/clear/text")
+    STR_DISP("Clear Text")
+    STR_MENU("Clear Text")
+    STR_HELP("Clear the current line's text, leaving override tags")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *line = c->selectionController->GetActiveLine();
+        auto blocks = line->ParseTags();
+        line->Text = join(blocks
+                          | indirected
+        | filtered([](AssDialogueBlock const & b) { return b.GetType() != AssBlockType::PLAIN; })
+        | transformed(get_text),
+        "");
+        c->ass->Commit(_("clear line"), AssFile::COMMIT_DIAG_TEXT, -1, line);
+    }
 };
 
 struct edit_insert_original final : public Command {
-	CMD_NAME("edit/insert_original")
-	STR_DISP("Insert Original")
-	STR_MENU("Insert Original")
-	STR_HELP("Insert the original line text at the cursor")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *line = c->selectionController->GetActiveLine();
-		int sel_start = c->textSelectionController->GetSelectionStart();
-		int sel_end = c->textSelectionController->GetSelectionEnd();
-
-		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);
-	}
+    CMD_NAME("edit/insert_original")
+    STR_DISP("Insert Original")
+    STR_MENU("Insert Original")
+    STR_HELP("Insert the original line text at the cursor")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *line = c->selectionController->GetActiveLine();
+        int sel_start = c->textSelectionController->GetSelectionStart();
+        int sel_end = c->textSelectionController->GetSelectionEnd();
+
+        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);
+    }
 };
 
 }
 
 namespace cmd {
-	void init_edit() {
-		reg(agi::make_unique<edit_color_primary>());
-		reg(agi::make_unique<edit_color_secondary>());
-		reg(agi::make_unique<edit_color_outline>());
-		reg(agi::make_unique<edit_color_shadow>());
-		reg(agi::make_unique<edit_font>());
-		reg(agi::make_unique<edit_find_replace>());
-		reg(agi::make_unique<edit_line_copy>());
-		reg(agi::make_unique<edit_line_cut>());
-		reg(agi::make_unique<edit_line_delete>());
-		reg(agi::make_unique<edit_line_duplicate>());
-		reg(agi::make_unique<edit_line_duplicate_shift>());
-		reg(agi::make_unique<edit_line_duplicate_shift_back>());
-		reg(agi::make_unique<edit_line_join_as_karaoke>());
-		reg(agi::make_unique<edit_line_join_concatenate>());
-		reg(agi::make_unique<edit_line_join_keep_first>());
-		reg(agi::make_unique<edit_line_paste>());
-		reg(agi::make_unique<edit_line_paste_over>());
-		reg(agi::make_unique<edit_line_recombine>());
-		reg(agi::make_unique<edit_line_split_by_karaoke>());
-		reg(agi::make_unique<edit_line_split_estimate>());
-		reg(agi::make_unique<edit_line_split_preserve>());
-		reg(agi::make_unique<edit_line_split_video>());
-		reg(agi::make_unique<edit_style_bold>());
-		reg(agi::make_unique<edit_style_italic>());
-		reg(agi::make_unique<edit_style_underline>());
-		reg(agi::make_unique<edit_style_strikeout>());
-		reg(agi::make_unique<edit_redo>());
-		reg(agi::make_unique<edit_undo>());
-		reg(agi::make_unique<edit_revert>());
-		reg(agi::make_unique<edit_insert_original>());
-		reg(agi::make_unique<edit_clear>());
-		reg(agi::make_unique<edit_clear_text>());
-	}
+void init_edit()
+{
+    reg(agi::make_unique<edit_color_primary>());
+    reg(agi::make_unique<edit_color_secondary>());
+    reg(agi::make_unique<edit_color_outline>());
+    reg(agi::make_unique<edit_color_shadow>());
+    reg(agi::make_unique<edit_font>());
+    reg(agi::make_unique<edit_find_replace>());
+    reg(agi::make_unique<edit_line_copy>());
+    reg(agi::make_unique<edit_line_cut>());
+    reg(agi::make_unique<edit_line_delete>());
+    reg(agi::make_unique<edit_line_duplicate>());
+    reg(agi::make_unique<edit_line_duplicate_shift>());
+    reg(agi::make_unique<edit_line_duplicate_shift_back>());
+    reg(agi::make_unique<edit_line_join_as_karaoke>());
+    reg(agi::make_unique<edit_line_join_concatenate>());
+    reg(agi::make_unique<edit_line_join_keep_first>());
+    reg(agi::make_unique<edit_line_paste>());
+    reg(agi::make_unique<edit_line_paste_over>());
+    reg(agi::make_unique<edit_line_recombine>());
+    reg(agi::make_unique<edit_line_split_by_karaoke>());
+    reg(agi::make_unique<edit_line_split_estimate>());
+    reg(agi::make_unique<edit_line_split_preserve>());
+    reg(agi::make_unique<edit_line_split_video>());
+    reg(agi::make_unique<edit_style_bold>());
+    reg(agi::make_unique<edit_style_italic>());
+    reg(agi::make_unique<edit_style_underline>());
+    reg(agi::make_unique<edit_style_strikeout>());
+    reg(agi::make_unique<edit_redo>());
+    reg(agi::make_unique<edit_undo>());
+    reg(agi::make_unique<edit_revert>());
+    reg(agi::make_unique<edit_insert_original>());
+    reg(agi::make_unique<edit_clear>());
+    reg(agi::make_unique<edit_clear_text>());
+}
 }
diff --git a/src/command/grid.cpp b/src/command/grid.cpp
index d0a9d165b7ce0955d71052fe3888a4732d219744..f792ae737a9711e96715f7137868586011dc1102 100644
--- a/src/command/grid.cpp
+++ b/src/command/grid.cpp
@@ -44,385 +44,387 @@
 #include <libaegisub/make_unique.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct grid_line_next final : public Command {
-	CMD_NAME("grid/line/next")
-	STR_MENU("Next Line")
-	STR_DISP("Next Line")
-	STR_HELP("Move to the next subtitle line")
-
-	void operator()(agi::Context *c) override {
-		c->selectionController->NextLine();
-	}
+    CMD_NAME("grid/line/next")
+    STR_MENU("Next Line")
+    STR_DISP("Next Line")
+    STR_HELP("Move to the next subtitle line")
+
+    void operator()(agi::Context *c) override {
+        c->selectionController->NextLine();
+    }
 };
 
 struct grid_line_next_create final : public Command {
-	CMD_NAME("grid/line/next/create")
-	CMD_ICON(button_audio_commit)
-	STR_MENU("Next Line")
-	STR_DISP("Next Line")
-	STR_HELP("Move to the next subtitle line, creating a new one if needed")
-
-	void operator()(agi::Context *c) override {
-		AudioTimingController *tc = c->audioController->GetTimingController();
-		if (tc)
-			tc->Commit();
-
-		AssDialogue *cur = c->selectionController->GetActiveLine();
-		c->selectionController->NextLine();
-		if (cur == c->selectionController->GetActiveLine()) {
-			auto newline = new AssDialogue;
-			newline->Start = cur->End;
-			newline->End = cur->End + OPT_GET("Timing/Default Duration")->GetInt();
-			newline->Style = cur->Style;
-
-			auto pos = c->ass->iterator_to(*cur);
-			c->ass->Events.insert(++pos, *newline);
-			c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
-			c->selectionController->NextLine();
-		}
-	}
+    CMD_NAME("grid/line/next/create")
+    CMD_ICON(button_audio_commit)
+    STR_MENU("Next Line")
+    STR_DISP("Next Line")
+    STR_HELP("Move to the next subtitle line, creating a new one if needed")
+
+    void operator()(agi::Context *c) override {
+        AudioTimingController *tc = c->audioController->GetTimingController();
+        if (tc)
+            tc->Commit();
+
+        AssDialogue *cur = c->selectionController->GetActiveLine();
+        c->selectionController->NextLine();
+        if (cur == c->selectionController->GetActiveLine()) {
+            auto newline = new AssDialogue;
+            newline->Start = cur->End;
+            newline->End = cur->End + OPT_GET("Timing/Default Duration")->GetInt();
+            newline->Style = cur->Style;
+
+            auto pos = c->ass->iterator_to(*cur);
+            c->ass->Events.insert(++pos, *newline);
+            c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
+            c->selectionController->NextLine();
+        }
+    }
 };
 
 struct grid_line_prev final : public Command {
-	CMD_NAME("grid/line/prev")
-	STR_MENU("Previous Line")
-	STR_DISP("Previous Line")
-	STR_HELP("Move to the previous line")
-
-	void operator()(agi::Context *c) override {
-		c->selectionController->PrevLine();
-	}
+    CMD_NAME("grid/line/prev")
+    STR_MENU("Previous Line")
+    STR_DISP("Previous Line")
+    STR_HELP("Move to the previous line")
+
+    void operator()(agi::Context *c) override {
+        c->selectionController->PrevLine();
+    }
 };
 
 struct grid_sort_actor final : public Command {
-	CMD_NAME("grid/sort/actor")
-	STR_MENU("&Actor Name")
-	STR_DISP("Actor Name")
-	STR_HELP("Sort all subtitles by their actor names")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompActor);
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/actor")
+    STR_MENU("&Actor Name")
+    STR_DISP("Actor Name")
+    STR_HELP("Sort all subtitles by their actor names")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompActor);
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct validate_sel_multiple : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() > 1;
-	}
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() > 1;
+    }
 };
 
 struct grid_sort_actor_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/actor/selected")
-	STR_MENU("&Actor Name")
-	STR_DISP("Actor Name")
-	STR_HELP("Sort selected subtitles by their actor names")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompActor, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/actor/selected")
+    STR_MENU("&Actor Name")
+    STR_DISP("Actor Name")
+    STR_HELP("Sort selected subtitles by their actor names")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompActor, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_effect final : public Command {
-	CMD_NAME("grid/sort/effect")
-	STR_MENU("&Effect")
-	STR_DISP("Effect")
-	STR_HELP("Sort all subtitles by their effects")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompEffect);
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/effect")
+    STR_MENU("&Effect")
+    STR_DISP("Effect")
+    STR_HELP("Sort all subtitles by their effects")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompEffect);
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_effect_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/effect/selected")
-	STR_MENU("&Effect")
-	STR_DISP("Effect")
-	STR_HELP("Sort selected subtitles by their effects")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompEffect, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/effect/selected")
+    STR_MENU("&Effect")
+    STR_DISP("Effect")
+    STR_HELP("Sort selected subtitles by their effects")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompEffect, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_end final : public Command {
-	CMD_NAME("grid/sort/end")
-	STR_MENU("&End Time")
-	STR_DISP("End Time")
-	STR_HELP("Sort all subtitles by their end times")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompEnd);
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/end")
+    STR_MENU("&End Time")
+    STR_DISP("End Time")
+    STR_HELP("Sort all subtitles by their end times")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompEnd);
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_end_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/end/selected")
-	STR_MENU("&End Time")
-	STR_DISP("End Time")
-	STR_HELP("Sort selected subtitles by their end times")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompEnd, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/end/selected")
+    STR_MENU("&End Time")
+    STR_DISP("End Time")
+    STR_HELP("Sort selected subtitles by their end times")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompEnd, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_layer final : public Command {
-	CMD_NAME("grid/sort/layer")
-	STR_MENU("&Layer")
-	STR_DISP("Layer")
-	STR_HELP("Sort all subtitles by their layer number")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompLayer);
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/layer")
+    STR_MENU("&Layer")
+    STR_DISP("Layer")
+    STR_HELP("Sort all subtitles by their layer number")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompLayer);
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_layer_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/layer/selected")
-	STR_MENU("&Layer")
-	STR_DISP("Layer")
-	STR_HELP("Sort selected subtitles by their layer number")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompLayer, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/layer/selected")
+    STR_MENU("&Layer")
+    STR_DISP("Layer")
+    STR_HELP("Sort selected subtitles by their layer number")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompLayer, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_start final : public Command {
-	CMD_NAME("grid/sort/start")
-	STR_MENU("&Start Time")
-	STR_DISP("Start Time")
-	STR_HELP("Sort all subtitles by their start times")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort();
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/start")
+    STR_MENU("&Start Time")
+    STR_DISP("Start Time")
+    STR_HELP("Sort all subtitles by their start times")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort();
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_start_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/start/selected")
-	STR_MENU("&Start Time")
-	STR_DISP("Start Time")
-	STR_HELP("Sort selected subtitles by their start times")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompStart, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/start/selected")
+    STR_MENU("&Start Time")
+    STR_DISP("Start Time")
+    STR_HELP("Sort selected subtitles by their start times")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompStart, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_style final : public Command {
-	CMD_NAME("grid/sort/style")
-	STR_MENU("St&yle Name")
-	STR_DISP("Style Name")
-	STR_HELP("Sort all subtitles by their style names")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompStyle);
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/style")
+    STR_MENU("St&yle Name")
+    STR_DISP("Style Name")
+    STR_HELP("Sort all subtitles by their style names")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompStyle);
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_sort_style_selected final : public validate_sel_multiple {
-	CMD_NAME("grid/sort/style/selected")
-	STR_MENU("St&yle Name")
-	STR_DISP("Style Name")
-	STR_HELP("Sort selected subtitles by their style names")
-
-	void operator()(agi::Context *c) override {
-		c->ass->Sort(AssFile::CompStyle, c->selectionController->GetSelectedSet());
-		c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/sort/style/selected")
+    STR_MENU("St&yle Name")
+    STR_DISP("Style Name")
+    STR_HELP("Sort selected subtitles by their style names")
+
+    void operator()(agi::Context *c) override {
+        c->ass->Sort(AssFile::CompStyle, c->selectionController->GetSelectedSet());
+        c->ass->Commit(_("sort"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_tag_cycle_hiding final : public Command {
-	CMD_NAME("grid/tag/cycle_hiding")
-	CMD_ICON(toggle_tag_hiding)
-	STR_MENU("Cycle Tag Hiding Mode")
-	STR_DISP("Cycle Tag Hiding Mode")
-	STR_HELP("Cycle through tag hiding modes")
-
-	void operator()(agi::Context *c) override {
-		int tagMode = OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt();
-
-		// Cycle to next
-		tagMode = (tagMode+1)%3;
-
-		// Show on status bar
-		wxString message;
-		if (tagMode == 0) message = _("ASS Override Tag mode set to show full tags.");
-		if (tagMode == 1) message = _("ASS Override Tag mode set to simplify tags.");
-		if (tagMode == 2) message = _("ASS Override Tag mode set to hide tags.");
-		c->frame->StatusTimeout(message,10000);
-
-		// Set option
-		OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(tagMode);
-	}
+    CMD_NAME("grid/tag/cycle_hiding")
+    CMD_ICON(toggle_tag_hiding)
+    STR_MENU("Cycle Tag Hiding Mode")
+    STR_DISP("Cycle Tag Hiding Mode")
+    STR_HELP("Cycle through tag hiding modes")
+
+    void operator()(agi::Context *c) override {
+        int tagMode = OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt();
+
+        // Cycle to next
+        tagMode = (tagMode + 1) % 3;
+
+        // Show on status bar
+        wxString message;
+        if (tagMode == 0) message = _("ASS Override Tag mode set to show full tags.");
+        if (tagMode == 1) message = _("ASS Override Tag mode set to simplify tags.");
+        if (tagMode == 2) message = _("ASS Override Tag mode set to hide tags.");
+        c->frame->StatusTimeout(message, 10000);
+
+        // Set option
+        OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(tagMode);
+    }
 };
 
 struct grid_tags_hide final : public Command {
-	CMD_NAME("grid/tags/hide")
-	STR_MENU("&Hide Tags")
-	STR_DISP("Hide Tags")
-	STR_HELP("Hide override tags in the subtitle grid")
-	CMD_TYPE(COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 2;
-	}
-
-	void operator()(agi::Context *) override {
-		OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(2);
-	}
+    CMD_NAME("grid/tags/hide")
+    STR_MENU("&Hide Tags")
+    STR_DISP("Hide Tags")
+    STR_HELP("Hide override tags in the subtitle grid")
+    CMD_TYPE(COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 2;
+    }
+
+    void operator()(agi::Context *) override {
+        OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(2);
+    }
 };
 
 struct grid_tags_show final : public Command {
-	CMD_NAME("grid/tags/show")
-	STR_MENU("Sh&ow Tags")
-	STR_DISP("Show Tags")
-	STR_HELP("Show full override tags in the subtitle grid")
-	CMD_TYPE(COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 0;
-	}
-
-	void operator()(agi::Context *) override {
-		OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(0);
-	}
+    CMD_NAME("grid/tags/show")
+    STR_MENU("Sh&ow Tags")
+    STR_DISP("Show Tags")
+    STR_HELP("Show full override tags in the subtitle grid")
+    CMD_TYPE(COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 0;
+    }
+
+    void operator()(agi::Context *) override {
+        OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(0);
+    }
 };
 
 struct grid_tags_simplify final : public Command {
-	CMD_NAME("grid/tags/simplify")
-	STR_MENU("S&implify Tags")
-	STR_DISP("Simplify Tags")
-	STR_HELP("Replace override tags in the subtitle grid with a simplified placeholder")
-	CMD_TYPE(COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 1;
-	}
-
-	void operator()(agi::Context *) override {
-		OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(1);
-	}
+    CMD_NAME("grid/tags/simplify")
+    STR_MENU("S&implify Tags")
+    STR_DISP("Simplify Tags")
+    STR_HELP("Replace override tags in the subtitle grid with a simplified placeholder")
+    CMD_TYPE(COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Subtitle/Grid/Hide Overrides")->GetInt() == 1;
+    }
+
+    void operator()(agi::Context *) override {
+        OPT_SET("Subtitle/Grid/Hide Overrides")->SetInt(1);
+    }
 };
 
 template<class T, class U>
-static bool move_one(T begin, T end, U const& to_move, bool swap) {
-	size_t move_count = 0;
-	auto prev = end;
-	for (auto it = begin; it != end; ++it) {
-		auto cur = &*it;
-		if (!to_move.count(cur))
-			prev = it;
-		else if (prev != end) {
-			it->swap_nodes(*prev);
-			if (swap)
-				std::swap(prev, it);
-			else
-				prev = it;
-			if (++move_count == to_move.size())
-				break;
-		}
-	}
-
-	return move_count > 0;
+static bool move_one(T begin, T end, U const &to_move, bool swap)
+{
+    size_t move_count = 0;
+    auto prev = end;
+    for (auto it = begin; it != end; ++it) {
+        auto cur = &*it;
+        if (!to_move.count(cur))
+            prev = it;
+        else if (prev != end) {
+            it->swap_nodes(*prev);
+            if (swap)
+                std::swap(prev, it);
+            else
+                prev = it;
+            if (++move_count == to_move.size())
+                break;
+        }
+    }
+
+    return move_count > 0;
 }
 
 struct grid_move_up final : public Command {
-	CMD_NAME("grid/move/up")
-	STR_MENU("Move line up")
-	STR_DISP("Move line up")
-	STR_HELP("Move the selected lines up one row")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() != 0;
-	}
-
-	void operator()(agi::Context *c) override {
-		if (move_one(c->ass->Events.begin(), c->ass->Events.end(), c->selectionController->GetSelectedSet(), false))
-			c->ass->Commit(_("move lines"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/move/up")
+    STR_MENU("Move line up")
+    STR_DISP("Move line up")
+    STR_HELP("Move the selected lines up one row")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() != 0;
+    }
+
+    void operator()(agi::Context *c) override {
+        if (move_one(c->ass->Events.begin(), c->ass->Events.end(), c->selectionController->GetSelectedSet(), false))
+            c->ass->Commit(_("move lines"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_move_down final : public Command {
-	CMD_NAME("grid/move/down")
-	STR_MENU("Move line down")
-	STR_DISP("Move line down")
-	STR_HELP("Move the selected lines down one row")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() != 0;
-	}
-
-	void operator()(agi::Context *c) override {
-		if (move_one(c->ass->Events.rbegin(), c->ass->Events.rend(), c->selectionController->GetSelectedSet(), true))
-			c->ass->Commit(_("move lines"), AssFile::COMMIT_ORDER);
-	}
+    CMD_NAME("grid/move/down")
+    STR_MENU("Move line down")
+    STR_DISP("Move line down")
+    STR_HELP("Move the selected lines down one row")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() != 0;
+    }
+
+    void operator()(agi::Context *c) override {
+        if (move_one(c->ass->Events.rbegin(), c->ass->Events.rend(), c->selectionController->GetSelectedSet(), true))
+            c->ass->Commit(_("move lines"), AssFile::COMMIT_ORDER);
+    }
 };
 
 struct grid_swap final : public Command {
-	CMD_NAME("grid/swap")
-	CMD_ICON(arrow_sort)
-	STR_MENU("Swap Lines")
-	STR_DISP("Swap Lines")
-	STR_HELP("Swap the two selected lines")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return c->selectionController->GetSelectedSet().size() == 2;
-	}
-
-	void operator()(agi::Context *c) override {
-		auto const& sel = c->selectionController->GetSelectedSet();
-		if (sel.size() == 2) {
-			(*sel.begin())->swap_nodes(**sel.rbegin());
-			c->ass->Commit(_("swap lines"), AssFile::COMMIT_ORDER);
-		}
-	}
+    CMD_NAME("grid/swap")
+    CMD_ICON(arrow_sort)
+    STR_MENU("Swap Lines")
+    STR_DISP("Swap Lines")
+    STR_HELP("Swap the two selected lines")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return c->selectionController->GetSelectedSet().size() == 2;
+    }
+
+    void operator()(agi::Context *c) override {
+        auto const &sel = c->selectionController->GetSelectedSet();
+        if (sel.size() == 2) {
+            (*sel.begin())->swap_nodes(**sel.rbegin());
+            c->ass->Commit(_("swap lines"), AssFile::COMMIT_ORDER);
+        }
+    }
 };
 
 }
 
 namespace cmd {
-	void init_grid() {
-		reg(agi::make_unique<grid_line_next>());
-		reg(agi::make_unique<grid_line_next_create>());
-		reg(agi::make_unique<grid_line_prev>());
-		reg(agi::make_unique<grid_sort_actor>());
-		reg(agi::make_unique<grid_sort_effect>());
-		reg(agi::make_unique<grid_sort_end>());
-		reg(agi::make_unique<grid_sort_layer>());
-		reg(agi::make_unique<grid_sort_start>());
-		reg(agi::make_unique<grid_sort_style>());
-		reg(agi::make_unique<grid_sort_actor_selected>());
-		reg(agi::make_unique<grid_sort_effect_selected>());
-		reg(agi::make_unique<grid_sort_end_selected>());
-		reg(agi::make_unique<grid_sort_layer_selected>());
-		reg(agi::make_unique<grid_sort_start_selected>());
-		reg(agi::make_unique<grid_sort_style_selected>());
-		reg(agi::make_unique<grid_move_down>());
-		reg(agi::make_unique<grid_move_up>());
-		reg(agi::make_unique<grid_swap>());
-		reg(agi::make_unique<grid_tag_cycle_hiding>());
-		reg(agi::make_unique<grid_tags_hide>());
-		reg(agi::make_unique<grid_tags_show>());
-		reg(agi::make_unique<grid_tags_simplify>());
-	}
+void init_grid()
+{
+    reg(agi::make_unique<grid_line_next>());
+    reg(agi::make_unique<grid_line_next_create>());
+    reg(agi::make_unique<grid_line_prev>());
+    reg(agi::make_unique<grid_sort_actor>());
+    reg(agi::make_unique<grid_sort_effect>());
+    reg(agi::make_unique<grid_sort_end>());
+    reg(agi::make_unique<grid_sort_layer>());
+    reg(agi::make_unique<grid_sort_start>());
+    reg(agi::make_unique<grid_sort_style>());
+    reg(agi::make_unique<grid_sort_actor_selected>());
+    reg(agi::make_unique<grid_sort_effect_selected>());
+    reg(agi::make_unique<grid_sort_end_selected>());
+    reg(agi::make_unique<grid_sort_layer_selected>());
+    reg(agi::make_unique<grid_sort_start_selected>());
+    reg(agi::make_unique<grid_sort_style_selected>());
+    reg(agi::make_unique<grid_move_down>());
+    reg(agi::make_unique<grid_move_up>());
+    reg(agi::make_unique<grid_swap>());
+    reg(agi::make_unique<grid_tag_cycle_hiding>());
+    reg(agi::make_unique<grid_tags_hide>());
+    reg(agi::make_unique<grid_tags_show>());
+    reg(agi::make_unique<grid_tags_simplify>());
+}
 }
diff --git a/src/command/help.cpp b/src/command/help.cpp
index 6b349e8b541c87fd580d6a5ef1f189349dfb94d0..eee2202a9711036a1bf0d989ace80f739dee869a 100644
--- a/src/command/help.cpp
+++ b/src/command/help.cpp
@@ -40,99 +40,101 @@
 #include <wx/msgdlg.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct help_bugs final : public Command {
-	CMD_NAME("help/bugs")
-	CMD_ICON(bugtracker_button)
-	STR_MENU("&Bug Tracker...")
-	STR_DISP("Bug Tracker")
-	STR_HELP("Visit Aegisub's bug tracker to report bugs and request new features")
-
-	void operator()(agi::Context *c) override {
-		if (wxGetMouseState().CmdDown()) {
-			if (wxGetMouseState().ShiftDown()) {
-				 wxMessageBox("Now crashing with an access violation...");
-				for (char *foo = (char*)nullptr;;) *foo++ = 42;
-			} else {
-				wxMessageBox("Now crashing with an unhandled exception...");
-				throw c->parent;
-			}
-		}
-		wxLaunchDefaultBrowser("https://github.com/Aegisub/Aegisub/issues", wxBROWSER_NEW_WINDOW);
-	}
+    CMD_NAME("help/bugs")
+    CMD_ICON(bugtracker_button)
+    STR_MENU("&Bug Tracker...")
+    STR_DISP("Bug Tracker")
+    STR_HELP("Visit Aegisub's bug tracker to report bugs and request new features")
+
+    void operator()(agi::Context *c) override {
+        if (wxGetMouseState().CmdDown()) {
+            if (wxGetMouseState().ShiftDown()) {
+                wxMessageBox("Now crashing with an access violation...");
+                for (char *foo = (char *)nullptr;;) *foo++ = 42;
+            }
+            else {
+                wxMessageBox("Now crashing with an unhandled exception...");
+                throw c->parent;
+            }
+        }
+        wxLaunchDefaultBrowser("https://github.com/Aegisub/Aegisub/issues", wxBROWSER_NEW_WINDOW);
+    }
 };
 
 struct help_contents final : public Command {
-	CMD_NAME("help/contents")
-	CMD_ICON(contents_button)
-	STR_MENU("&Contents")
-	STR_DISP("Contents")
-	STR_HELP("Help topics")
-
-	void operator()(agi::Context *) override {
-		HelpButton::OpenPage("Main");
-	}
+    CMD_NAME("help/contents")
+    CMD_ICON(contents_button)
+    STR_MENU("&Contents")
+    STR_DISP("Contents")
+    STR_HELP("Help topics")
+
+    void operator()(agi::Context *) override {
+        HelpButton::OpenPage("Main");
+    }
 };
 
 struct help_irc final : public Command {
-	CMD_NAME("help/irc")
-	CMD_ICON(irc_button)
-	STR_MENU("&IRC Channel")
-	STR_DISP("IRC Channel")
-	STR_HELP("Visit Aegisub's official IRC channel")
-
-	void operator()(agi::Context *) override {
-		wxLaunchDefaultBrowser("irc://irc.rizon.net/aegisub", wxBROWSER_NEW_WINDOW);
-	}
+    CMD_NAME("help/irc")
+    CMD_ICON(irc_button)
+    STR_MENU("&IRC Channel")
+    STR_DISP("IRC Channel")
+    STR_HELP("Visit Aegisub's official IRC channel")
+
+    void operator()(agi::Context *) override {
+        wxLaunchDefaultBrowser("irc://irc.rizon.net/aegisub", wxBROWSER_NEW_WINDOW);
+    }
 };
 
 struct help_video final : public Command {
-	CMD_NAME("help/video")
-	CMD_ICON(visual_help)
-	STR_MENU("&Visual Typesetting")
-	STR_DISP("Visual Typesetting")
-	STR_HELP("Open the manual page for Visual Typesetting")
-
-	void operator()(agi::Context *) override {
-		HelpButton::OpenPage("Visual Typesetting");
-	}
+    CMD_NAME("help/video")
+    CMD_ICON(visual_help)
+    STR_MENU("&Visual Typesetting")
+    STR_DISP("Visual Typesetting")
+    STR_HELP("Open the manual page for Visual Typesetting")
+
+    void operator()(agi::Context *) override {
+        HelpButton::OpenPage("Visual Typesetting");
+    }
 };
 
 struct help_website final : public Command {
-	CMD_NAME("help/website")
-	CMD_ICON(website_button)
-	STR_MENU("&Website")
-	STR_DISP("Website")
-	STR_HELP("Visit Aegisub's official website")
-
-	void operator()(agi::Context *) override {
-		wxLaunchDefaultBrowser("http://www.aegisub.org/", wxBROWSER_NEW_WINDOW);
-	}
+    CMD_NAME("help/website")
+    CMD_ICON(website_button)
+    STR_MENU("&Website")
+    STR_DISP("Website")
+    STR_HELP("Visit Aegisub's official website")
+
+    void operator()(agi::Context *) override {
+        wxLaunchDefaultBrowser("http://www.aegisub.org/", wxBROWSER_NEW_WINDOW);
+    }
 };
 
 struct help_joysound_export final : public Command {
-	CMD_NAME("help/joysound_exporter")
-	CMD_ICON(website_button)
-	STR_MENU("&Joysound Exporter")
-	STR_DISP("Joysound Exporter")
-	STR_HELP("Export karaoke from Joysound")
-
-	void operator()(agi::Context *) override {
-		wxLaunchDefaultBrowser("https://joysound.rhiobet.ninja/", wxBROWSER_NEW_WINDOW);
-	}
+    CMD_NAME("help/joysound_exporter")
+    CMD_ICON(website_button)
+    STR_MENU("&Joysound Exporter")
+    STR_DISP("Joysound Exporter")
+    STR_HELP("Export karaoke from Joysound")
+
+    void operator()(agi::Context *) override {
+        wxLaunchDefaultBrowser("https://joysound.rhiobet.ninja/", wxBROWSER_NEW_WINDOW);
+    }
 };
 
 }
 
 
 namespace cmd {
-	void init_help() {
-		reg(agi::make_unique<help_bugs>());
-		reg(agi::make_unique<help_contents>());
-		reg(agi::make_unique<help_irc>());
-		reg(agi::make_unique<help_video>());
-		reg(agi::make_unique<help_website>());
-		reg(agi::make_unique<help_joysound_export>());
-	}
+void init_help()
+{
+    reg(agi::make_unique<help_bugs>());
+    reg(agi::make_unique<help_contents>());
+    reg(agi::make_unique<help_irc>());
+    reg(agi::make_unique<help_video>());
+    reg(agi::make_unique<help_website>());
+    reg(agi::make_unique<help_joysound_export>());
+}
 }
diff --git a/src/command/keyframe.cpp b/src/command/keyframe.cpp
index 29e1739706d7a40a2909153ff172f1aec2eb8ddb..78892c524123b77b5f92e5cc73f5b2c4e150aaa3 100644
--- a/src/command/keyframe.cpp
+++ b/src/command/keyframe.cpp
@@ -42,72 +42,73 @@
 #include <libaegisub/make_unique.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct keyframe_close final : public Command {
-	CMD_NAME("keyframe/close")
-	CMD_ICON(close_keyframes_menu)
-	STR_MENU("Close Keyframes")
-	STR_DISP("Close Keyframes")
-	STR_HELP("Discard the currently loaded keyframes and use those from the video, if any")
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_NAME("keyframe/close")
+    CMD_ICON(close_keyframes_menu)
+    STR_MENU("Close Keyframes")
+    STR_DISP("Close Keyframes")
+    STR_HELP("Discard the currently loaded keyframes and use those from the video, if any")
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return c->project->CanCloseKeyframes();
-	}
+    bool Validate(const agi::Context *c) override {
+        return c->project->CanCloseKeyframes();
+    }
 
-	void operator()(agi::Context *c) override {
-		c->project->CloseKeyframes();
-	}
+    void operator()(agi::Context *c) override {
+        c->project->CloseKeyframes();
+    }
 };
 
 struct keyframe_open final : public Command {
-	CMD_NAME("keyframe/open")
-	CMD_ICON(open_keyframes_menu)
-	STR_MENU("Open Keyframes...")
-	STR_DISP("Open Keyframes")
-	STR_HELP("Open a keyframe list file")
+    CMD_NAME("keyframe/open")
+    CMD_ICON(open_keyframes_menu)
+    STR_MENU("Open Keyframes...")
+    STR_DISP("Open Keyframes")
+    STR_HELP("Open a keyframe list file")
 
-	void operator()(agi::Context *c) override {
-		auto filename = OpenFileSelector(
-			_("Open keyframes file"),
-			"Path/Last/Keyframes", "" ,".txt",
-			from_wx(_("All Supported Formats") +
-				" (*.txt, *.pass, *.stats, *.log)|*.txt;*.pass;*.stats;*.log|" +
-				_("All Files") + " (*.*)|*.*"),
-			c->parent);
+    void operator()(agi::Context *c) override {
+        auto filename = OpenFileSelector(
+                            _("Open keyframes file"),
+                            "Path/Last/Keyframes", "", ".txt",
+                            from_wx(_("All Supported Formats") +
+                                    " (*.txt, *.pass, *.stats, *.log)|*.txt;*.pass;*.stats;*.log|" +
+                                    _("All Files") + " (*.*)|*.*"),
+                            c->parent);
 
-		if (!filename.empty())
-			c->project->LoadKeyframes(filename);
-	}
+        if (!filename.empty())
+            c->project->LoadKeyframes(filename);
+    }
 };
 
 struct keyframe_save final : public Command {
-	CMD_NAME("keyframe/save")
-	CMD_ICON(save_keyframes_menu)
-	STR_MENU("Save Keyframes...")
-	STR_DISP("Save Keyframes")
-	STR_HELP("Save the current list of keyframes to a file")
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_NAME("keyframe/save")
+    CMD_ICON(save_keyframes_menu)
+    STR_MENU("Save Keyframes...")
+    STR_DISP("Save Keyframes")
+    STR_HELP("Save the current list of keyframes to a file")
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return !c->project->Keyframes().empty();
-	}
+    bool Validate(const agi::Context *c) override {
+        return !c->project->Keyframes().empty();
+    }
 
-	void operator()(agi::Context *c) override {
-		auto filename = SaveFileSelector(_("Save keyframes file"), "Path/Last/Keyframes", "", "*.key.txt", "Text files (*.txt)|*.txt", c->parent);
-		if (filename.empty()) return;
+    void operator()(agi::Context *c) override {
+        auto filename = SaveFileSelector(_("Save keyframes file"), "Path/Last/Keyframes", "", "*.key.txt", "Text files (*.txt)|*.txt", c->parent);
+        if (filename.empty()) return;
 
-		agi::keyframe::Save(filename, c->project->Keyframes());
-		config::mru->Add("Keyframes", filename);
-	}
+        agi::keyframe::Save(filename, c->project->Keyframes());
+        config::mru->Add("Keyframes", filename);
+    }
 };
 }
 
 namespace cmd {
-	void init_keyframe() {
-		reg(agi::make_unique<keyframe_close>());
-		reg(agi::make_unique<keyframe_open>());
-		reg(agi::make_unique<keyframe_save>());
-	}
+void init_keyframe()
+{
+    reg(agi::make_unique<keyframe_close>());
+    reg(agi::make_unique<keyframe_open>());
+    reg(agi::make_unique<keyframe_save>());
+}
 }
diff --git a/src/command/recent.cpp b/src/command/recent.cpp
index 6968a132e6f88dd5c9e9402004d7aa3f395134df..28a19900832d0caf91cc9ec4e32c3f637cd20e7d 100644
--- a/src/command/recent.cpp
+++ b/src/command/recent.cpp
@@ -39,7 +39,7 @@
 #include <libaegisub/make_unique.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 COMMAND_GROUP(recent_audio,     "recent/audio",     _("Recent"), _("Recent"), _("Open recent audio"));
 COMMAND_GROUP(recent_keyframes, "recent/keyframe",  _("Recent"), _("Recent"), _("Open recent keyframes"));
@@ -48,92 +48,93 @@ COMMAND_GROUP(recent_timecodes, "recent/timecodes", _("Recent"), _("Recent"), _(
 COMMAND_GROUP(recent_video,     "recent/video",     _("Recent"), _("Recent"), _("Open recent video"));
 
 struct recent_audio_entry : public Command {
-	CMD_NAME("recent/audio/")
-	STR_MENU("Recent")
-	STR_DISP("Recent")
-	STR_HELP("Open recent audio")
-
-	void operator()(agi::Context *c, int id) {
-		c->project->LoadAudio(config::mru->GetEntry("Audio", id));
-	}
+    CMD_NAME("recent/audio/")
+    STR_MENU("Recent")
+    STR_DISP("Recent")
+    STR_HELP("Open recent audio")
+
+    void operator()(agi::Context *c, int id) {
+        c->project->LoadAudio(config::mru->GetEntry("Audio", id));
+    }
 };
 
 struct recent_keyframes_entry : public Command {
-	CMD_NAME("recent/keyframes/")
-	STR_MENU("Recent")
-	STR_DISP("Recent")
-	STR_HELP("Open recent keyframes")
-
-	void operator()(agi::Context *c, int id) {
-		c->project->LoadKeyframes(config::mru->GetEntry("Keyframes", id));
-	}
+    CMD_NAME("recent/keyframes/")
+    STR_MENU("Recent")
+    STR_DISP("Recent")
+    STR_HELP("Open recent keyframes")
+
+    void operator()(agi::Context *c, int id) {
+        c->project->LoadKeyframes(config::mru->GetEntry("Keyframes", id));
+    }
 };
 
 struct recent_subtitle_entry : public Command {
-	CMD_NAME("recent/subtitle/")
-	STR_MENU("Recent")
-	STR_DISP("Recent")
-	STR_HELP("Open recent subtitles")
+    CMD_NAME("recent/subtitle/")
+    STR_MENU("Recent")
+    STR_DISP("Recent")
+    STR_HELP("Open recent subtitles")
 
-	void operator()(agi::Context *c, int id) {
+    void operator()(agi::Context *c, int id) {
 #ifdef __APPLE__
-		wxGetApp().NewProjectContext().project->LoadSubtitles(config::mru->GetEntry("Subtitle", id));
+        wxGetApp().NewProjectContext().project->LoadSubtitles(config::mru->GetEntry("Subtitle", id));
 #else
-		if (c->subsController->TryToClose() == wxCANCEL) return;
-		c->project->LoadSubtitles(config::mru->GetEntry("Subtitle", id));
+        if (c->subsController->TryToClose() == wxCANCEL) return;
+        c->project->LoadSubtitles(config::mru->GetEntry("Subtitle", id));
 #endif
-	}
+    }
 };
 
 struct recent_timecodes_entry : public Command {
-	CMD_NAME("recent/timecodes/")
-	STR_MENU("Recent")
-	STR_DISP("Recent")
-	STR_HELP("Open recent timecodes")
-
-	void operator()(agi::Context *c, int id) {
-		c->project->LoadTimecodes(config::mru->GetEntry("Timecodes", id));
-	}
+    CMD_NAME("recent/timecodes/")
+    STR_MENU("Recent")
+    STR_DISP("Recent")
+    STR_HELP("Open recent timecodes")
+
+    void operator()(agi::Context *c, int id) {
+        c->project->LoadTimecodes(config::mru->GetEntry("Timecodes", id));
+    }
 };
 
 struct recent_video_entry : public Command {
-	CMD_NAME("recent/video/")
-	STR_MENU("Recent")
-	STR_DISP("Recent")
-	STR_HELP("Open recent videos")
-
-	void operator()(agi::Context *c, int id) {
-		c->project->LoadVideo(config::mru->GetEntry("Video", id));
-	}
+    CMD_NAME("recent/video/")
+    STR_MENU("Recent")
+    STR_DISP("Recent")
+    STR_HELP("Open recent videos")
+
+    void operator()(agi::Context *c, int id) {
+        c->project->LoadVideo(config::mru->GetEntry("Video", id));
+    }
 };
 
 template<class T>
 class mru_wrapper final : public T {
-	int id;
-	std::string full_name;
+    int id;
+    std::string full_name;
 public:
-	const char *name() const { return full_name.c_str(); }
-	void operator()(agi::Context *c) {
-		T::operator()(c, id);
-	}
-	mru_wrapper(int id) : id(id) , full_name(T::name() + std::to_string(id)) { }
+    const char *name() const { return full_name.c_str(); }
+    void operator()(agi::Context *c) {
+        T::operator()(c, id);
+    }
+    mru_wrapper(int id) : id(id), full_name(T::name() + std::to_string(id)) { }
 };
 }
 
 namespace cmd {
-	void init_recent() {
-		reg(agi::make_unique<recent_audio>());
-		reg(agi::make_unique<recent_keyframes>());
-		reg(agi::make_unique<recent_subtitle>());
-		reg(agi::make_unique<recent_timecodes>());
-		reg(agi::make_unique<recent_video>());
-
-		for (int i = 0; i < 16; ++i) {
-			reg(agi::make_unique<mru_wrapper<recent_audio_entry>>(i));
-			reg(agi::make_unique<mru_wrapper<recent_keyframes_entry>>(i));
-			reg(agi::make_unique<mru_wrapper<recent_subtitle_entry>>(i));
-			reg(agi::make_unique<mru_wrapper<recent_timecodes_entry>>(i));
-			reg(agi::make_unique<mru_wrapper<recent_video_entry>>(i));
-		}
-	}
+void init_recent()
+{
+    reg(agi::make_unique<recent_audio>());
+    reg(agi::make_unique<recent_keyframes>());
+    reg(agi::make_unique<recent_subtitle>());
+    reg(agi::make_unique<recent_timecodes>());
+    reg(agi::make_unique<recent_video>());
+
+    for (int i = 0; i < 16; ++i) {
+        reg(agi::make_unique<mru_wrapper<recent_audio_entry>>(i));
+        reg(agi::make_unique<mru_wrapper<recent_keyframes_entry>>(i));
+        reg(agi::make_unique<mru_wrapper<recent_subtitle_entry>>(i));
+        reg(agi::make_unique<mru_wrapper<recent_timecodes_entry>>(i));
+        reg(agi::make_unique<mru_wrapper<recent_video_entry>>(i));
+    }
+}
 }
diff --git a/src/command/subtitle.cpp b/src/command/subtitle.cpp
index fe095198ddf387e338176371945e898d95a0cb1b..2c95dcde73ccf103557fffbde3a1f4e4d033521c 100644
--- a/src/command/subtitle.cpp
+++ b/src/command/subtitle.cpp
@@ -58,418 +58,422 @@
 #include <wx/choicdlg.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct validate_nonempty_selection : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return !c->selectionController->GetSelectedSet().empty();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return !c->selectionController->GetSelectedSet().empty();
+    }
 };
 
 struct validate_nonempty_selection_video_loaded : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return c->project->VideoProvider() && !c->selectionController->GetSelectedSet().empty();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return c->project->VideoProvider() && !c->selectionController->GetSelectedSet().empty();
+    }
 };
 
 struct subtitle_attachment final : public Command {
-	CMD_NAME("subtitle/attachment")
-	CMD_ICON(attach_button)
-	STR_MENU("A&ttachments...")
-	STR_DISP("Attachments")
-	STR_HELP("Open the attachment manager dialog")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowAttachmentsDialog(c->parent, c->ass.get());
-	}
+    CMD_NAME("subtitle/attachment")
+    CMD_ICON(attach_button)
+    STR_MENU("A&ttachments...")
+    STR_DISP("Attachments")
+    STR_HELP("Open the attachment manager dialog")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowAttachmentsDialog(c->parent, c->ass.get());
+    }
 };
 
 struct subtitle_find final : public Command {
-	CMD_NAME("subtitle/find")
-	CMD_ICON(find_button)
-	STR_MENU("&Find...")
-	STR_DISP("Find")
-	STR_HELP("Search for text in the subtitles")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		DialogSearchReplace::Show(c, false);
-	}
+    CMD_NAME("subtitle/find")
+    CMD_ICON(find_button)
+    STR_MENU("&Find...")
+    STR_DISP("Find")
+    STR_HELP("Search for text in the subtitles")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        DialogSearchReplace::Show(c, false);
+    }
 };
 
 struct subtitle_find_next final : public Command {
-	CMD_NAME("subtitle/find/next")
-	CMD_ICON(find_next_menu)
-	STR_MENU("Find &Next")
-	STR_DISP("Find Next")
-	STR_HELP("Find next match of last search")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		if (!c->search->FindNext())
-			DialogSearchReplace::Show(c, false);
-	}
+    CMD_NAME("subtitle/find/next")
+    CMD_ICON(find_next_menu)
+    STR_MENU("Find &Next")
+    STR_DISP("Find Next")
+    STR_HELP("Find next match of last search")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        if (!c->search->FindNext())
+            DialogSearchReplace::Show(c, false);
+    }
 };
 
-static void insert_subtitle_at_video(agi::Context *c, bool after) {
-	auto def = new AssDialogue;
-	int video_ms = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START);
-	def->Start = video_ms;
-	def->End = video_ms + OPT_GET("Timing/Default Duration")->GetInt();
-	def->Style = c->selectionController->GetActiveLine()->Style;
+static void insert_subtitle_at_video(agi::Context *c, bool after)
+{
+    auto def = new AssDialogue;
+    int video_ms = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START);
+    def->Start = video_ms;
+    def->End = video_ms + OPT_GET("Timing/Default Duration")->GetInt();
+    def->Style = c->selectionController->GetActiveLine()->Style;
 
-	auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
-	if (after) ++pos;
+    auto pos = c->ass->iterator_to(*c->selectionController->GetActiveLine());
+    if (after) ++pos;
 
-	c->ass->Events.insert(pos, *def);
-	c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
+    c->ass->Events.insert(pos, *def);
+    c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
 
-	c->selectionController->SetSelectionAndActive({ def }, def);
+    c->selectionController->SetSelectionAndActive({ def }, def);
 }
 
 struct subtitle_insert_after final : public validate_nonempty_selection {
-	CMD_NAME("subtitle/insert/after")
-	STR_MENU("&After Current")
-	STR_DISP("After Current")
-	STR_HELP("Insert a new line after the current one")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *active_line = c->selectionController->GetActiveLine();
-
-		auto new_line = new AssDialogue;
-		new_line->Style = active_line->Style;
-		new_line->Start = active_line->End;
-		new_line->End = new_line->Start + OPT_GET("Timing/Default Duration")->GetInt();
-
-		for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
-			AssDialogue *diag = &*it;
-
-			// Limit the line to the available time
-			if (diag->Start >= new_line->Start)
-				new_line->End = std::min(new_line->End, diag->Start);
-
-			// If we just hit the active line, insert the new line after it
-			if (diag == active_line) {
-				++it;
-				c->ass->Events.insert(it, *new_line);
-				--it;
-			}
-		}
-
-		c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
-		c->selectionController->SetSelectionAndActive({ new_line }, new_line);
-	}
+    CMD_NAME("subtitle/insert/after")
+    STR_MENU("&After Current")
+    STR_DISP("After Current")
+    STR_HELP("Insert a new line after the current one")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *active_line = c->selectionController->GetActiveLine();
+
+        auto new_line = new AssDialogue;
+        new_line->Style = active_line->Style;
+        new_line->Start = active_line->End;
+        new_line->End = new_line->Start + OPT_GET("Timing/Default Duration")->GetInt();
+
+        for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
+            AssDialogue *diag = &*it;
+
+            // Limit the line to the available time
+            if (diag->Start >= new_line->Start)
+                new_line->End = std::min(new_line->End, diag->Start);
+
+            // If we just hit the active line, insert the new line after it
+            if (diag == active_line) {
+                ++it;
+                c->ass->Events.insert(it, *new_line);
+                --it;
+            }
+        }
+
+        c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
+        c->selectionController->SetSelectionAndActive({ new_line }, new_line);
+    }
 };
 
 struct subtitle_insert_after_videotime final : public validate_nonempty_selection_video_loaded {
-	CMD_NAME("subtitle/insert/after/videotime")
-	STR_MENU("After Current, at Video Time")
-	STR_DISP("After Current, at Video Time")
-	STR_HELP("Insert a new line after the current one, starting at video time")
-
-	void operator()(agi::Context *c) override {
-		insert_subtitle_at_video(c, true);
-	}
+    CMD_NAME("subtitle/insert/after/videotime")
+    STR_MENU("After Current, at Video Time")
+    STR_DISP("After Current, at Video Time")
+    STR_HELP("Insert a new line after the current one, starting at video time")
+
+    void operator()(agi::Context *c) override {
+        insert_subtitle_at_video(c, true);
+    }
 };
 
 struct subtitle_insert_before final : public validate_nonempty_selection {
-	CMD_NAME("subtitle/insert/before")
-	STR_MENU("&Before Current")
-	STR_DISP("Before Current")
-	STR_HELP("Insert a new line before the current one")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *active_line = c->selectionController->GetActiveLine();
-
-		auto new_line = new AssDialogue;
-		new_line->Style = active_line->Style;
-		new_line->End = active_line->Start;
-		new_line->Start = new_line->End - OPT_GET("Timing/Default Duration")->GetInt();
-
-		for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
-			auto diag = &*it;
-
-			// Limit the line to the available time
-			if (diag->End <= new_line->End)
-				new_line->Start = std::max(new_line->Start, diag->End);
-
-			// If we just hit the active line, insert the new line before it
-			if (diag == active_line)
-				c->ass->Events.insert(it, *new_line);
-		}
-
-		c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
-		c->selectionController->SetSelectionAndActive({ new_line }, new_line);
-	}
+    CMD_NAME("subtitle/insert/before")
+    STR_MENU("&Before Current")
+    STR_DISP("Before Current")
+    STR_HELP("Insert a new line before the current one")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *active_line = c->selectionController->GetActiveLine();
+
+        auto new_line = new AssDialogue;
+        new_line->Style = active_line->Style;
+        new_line->End = active_line->Start;
+        new_line->Start = new_line->End - OPT_GET("Timing/Default Duration")->GetInt();
+
+        for (auto it = c->ass->Events.begin(); it != c->ass->Events.end(); ++it) {
+            auto diag = &*it;
+
+            // Limit the line to the available time
+            if (diag->End <= new_line->End)
+                new_line->Start = std::max(new_line->Start, diag->End);
+
+            // If we just hit the active line, insert the new line before it
+            if (diag == active_line)
+                c->ass->Events.insert(it, *new_line);
+        }
+
+        c->ass->Commit(_("line insertion"), AssFile::COMMIT_DIAG_ADDREM);
+        c->selectionController->SetSelectionAndActive({ new_line }, new_line);
+    }
 };
 
 struct subtitle_insert_before_videotime final : public validate_nonempty_selection_video_loaded {
-	CMD_NAME("subtitle/insert/before/videotime")
-	STR_MENU("Before Current, at Video Time")
-	STR_DISP("Before Current, at Video Time")
-	STR_HELP("Insert a new line before the current one, starting at video time")
-
-	void operator()(agi::Context *c) override {
-		insert_subtitle_at_video(c, false);
-	}
+    CMD_NAME("subtitle/insert/before/videotime")
+    STR_MENU("Before Current, at Video Time")
+    STR_DISP("Before Current, at Video Time")
+    STR_HELP("Insert a new line before the current one, starting at video time")
+
+    void operator()(agi::Context *c) override {
+        insert_subtitle_at_video(c, false);
+    }
 };
 
-bool is_okay_to_close_subtitles(agi::Context *c) {
+bool is_okay_to_close_subtitles(agi::Context *c)
+{
 #ifdef __APPLE__
-	return true;
+    return true;
 #else
-	return c->subsController->TryToClose() != wxCANCEL;
+    return c->subsController->TryToClose() != wxCANCEL;
 #endif
 }
 
-void load_subtitles(agi::Context *c, agi::fs::path const& path, std::string const& encoding="") {
+void load_subtitles(agi::Context *c, agi::fs::path const &path, std::string const &encoding = "")
+{
 #ifdef __APPLE__
-	wxGetApp().NewProjectContext().project->LoadSubtitles(path, encoding);
+    wxGetApp().NewProjectContext().project->LoadSubtitles(path, encoding);
 #else
-	c->project->LoadSubtitles(path, encoding);
+    c->project->LoadSubtitles(path, encoding);
 #endif
 }
 
 struct subtitle_new final : public Command {
-	CMD_NAME("subtitle/new")
-	CMD_ICON(new_toolbutton)
-	STR_MENU("&New Subtitles")
-	STR_DISP("New Subtitles")
-	STR_HELP("New subtitles")
+    CMD_NAME("subtitle/new")
+    CMD_ICON(new_toolbutton)
+    STR_MENU("&New Subtitles")
+    STR_DISP("New Subtitles")
+    STR_HELP("New subtitles")
 
-	void operator()(agi::Context *c) override {
+    void operator()(agi::Context *c) override {
 #ifdef __APPLE__
-		wxGetApp().NewProjectContext();
+        wxGetApp().NewProjectContext();
 #else
-		if (is_okay_to_close_subtitles(c))
-			c->project->CloseSubtitles();
+        if (is_okay_to_close_subtitles(c))
+            c->project->CloseSubtitles();
 #endif
-	}
+    }
 };
 
 struct subtitle_close final : public Command {
-	CMD_NAME("subtitle/close")
-	CMD_ICON(new_toolbutton)
-	STR_MENU("Close")
-	STR_DISP("Close")
-	STR_HELP("Close")
-
-	void operator()(agi::Context *c) override {
-		c->frame->Close();
-	}
+    CMD_NAME("subtitle/close")
+    CMD_ICON(new_toolbutton)
+    STR_MENU("Close")
+    STR_DISP("Close")
+    STR_HELP("Close")
+
+    void operator()(agi::Context *c) override {
+        c->frame->Close();
+    }
 };
 
 struct subtitle_open final : public Command {
-	CMD_NAME("subtitle/open")
-	CMD_ICON(open_toolbutton)
-	STR_MENU("&Open Subtitles...")
-	STR_DISP("Open Subtitles")
-	STR_HELP("Open a subtitles file")
-
-	void operator()(agi::Context *c) override {
-		if (!is_okay_to_close_subtitles(c)) return;
-
-		auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "","", SubtitleFormat::GetWildcards(0), c->parent);
-		if (!filename.empty())
-			load_subtitles(c, filename);
-	}
+    CMD_NAME("subtitle/open")
+    CMD_ICON(open_toolbutton)
+    STR_MENU("&Open Subtitles...")
+    STR_DISP("Open Subtitles")
+    STR_HELP("Open a subtitles file")
+
+    void operator()(agi::Context *c) override {
+        if (!is_okay_to_close_subtitles(c)) return;
+
+        auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "", "", SubtitleFormat::GetWildcards(0), c->parent);
+        if (!filename.empty())
+            load_subtitles(c, filename);
+    }
 };
 
 struct subtitle_open_autosave final : public Command {
-	CMD_NAME("subtitle/open/autosave")
-	STR_MENU("Open A&utosaved Subtitles...")
-	STR_DISP("Open Autosaved Subtitles")
-	STR_HELP("Open a previous version of a file which was autosaved by Aegisub")
-
-	void operator()(agi::Context *c) override {
-		if (!is_okay_to_close_subtitles(c)) return;
-		auto filename = PickAutosaveFile(c->parent);
-		if (!filename.empty())
-			load_subtitles(c, filename);
-	}
+    CMD_NAME("subtitle/open/autosave")
+    STR_MENU("Open A&utosaved Subtitles...")
+    STR_DISP("Open Autosaved Subtitles")
+    STR_HELP("Open a previous version of a file which was autosaved by Aegisub")
+
+    void operator()(agi::Context *c) override {
+        if (!is_okay_to_close_subtitles(c)) return;
+        auto filename = PickAutosaveFile(c->parent);
+        if (!filename.empty())
+            load_subtitles(c, filename);
+    }
 };
 
 struct subtitle_open_charset final : public Command {
-	CMD_NAME("subtitle/open/charset")
-	CMD_ICON(open_with_toolbutton)
-	STR_MENU("Open Subtitles with &Charset...")
-	STR_DISP("Open Subtitles with Charset")
-	STR_HELP("Open a subtitles file with a specific file encoding")
+    CMD_NAME("subtitle/open/charset")
+    CMD_ICON(open_with_toolbutton)
+    STR_MENU("Open Subtitles with &Charset...")
+    STR_DISP("Open Subtitles with Charset")
+    STR_HELP("Open a subtitles file with a specific file encoding")
 
-	void operator()(agi::Context *c) override {
-		if (!is_okay_to_close_subtitles(c)) return;
+    void operator()(agi::Context *c) override {
+        if (!is_okay_to_close_subtitles(c)) return;
 
-		auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "","", SubtitleFormat::GetWildcards(0), c->parent);
-		if (filename.empty()) return;
+        auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "", "", SubtitleFormat::GetWildcards(0), c->parent);
+        if (filename.empty()) return;
 
-		wxString charset = wxGetSingleChoice(_("Choose charset code:"), _("Charset"), agi::charset::GetEncodingsList<wxArrayString>(), c->parent, -1, -1, true, 250, 200);
-		if (charset.empty()) return;
+        wxString charset = wxGetSingleChoice(_("Choose charset code:"), _("Charset"), agi::charset::GetEncodingsList<wxArrayString>(), c->parent, -1, -1, true, 250, 200);
+        if (charset.empty()) return;
 
-		load_subtitles(c, filename, from_wx(charset));
-	}
+        load_subtitles(c, filename, from_wx(charset));
+    }
 };
 
 struct subtitle_open_video final : public Command {
-	CMD_NAME("subtitle/open/video")
-	STR_MENU("Open Subtitles from &Video")
-	STR_DISP("Open Subtitles from Video")
-	STR_HELP("Open the subtitles from the current video file")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	void operator()(agi::Context *c) override {
-		if (c->subsController->TryToClose() == wxCANCEL) return;
-		c->project->LoadSubtitles(c->project->VideoName(), "binary", false);
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return c->project->CanLoadSubtitlesFromVideo();
-	}
+    CMD_NAME("subtitle/open/video")
+    STR_MENU("Open Subtitles from &Video")
+    STR_DISP("Open Subtitles from Video")
+    STR_HELP("Open the subtitles from the current video file")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    void operator()(agi::Context *c) override {
+        if (c->subsController->TryToClose() == wxCANCEL) return;
+        c->project->LoadSubtitles(c->project->VideoName(), "binary", false);
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return c->project->CanLoadSubtitlesFromVideo();
+    }
 };
 
 struct subtitle_properties final : public Command {
-	CMD_NAME("subtitle/properties")
-	CMD_ICON(properties_toolbutton)
-	STR_MENU("&Properties...")
-	STR_DISP("Properties")
-	STR_HELP("Open script properties window")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowPropertiesDialog(c);
-	}
+    CMD_NAME("subtitle/properties")
+    CMD_ICON(properties_toolbutton)
+    STR_MENU("&Properties...")
+    STR_DISP("Properties")
+    STR_HELP("Open script properties window")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowPropertiesDialog(c);
+    }
 };
 
-static void save_subtitles(agi::Context *c, agi::fs::path filename) {
-	if (filename.empty()) {
-		c->videoController->Stop();
-		filename = SaveFileSelector(_("Save subtitles file"), "Path/Last/Subtitles",
-			c->subsController->Filename().stem().string() + ".ass", "ass",
-			"Advanced Substation Alpha (*.ass)|*.ass", c->parent);
-		if (filename.empty()) return;
-	}
-
-	try {
-		c->subsController->Save(filename);
-	}
-	catch (const agi::Exception& err) {
-		wxMessageBox(to_wx(err.GetMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, c->parent);
-	}
-	catch (...) {
-		wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, c->parent);
-	}
+static void save_subtitles(agi::Context *c, agi::fs::path filename)
+{
+    if (filename.empty()) {
+        c->videoController->Stop();
+        filename = SaveFileSelector(_("Save subtitles file"), "Path/Last/Subtitles",
+                                    c->subsController->Filename().stem().string() + ".ass", "ass",
+                                    "Advanced Substation Alpha (*.ass)|*.ass", c->parent);
+        if (filename.empty()) return;
+    }
+
+    try {
+        c->subsController->Save(filename);
+    }
+    catch (const agi::Exception &err) {
+        wxMessageBox(to_wx(err.GetMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, c->parent);
+    }
+    catch (...) {
+        wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, c->parent);
+    }
 }
 
 struct subtitle_save final : public Command {
-	CMD_NAME("subtitle/save")
-	CMD_ICON(save_toolbutton)
-	STR_MENU("&Save Subtitles")
-	STR_DISP("Save Subtitles")
-	STR_HELP("Save the current subtitles")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	void operator()(agi::Context *c) override {
-		save_subtitles(c, c->subsController->CanSave() ? c->subsController->Filename() : "");
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return c->subsController->IsModified();
-	}
+    CMD_NAME("subtitle/save")
+    CMD_ICON(save_toolbutton)
+    STR_MENU("&Save Subtitles")
+    STR_DISP("Save Subtitles")
+    STR_HELP("Save the current subtitles")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    void operator()(agi::Context *c) override {
+        save_subtitles(c, c->subsController->CanSave() ? c->subsController->Filename() : "");
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return c->subsController->IsModified();
+    }
 };
 
 struct subtitle_save_as final : public Command {
-	CMD_NAME("subtitle/save/as")
-	CMD_ICON(save_as_toolbutton)
-	STR_MENU("Save Subtitles &as...")
-	STR_DISP("Save Subtitles as")
-	STR_HELP("Save subtitles with another name")
-
-	void operator()(agi::Context *c) override {
-		save_subtitles(c, "");
-	}
+    CMD_NAME("subtitle/save/as")
+    CMD_ICON(save_as_toolbutton)
+    STR_MENU("Save Subtitles &as...")
+    STR_DISP("Save Subtitles as")
+    STR_HELP("Save subtitles with another name")
+
+    void operator()(agi::Context *c) override {
+        save_subtitles(c, "");
+    }
 };
 
 struct subtitle_select_all final : public Command {
-	CMD_NAME("subtitle/select/all")
-	STR_MENU("Select &All")
-	STR_DISP("Select All")
-	STR_HELP("Select all dialogue lines")
-
-	void operator()(agi::Context *c) override {
-		Selection sel;
-		boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
-		c->selectionController->SetSelectedSet(std::move(sel));
-	}
+    CMD_NAME("subtitle/select/all")
+    STR_MENU("Select &All")
+    STR_DISP("Select All")
+    STR_HELP("Select all dialogue lines")
+
+    void operator()(agi::Context *c) override {
+        Selection sel;
+        boost::copy(c->ass->Events | agi::address_of, inserter(sel, sel.end()));
+        c->selectionController->SetSelectedSet(std::move(sel));
+    }
 };
 
 struct subtitle_select_visible final : public Command {
-	CMD_NAME("subtitle/select/visible")
-	CMD_ICON(select_visible_button)
-	STR_MENU("Select Visible")
-	STR_DISP("Select Visible")
-	STR_HELP("Select all dialogue lines that are visible on the current video frame")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-
-		Selection new_selection;
-		int frame = c->videoController->GetFrameN();
-
-		for (auto& diag : c->ass->Events) {
-			if (c->videoController->FrameAtTime(diag.Start, agi::vfr::START) <= frame &&
-				c->videoController->FrameAtTime(diag.End, agi::vfr::END) >= frame)
-			{
-				if (new_selection.empty())
-					c->selectionController->SetActiveLine(&diag);
-				new_selection.insert(&diag);
-			}
-		}
-
-		c->selectionController->SetSelectedSet(std::move(new_selection));
-	}
-
-	bool Validate(const agi::Context *c) override {
-		return !!c->project->VideoProvider();
-	}
+    CMD_NAME("subtitle/select/visible")
+    CMD_ICON(select_visible_button)
+    STR_MENU("Select Visible")
+    STR_DISP("Select Visible")
+    STR_HELP("Select all dialogue lines that are visible on the current video frame")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+
+        Selection new_selection;
+        int frame = c->videoController->GetFrameN();
+
+        for (auto &diag : c->ass->Events) {
+            if (c->videoController->FrameAtTime(diag.Start, agi::vfr::START) <= frame &&
+                c->videoController->FrameAtTime(diag.End, agi::vfr::END) >= frame) {
+                if (new_selection.empty())
+                    c->selectionController->SetActiveLine(&diag);
+                new_selection.insert(&diag);
+            }
+        }
+
+        c->selectionController->SetSelectedSet(std::move(new_selection));
+    }
+
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->VideoProvider();
+    }
 };
 
 struct subtitle_spellcheck final : public Command {
-	CMD_NAME("subtitle/spellcheck")
-	CMD_ICON(spellcheck_toolbutton)
-	STR_MENU("Spell &Checker...")
-	STR_DISP("Spell Checker")
-	STR_HELP("Open spell checker")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowSpellcheckerDialog(c);
-	}
+    CMD_NAME("subtitle/spellcheck")
+    CMD_ICON(spellcheck_toolbutton)
+    STR_MENU("Spell &Checker...")
+    STR_DISP("Spell Checker")
+    STR_HELP("Open spell checker")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowSpellcheckerDialog(c);
+    }
 };
 }
 
 namespace cmd {
-	void init_subtitle() {
-		reg(agi::make_unique<subtitle_attachment>());
-		reg(agi::make_unique<subtitle_find>());
-		reg(agi::make_unique<subtitle_find_next>());
-		reg(agi::make_unique<subtitle_insert_after>());
-		reg(agi::make_unique<subtitle_insert_after_videotime>());
-		reg(agi::make_unique<subtitle_insert_before>());
-		reg(agi::make_unique<subtitle_insert_before_videotime>());
-		reg(agi::make_unique<subtitle_new>());
-		reg(agi::make_unique<subtitle_close>());
-		reg(agi::make_unique<subtitle_open>());
-		reg(agi::make_unique<subtitle_open_autosave>());
-		reg(agi::make_unique<subtitle_open_charset>());
-		reg(agi::make_unique<subtitle_open_video>());
-		reg(agi::make_unique<subtitle_properties>());
-		reg(agi::make_unique<subtitle_save>());
-		reg(agi::make_unique<subtitle_save_as>());
-		reg(agi::make_unique<subtitle_select_all>());
-		reg(agi::make_unique<subtitle_select_visible>());
-		reg(agi::make_unique<subtitle_spellcheck>());
-	}
+void init_subtitle()
+{
+    reg(agi::make_unique<subtitle_attachment>());
+    reg(agi::make_unique<subtitle_find>());
+    reg(agi::make_unique<subtitle_find_next>());
+    reg(agi::make_unique<subtitle_insert_after>());
+    reg(agi::make_unique<subtitle_insert_after_videotime>());
+    reg(agi::make_unique<subtitle_insert_before>());
+    reg(agi::make_unique<subtitle_insert_before_videotime>());
+    reg(agi::make_unique<subtitle_new>());
+    reg(agi::make_unique<subtitle_close>());
+    reg(agi::make_unique<subtitle_open>());
+    reg(agi::make_unique<subtitle_open_autosave>());
+    reg(agi::make_unique<subtitle_open_charset>());
+    reg(agi::make_unique<subtitle_open_video>());
+    reg(agi::make_unique<subtitle_properties>());
+    reg(agi::make_unique<subtitle_save>());
+    reg(agi::make_unique<subtitle_save_as>());
+    reg(agi::make_unique<subtitle_select_all>());
+    reg(agi::make_unique<subtitle_select_visible>());
+    reg(agi::make_unique<subtitle_spellcheck>());
+}
 }
diff --git a/src/command/time.cpp b/src/command/time.cpp
index f123e654f302a76836651210760cfcd5930af3b3..31380fdd51965be16e3b4b9be232e2e7c632995b 100644
--- a/src/command/time.cpp
+++ b/src/command/time.cpp
@@ -51,459 +51,463 @@
 namespace {
 using cmd::Command;
 
-static inline void toggle(const char *opt) {
-	OPT_SET(opt)->SetBool(!OPT_GET(opt)->GetBool());
+static inline void toggle(const char *opt)
+{
+    OPT_SET(opt)->SetBool(!OPT_GET(opt)->GetBool());
 }
 
 struct validate_video_loaded : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return !!c->project->VideoProvider();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->VideoProvider();
+    }
 };
 
 struct validate_adjoinable : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		size_t sel_size = c->selectionController->GetSelectedSet().size();
-		if (sel_size == 0) return false;
-		if (sel_size == 1 || sel_size == c->ass->Events.size()) return true;
-
-		auto sel = c->selectionController->GetSortedSelection();
-		for (size_t i = 1; i < sel_size; ++i) {
-			if (sel[i]->Row != sel[i - 1]->Row + 1)
-				return false;
-		}
-		return true;
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        size_t sel_size = c->selectionController->GetSelectedSet().size();
+        if (sel_size == 0) return false;
+        if (sel_size == 1 || sel_size == c->ass->Events.size()) return true;
+
+        auto sel = c->selectionController->GetSortedSelection();
+        for (size_t i = 1; i < sel_size; ++i) {
+            if (sel[i]->Row != sel[i - 1]->Row + 1)
+                return false;
+        }
+        return true;
+    }
 };
 
-void adjoin_lines(agi::Context *c, bool set_start) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-	AssDialogue *prev = nullptr;
-	size_t seen = 0;
-	bool prev_sel = false;
-	for (auto& diag : c->ass->Events) {
-		bool cur_sel = !!sel.count(&diag);
-		if (prev) {
-			// One row selections act as if the previous or next line was selected
-			if (set_start && cur_sel && (sel.size() == 1 || prev_sel))
-				diag.Start = prev->End;
-			else if (!set_start && prev_sel && (cur_sel || sel.size() == 1))
-				prev->End = diag.Start;
-		}
-
-		if (seen == sel.size())
-			break;
-
-		if (cur_sel)
-			++seen;
-
-		prev = &diag;
-		prev_sel = cur_sel;
-	}
-
-	c->ass->Commit(_("adjoin"), AssFile::COMMIT_DIAG_TIME);
+void adjoin_lines(agi::Context *c, bool set_start)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    AssDialogue *prev = nullptr;
+    size_t seen = 0;
+    bool prev_sel = false;
+    for (auto &diag : c->ass->Events) {
+        bool cur_sel = !!sel.count(&diag);
+        if (prev) {
+            // One row selections act as if the previous or next line was selected
+            if (set_start && cur_sel && (sel.size() == 1 || prev_sel))
+                diag.Start = prev->End;
+            else if (!set_start && prev_sel && (cur_sel || sel.size() == 1))
+                prev->End = diag.Start;
+        }
+
+        if (seen == sel.size())
+            break;
+
+        if (cur_sel)
+            ++seen;
+
+        prev = &diag;
+        prev_sel = cur_sel;
+    }
+
+    c->ass->Commit(_("adjoin"), AssFile::COMMIT_DIAG_TIME);
 }
 
 struct time_continuous_end final : public validate_adjoinable {
-	CMD_NAME("time/continuous/end")
-	STR_MENU("Change &End")
-	STR_DISP("Change End")
-	STR_HELP("Change end times of lines to the next line's start time")
-
-	void operator()(agi::Context *c) override {
-		adjoin_lines(c, false);
-	}
+    CMD_NAME("time/continuous/end")
+    STR_MENU("Change &End")
+    STR_DISP("Change End")
+    STR_HELP("Change end times of lines to the next line's start time")
+
+    void operator()(agi::Context *c) override {
+        adjoin_lines(c, false);
+    }
 };
 
 struct time_continuous_start final : public validate_adjoinable {
-	CMD_NAME("time/continuous/start")
-	STR_MENU("Change &Start")
-	STR_DISP("Change Start")
-	STR_HELP("Change start times of lines to the previous line's end time")
-
-	void operator()(agi::Context *c) override {
-		adjoin_lines(c, true);
-	}
+    CMD_NAME("time/continuous/start")
+    STR_MENU("Change &Start")
+    STR_DISP("Change Start")
+    STR_HELP("Change start times of lines to the previous line's end time")
+
+    void operator()(agi::Context *c) override {
+        adjoin_lines(c, true);
+    }
 };
 
 struct time_frame_current final : public validate_video_loaded {
-	CMD_NAME("time/frame/current")
-	CMD_ICON(shift_to_frame)
-	STR_MENU("Shift to &Current Frame")
-	STR_DISP("Shift to Current Frame")
-	STR_HELP("Shift selection so that the active line starts at current frame")
+    CMD_NAME("time/frame/current")
+    CMD_ICON(shift_to_frame)
+    STR_MENU("Shift to &Current Frame")
+    STR_DISP("Shift to Current Frame")
+    STR_HELP("Shift selection so that the active line starts at current frame")
 
-	void operator()(agi::Context *c) override {
-		auto const& sel = c->selectionController->GetSelectedSet();
-		const auto active_line = c->selectionController->GetActiveLine();
+    void operator()(agi::Context *c) override {
+        auto const &sel = c->selectionController->GetSelectedSet();
+        const auto active_line = c->selectionController->GetActiveLine();
 
-		if (sel.empty() || !active_line) return;
+        if (sel.empty() || !active_line) return;
 
-		int target_start = std::max(0, c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START));
-		int shift_by = target_start - active_line->Start;
+        int target_start = std::max(0, c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START));
+        int shift_by = target_start - active_line->Start;
 
-		for (auto line : sel) {
-			line->Start = line->Start + shift_by;
-			line->End = line->End + shift_by;
-		}
+        for (auto line : sel) {
+            line->Start = line->Start + shift_by;
+            line->End = line->End + shift_by;
+        }
 
-		c->ass->Commit(_("shift to frame"), AssFile::COMMIT_DIAG_TIME);
-	}
+        c->ass->Commit(_("shift to frame"), AssFile::COMMIT_DIAG_TIME);
+    }
 };
 
 struct time_shift final : public Command {
-	CMD_NAME("time/shift")
-	CMD_ICON(shift_times_toolbutton)
-	STR_MENU("S&hift Times...")
-	STR_DISP("Shift Times")
-	STR_HELP("Shift subtitles by time or frames")
-
-	void operator()(agi::Context *c) override {
-		ShowShiftTimesDialog(c);
-	}
+    CMD_NAME("time/shift")
+    CMD_ICON(shift_times_toolbutton)
+    STR_MENU("S&hift Times...")
+    STR_DISP("Shift Times")
+    STR_HELP("Shift subtitles by time or frames")
+
+    void operator()(agi::Context *c) override {
+        ShowShiftTimesDialog(c);
+    }
 };
 
-static void snap_subs_video(agi::Context *c, bool set_start) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-	if (sel.empty()) return;
+static void snap_subs_video(agi::Context *c, bool set_start)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    if (sel.empty()) return;
 
-	int start = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START);
-	int end = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::END);
+    int start = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::START);
+    int end = c->videoController->TimeAtFrame(c->videoController->GetFrameN(), agi::vfr::END);
 
-	for (auto line : sel) {
-		if (set_start || line->Start > start)
-			line->Start = start;
-		if (!set_start || line->End < end)
-			line->End = end;
-	}
+    for (auto line : sel) {
+        if (set_start || line->Start > start)
+            line->Start = start;
+        if (!set_start || line->End < end)
+            line->End = end;
+    }
 
-	c->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
+    c->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
 }
 
 struct time_snap_end_video final : public validate_video_loaded {
-	CMD_NAME("time/snap/end_video")
-	CMD_ICON(subend_to_video)
-	STR_MENU("Snap &End to Video")
-	STR_DISP("Snap End to Video")
-	STR_HELP("Set end of selected subtitles to current video frame")
-
-	void operator()(agi::Context *c) override {
-		snap_subs_video(c, false);
-	}
+    CMD_NAME("time/snap/end_video")
+    CMD_ICON(subend_to_video)
+    STR_MENU("Snap &End to Video")
+    STR_DISP("Snap End to Video")
+    STR_HELP("Set end of selected subtitles to current video frame")
+
+    void operator()(agi::Context *c) override {
+        snap_subs_video(c, false);
+    }
 };
 
 struct time_snap_scene final : public validate_video_loaded {
-	CMD_NAME("time/snap/scene")
-	CMD_ICON(snap_subs_to_scene)
-	STR_MENU("Snap to S&cene")
-	STR_DISP("Snap to Scene")
-	STR_HELP("Set start and end of subtitles to the keyframes around current video frame")
-
-	void operator()(agi::Context *c) override {
-		auto const& keyframes = c->project->Keyframes();
-		if (keyframes.empty()) return;
-
-		VideoController *con = c->videoController.get();
-		int curFrame = con->GetFrameN();
-		int prev = 0;
-		int next = 0;
-
-		if (curFrame < keyframes.front())
-			next = keyframes.front();
-		else if (curFrame >= keyframes.back()) {
-			prev = keyframes.back();
-			next = c->project->VideoProvider()->GetFrameCount();
-		}
-		else {
-			auto kf = std::lower_bound(keyframes.begin(), keyframes.end(), curFrame);
-			if (*kf == curFrame) {
-				prev = *kf;
-				next = *(kf + 1);
-			}
-			else {
-				prev = *(kf - 1);
-				next = *kf;
-			}
-		}
-
-		int start_ms = con->TimeAtFrame(prev,agi::vfr::START);
-		int end_ms = con->TimeAtFrame(next-1,agi::vfr::END);
-
-		for (auto line : c->selectionController->GetSelectedSet()) {
-			line->Start = start_ms;
-			line->End = end_ms;
-		}
-
-		c->ass->Commit(_("snap to scene"), AssFile::COMMIT_DIAG_TIME);
-	}
+    CMD_NAME("time/snap/scene")
+    CMD_ICON(snap_subs_to_scene)
+    STR_MENU("Snap to S&cene")
+    STR_DISP("Snap to Scene")
+    STR_HELP("Set start and end of subtitles to the keyframes around current video frame")
+
+    void operator()(agi::Context *c) override {
+        auto const &keyframes = c->project->Keyframes();
+        if (keyframes.empty()) return;
+
+        VideoController *con = c->videoController.get();
+        int curFrame = con->GetFrameN();
+        int prev = 0;
+        int next = 0;
+
+        if (curFrame < keyframes.front())
+            next = keyframes.front();
+        else if (curFrame >= keyframes.back()) {
+            prev = keyframes.back();
+            next = c->project->VideoProvider()->GetFrameCount();
+        }
+        else {
+            auto kf = std::lower_bound(keyframes.begin(), keyframes.end(), curFrame);
+            if (*kf == curFrame) {
+                prev = *kf;
+                next = *(kf + 1);
+            }
+            else {
+                prev = *(kf - 1);
+                next = *kf;
+            }
+        }
+
+        int start_ms = con->TimeAtFrame(prev, agi::vfr::START);
+        int end_ms = con->TimeAtFrame(next - 1, agi::vfr::END);
+
+        for (auto line : c->selectionController->GetSelectedSet()) {
+            line->Start = start_ms;
+            line->End = end_ms;
+        }
+
+        c->ass->Commit(_("snap to scene"), AssFile::COMMIT_DIAG_TIME);
+    }
 };
 
 struct time_add_lead_both final : public Command {
-	CMD_NAME("time/lead/both")
-	STR_MENU("Add lead in and out")
-	STR_DISP("Add lead in and out")
-	STR_HELP("Add both lead in and out to the selected lines")
-	void operator()(agi::Context *c) override {
-		if (AudioTimingController *tc = c->audioController->GetTimingController()) {
-			tc->AddLeadIn();
-			tc->AddLeadOut();
-		}
-	}
+    CMD_NAME("time/lead/both")
+    STR_MENU("Add lead in and out")
+    STR_DISP("Add lead in and out")
+    STR_HELP("Add both lead in and out to the selected lines")
+    void operator()(agi::Context *c) override {
+        if (AudioTimingController *tc = c->audioController->GetTimingController()) {
+            tc->AddLeadIn();
+            tc->AddLeadOut();
+        }
+    }
 };
 
 struct time_add_lead_in final : public Command {
-	CMD_NAME("time/lead/in")
-	CMD_ICON(button_leadin)
-	STR_MENU("Add lead in")
-	STR_DISP("Add lead in")
-	STR_HELP("Add the lead in time to the selected lines")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->AddLeadIn();
-	}
+    CMD_NAME("time/lead/in")
+    CMD_ICON(button_leadin)
+    STR_MENU("Add lead in")
+    STR_DISP("Add lead in")
+    STR_HELP("Add the lead in time to the selected lines")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->AddLeadIn();
+    }
 };
 
 struct time_add_lead_out final : public Command {
-	CMD_NAME("time/lead/out")
-	CMD_ICON(button_leadout)
-	STR_MENU("Add lead out")
-	STR_DISP("Add lead out")
-	STR_HELP("Add the lead out time to the selected lines")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->AddLeadOut();
-	}
+    CMD_NAME("time/lead/out")
+    CMD_ICON(button_leadout)
+    STR_MENU("Add lead out")
+    STR_DISP("Add lead out")
+    STR_HELP("Add the lead out time to the selected lines")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->AddLeadOut();
+    }
 };
 
 struct time_length_increase final : public Command {
-	CMD_NAME("time/length/increase")
-	STR_MENU("Increase length")
-	STR_DISP("Increase length")
-	STR_HELP("Increase the length of the current timing unit")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyLength(1, false);
-	}
+    CMD_NAME("time/length/increase")
+    STR_MENU("Increase length")
+    STR_DISP("Increase length")
+    STR_HELP("Increase the length of the current timing unit")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyLength(1, false);
+    }
 };
 
 struct time_length_increase_shift final : public Command {
-	CMD_NAME("time/length/increase/shift")
-	STR_MENU("Increase length and shift")
-	STR_DISP("Increase length and shift")
-	STR_HELP("Increase the length of the current timing unit and shift the following items")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyLength(1, true);
-	}
+    CMD_NAME("time/length/increase/shift")
+    STR_MENU("Increase length and shift")
+    STR_DISP("Increase length and shift")
+    STR_HELP("Increase the length of the current timing unit and shift the following items")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyLength(1, true);
+    }
 };
 
 struct time_length_decrease final : public Command {
-	CMD_NAME("time/length/decrease")
-	STR_MENU("Decrease length")
-	STR_DISP("Decrease length")
-	STR_HELP("Decrease the length of the current timing unit")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyLength(-1, false);
-	}
+    CMD_NAME("time/length/decrease")
+    STR_MENU("Decrease length")
+    STR_DISP("Decrease length")
+    STR_HELP("Decrease the length of the current timing unit")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyLength(-1, false);
+    }
 };
 
 struct time_length_decrease_shift final : public Command {
-	CMD_NAME("time/length/decrease/shift")
-	STR_MENU("Decrease length and shift")
-	STR_DISP("Decrease length and shift")
-	STR_HELP("Decrease the length of the current timing unit and shift the following items")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyLength(-1, true);
-	}
+    CMD_NAME("time/length/decrease/shift")
+    STR_MENU("Decrease length and shift")
+    STR_DISP("Decrease length and shift")
+    STR_HELP("Decrease the length of the current timing unit and shift the following items")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyLength(-1, true);
+    }
 };
 
 struct time_start_increase final : public Command {
-	CMD_NAME("time/start/increase")
-	STR_MENU("Shift start time forward")
-	STR_DISP("Shift start time forward")
-	STR_HELP("Shift the start time of the current timing unit forward")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyStart(1);
-	}
+    CMD_NAME("time/start/increase")
+    STR_MENU("Shift start time forward")
+    STR_DISP("Shift start time forward")
+    STR_HELP("Shift the start time of the current timing unit forward")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyStart(1);
+    }
 };
 
 struct time_start_decrease final : public Command {
-	CMD_NAME("time/start/decrease")
-	STR_MENU("Shift start time backward")
-	STR_DISP("Shift start time backward")
-	STR_HELP("Shift the start time of the current timing unit backward")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->ModifyStart(-1);
-	}
+    CMD_NAME("time/start/decrease")
+    STR_MENU("Shift start time backward")
+    STR_DISP("Shift start time backward")
+    STR_HELP("Shift the start time of the current timing unit backward")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->ModifyStart(-1);
+    }
 };
 
 struct time_snap_start_video final : public validate_video_loaded {
-	CMD_NAME("time/snap/start_video")
-	CMD_ICON(substart_to_video)
-	STR_MENU("Snap &Start to Video")
-	STR_DISP("Snap Start to Video")
-	STR_HELP("Set start of selected subtitles to current video frame")
-
-	void operator()(agi::Context *c) override {
-		snap_subs_video(c, true);
-	}
+    CMD_NAME("time/snap/start_video")
+    CMD_ICON(substart_to_video)
+    STR_MENU("Snap &Start to Video")
+    STR_DISP("Snap Start to Video")
+    STR_HELP("Set start of selected subtitles to current video frame")
+
+    void operator()(agi::Context *c) override {
+        snap_subs_video(c, true);
+    }
 };
 
 struct time_next final : public Command {
-	CMD_NAME("time/next")
-	CMD_ICON(button_next)
-	STR_MENU("Next Line")
-	STR_DISP("Next Line")
-	STR_HELP("Next line or syllable")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->Next(AudioTimingController::TIMING_UNIT);
-	}
+    CMD_NAME("time/next")
+    CMD_ICON(button_next)
+    STR_MENU("Next Line")
+    STR_DISP("Next Line")
+    STR_HELP("Next line or syllable")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->Next(AudioTimingController::TIMING_UNIT);
+    }
 };
 
 struct time_prev final : public Command {
-	CMD_NAME("time/prev")
-	CMD_ICON(button_prev)
-	STR_MENU("Previous Line")
-	STR_DISP("Previous Line")
-	STR_HELP("Previous line or syllable")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController())
-			c->audioController->GetTimingController()->Prev();
-	}
+    CMD_NAME("time/prev")
+    CMD_ICON(button_prev)
+    STR_MENU("Previous Line")
+    STR_DISP("Previous Line")
+    STR_HELP("Previous line or syllable")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController())
+            c->audioController->GetTimingController()->Prev();
+    }
 };
 
 struct time_tap_connect final : public Command {
-	CMD_NAME("time/tap/connect")
-	CMD_ICON(time_tap_connect)
-	STR_MENU("Time tap connect")
-	STR_DISP("Time tap connect")
-	STR_HELP("Set tap marker to audio position, connect next line's start")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return
-			OPT_GET("Timing/Tap To Time")->GetBool() &&
-			c->audioController->IsPlaying();
-	}
-
-	void operator()(agi::Context *c) override {
-		if (c->audioController->IsPlaying()) {
-			AudioTimingController *tc = c->audioController->GetTimingController();
-			if (tc) {
-				int ms = c->audioController->GetPlaybackPosition();
-
-				tc->MoveTapMarker(ms);
-				bool moved_marker = tc->NextTapMarker();
-				if (!moved_marker &&
-						OPT_GET("Audio/Auto/Commit")->GetBool() &&
-						OPT_GET("Audio/Next Line on Commit")->GetBool()) {
-					// go to next line, and then tap again to connect start to the same
-					// time
-					c->selectionController->NextLine();
-					tc->MoveTapMarker(ms);
-					tc->NextTapMarker();
-				}
-			}
-		}
-	}
+    CMD_NAME("time/tap/connect")
+    CMD_ICON(time_tap_connect)
+    STR_MENU("Time tap connect")
+    STR_DISP("Time tap connect")
+    STR_HELP("Set tap marker to audio position, connect next line's start")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return
+            OPT_GET("Timing/Tap To Time")->GetBool() &&
+            c->audioController->IsPlaying();
+    }
+
+    void operator()(agi::Context *c) override {
+        if (c->audioController->IsPlaying()) {
+            AudioTimingController *tc = c->audioController->GetTimingController();
+            if (tc) {
+                int ms = c->audioController->GetPlaybackPosition();
+
+                tc->MoveTapMarker(ms);
+                bool moved_marker = tc->NextTapMarker();
+                if (!moved_marker &&
+                    OPT_GET("Audio/Auto/Commit")->GetBool() &&
+                    OPT_GET("Audio/Next Line on Commit")->GetBool()) {
+                    // go to next line, and then tap again to connect start to the same
+                    // time
+                    c->selectionController->NextLine();
+                    tc->MoveTapMarker(ms);
+                    tc->NextTapMarker();
+                }
+            }
+        }
+    }
 };
 
 struct time_tap_no_connect final : public Command {
-	CMD_NAME("time/tap/no_connect")
-	CMD_ICON(time_tap_no_connect)
-	STR_MENU("Tap marker no connect")
-	STR_DISP("Tap marker no connect")
-	STR_HELP("Set tap marker to audio position, do not connect next line's start")
-	CMD_TYPE(COMMAND_VALIDATE)
-
-	bool Validate(const agi::Context *c) override {
-		return
-			OPT_GET("Timing/Tap To Time")->GetBool() &&
-			c->audioController->IsPlaying();
-	}
-
-	void operator()(agi::Context *c) override {
-		if (c->audioController->IsPlaying()) {
-			AudioTimingController *tc = c->audioController->GetTimingController();
-			if (tc) {
-				int ms = c->audioController->GetPlaybackPosition();
-
-				tc->MoveTapMarker(ms);
-				bool moved_marker = tc->NextTapMarker();
-				if (!moved_marker &&
-						OPT_GET("Audio/Auto/Commit")->GetBool() &&
-						OPT_GET("Audio/Next Line on Commit")->GetBool()) {
-					// go to next line, but don't do anything more
-					c->selectionController->NextLine();
-				}
-			}
-		}
-	}
+    CMD_NAME("time/tap/no_connect")
+    CMD_ICON(time_tap_no_connect)
+    STR_MENU("Tap marker no connect")
+    STR_DISP("Tap marker no connect")
+    STR_HELP("Set tap marker to audio position, do not connect next line's start")
+    CMD_TYPE(COMMAND_VALIDATE)
+
+    bool Validate(const agi::Context *c) override {
+        return
+            OPT_GET("Timing/Tap To Time")->GetBool() &&
+            c->audioController->IsPlaying();
+    }
+
+    void operator()(agi::Context *c) override {
+        if (c->audioController->IsPlaying()) {
+            AudioTimingController *tc = c->audioController->GetTimingController();
+            if (tc) {
+                int ms = c->audioController->GetPlaybackPosition();
+
+                tc->MoveTapMarker(ms);
+                bool moved_marker = tc->NextTapMarker();
+                if (!moved_marker &&
+                    OPT_GET("Audio/Auto/Commit")->GetBool() &&
+                    OPT_GET("Audio/Next Line on Commit")->GetBool()) {
+                    // go to next line, but don't do anything more
+                    c->selectionController->NextLine();
+                }
+            }
+        }
+    }
 };
 
 struct time_opt_tap_to_time final : public Command {
-	CMD_NAME("time/opt/tap_to_time")
-	CMD_ICON(time_opt_tap_to_time)
-	STR_MENU("Enable tap-to-time UI")
-	STR_DISP("Enable tap-to-time UI")
-	STR_HELP("Enable tap-to-time UI")
-	CMD_TYPE(COMMAND_TOGGLE)
-
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Timing/Tap To Time")->GetBool();
-	}
-
-	void operator()(agi::Context *) override {
-		toggle("Timing/Tap To Time");
-	}
+    CMD_NAME("time/opt/tap_to_time")
+    CMD_ICON(time_opt_tap_to_time)
+    STR_MENU("Enable tap-to-time UI")
+    STR_DISP("Enable tap-to-time UI")
+    STR_HELP("Enable tap-to-time UI")
+    CMD_TYPE(COMMAND_TOGGLE)
+
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Timing/Tap To Time")->GetBool();
+    }
+
+    void operator()(agi::Context *) override {
+        toggle("Timing/Tap To Time");
+    }
 };
 
 struct time_add_space final : public Command {
-	CMD_NAME("time/add_space")
-	STR_MENU("Add Space")
-	STR_DISP("Add Space")
-	STR_HELP("Add a space at the end of the current syllab")
-	void operator()(agi::Context *c) override {
-		if (c->audioController->GetTimingController()) {
-			AudioTimingController *tc = c->audioController->GetTimingController();
-			tc->SetCurrentSylText(tc->GetCurrentSylText() + " ");
-			// tc->Next(AudioTimingController::TIMING_UNIT);
-		}
-	}
+    CMD_NAME("time/add_space")
+    STR_MENU("Add Space")
+    STR_DISP("Add Space")
+    STR_HELP("Add a space at the end of the current syllab")
+    void operator()(agi::Context *c) override {
+        if (c->audioController->GetTimingController()) {
+            AudioTimingController *tc = c->audioController->GetTimingController();
+            tc->SetCurrentSylText(tc->GetCurrentSylText() + " ");
+            // tc->Next(AudioTimingController::TIMING_UNIT);
+        }
+    }
 };
 
 }
 
 namespace cmd {
-	void init_time() {
-		reg(agi::make_unique<time_add_lead_both>());
-		reg(agi::make_unique<time_add_lead_in>());
-		reg(agi::make_unique<time_add_lead_out>());
-		reg(agi::make_unique<time_continuous_end>());
-		reg(agi::make_unique<time_continuous_start>());
-		reg(agi::make_unique<time_frame_current>());
-		reg(agi::make_unique<time_length_decrease>());
-		reg(agi::make_unique<time_length_decrease_shift>());
-		reg(agi::make_unique<time_length_increase>());
-		reg(agi::make_unique<time_length_increase_shift>());
-		reg(agi::make_unique<time_tap_connect>());
-		reg(agi::make_unique<time_tap_no_connect>());
-		reg(agi::make_unique<time_next>());
-		reg(agi::make_unique<time_opt_tap_to_time>());
-		reg(agi::make_unique<time_prev>());
-		reg(agi::make_unique<time_shift>());
-		reg(agi::make_unique<time_snap_end_video>());
-		reg(agi::make_unique<time_snap_scene>());
-		reg(agi::make_unique<time_snap_start_video>());
-		reg(agi::make_unique<time_start_decrease>());
-		reg(agi::make_unique<time_start_increase>());
-		reg(agi::make_unique<time_add_space>());
-	}
+void init_time()
+{
+    reg(agi::make_unique<time_add_lead_both>());
+    reg(agi::make_unique<time_add_lead_in>());
+    reg(agi::make_unique<time_add_lead_out>());
+    reg(agi::make_unique<time_continuous_end>());
+    reg(agi::make_unique<time_continuous_start>());
+    reg(agi::make_unique<time_frame_current>());
+    reg(agi::make_unique<time_length_decrease>());
+    reg(agi::make_unique<time_length_decrease_shift>());
+    reg(agi::make_unique<time_length_increase>());
+    reg(agi::make_unique<time_length_increase_shift>());
+    reg(agi::make_unique<time_tap_connect>());
+    reg(agi::make_unique<time_tap_no_connect>());
+    reg(agi::make_unique<time_next>());
+    reg(agi::make_unique<time_opt_tap_to_time>());
+    reg(agi::make_unique<time_prev>());
+    reg(agi::make_unique<time_shift>());
+    reg(agi::make_unique<time_snap_end_video>());
+    reg(agi::make_unique<time_snap_scene>());
+    reg(agi::make_unique<time_snap_start_video>());
+    reg(agi::make_unique<time_start_decrease>());
+    reg(agi::make_unique<time_start_increase>());
+    reg(agi::make_unique<time_add_space>());
+}
 }
diff --git a/src/command/timecode.cpp b/src/command/timecode.cpp
index 6f7fb5faa043470bc0f5a0e4758e3fd1f5290907..49b19d9220c72976e7505490d12200f75950e701 100644
--- a/src/command/timecode.cpp
+++ b/src/command/timecode.cpp
@@ -44,73 +44,74 @@
 #include <wx/msgdlg.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct timecode_close final : public Command {
-	CMD_NAME("timecode/close")
-	CMD_ICON(close_timecodes_menu)
-	STR_MENU("Close Timecodes File")
-	STR_DISP("Close Timecodes File")
-	STR_HELP("Close the currently open timecodes file")
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_NAME("timecode/close")
+    CMD_ICON(close_timecodes_menu)
+    STR_MENU("Close Timecodes File")
+    STR_DISP("Close Timecodes File")
+    STR_HELP("Close the currently open timecodes file")
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return c->project->CanCloseTimecodes();
-	}
+    bool Validate(const agi::Context *c) override {
+        return c->project->CanCloseTimecodes();
+    }
 
-	void operator()(agi::Context *c) override {
-		c->project->CloseTimecodes();
-	}
+    void operator()(agi::Context *c) override {
+        c->project->CloseTimecodes();
+    }
 };
 
 struct timecode_open final : public Command {
-	CMD_NAME("timecode/open")
-	CMD_ICON(open_timecodes_menu)
-	STR_MENU("Open Timecodes File...")
-	STR_DISP("Open Timecodes File")
-	STR_HELP("Open a VFR timecodes v1 or v2 file")
+    CMD_NAME("timecode/open")
+    CMD_ICON(open_timecodes_menu)
+    STR_MENU("Open Timecodes File...")
+    STR_DISP("Open Timecodes File")
+    STR_HELP("Open a VFR timecodes v1 or v2 file")
 
-	void operator()(agi::Context *c) override {
-		auto str = from_wx(_("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*");
-		auto filename = OpenFileSelector(_("Open Timecodes File"), "Path/Last/Timecodes", "", "", str, c->parent);
-		if (!filename.empty())
-			c->project->LoadTimecodes(filename);
-	}
+    void operator()(agi::Context *c) override {
+        auto str = from_wx(_("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*");
+        auto filename = OpenFileSelector(_("Open Timecodes File"), "Path/Last/Timecodes", "", "", str, c->parent);
+        if (!filename.empty())
+            c->project->LoadTimecodes(filename);
+    }
 };
 
 struct timecode_save final : public Command {
-	CMD_NAME("timecode/save")
-	CMD_ICON(save_timecodes_menu)
-	STR_MENU("Save Timecodes File...")
-	STR_DISP("Save Timecodes File")
-	STR_HELP("Save a VFR timecodes v2 file")
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_NAME("timecode/save")
+    CMD_ICON(save_timecodes_menu)
+    STR_MENU("Save Timecodes File...")
+    STR_DISP("Save Timecodes File")
+    STR_HELP("Save a VFR timecodes v2 file")
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return c->project->Timecodes().IsLoaded();
-	}
+    bool Validate(const agi::Context *c) override {
+        return c->project->Timecodes().IsLoaded();
+    }
 
-	void operator()(agi::Context *c) override {
-		auto str = from_wx(_("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*");
-		auto filename = SaveFileSelector(_("Save Timecodes File"), "Path/Last/Timecodes", "", "", str, c->parent);
-		if (filename.empty()) return;
+    void operator()(agi::Context *c) override {
+        auto str = from_wx(_("All Supported Formats") + " (*.txt)|*.txt|" + _("All Files") + " (*.*)|*.*");
+        auto filename = SaveFileSelector(_("Save Timecodes File"), "Path/Last/Timecodes", "", "", str, c->parent);
+        if (filename.empty()) return;
 
-		try {
-			auto provider = c->project->VideoProvider();
-			c->project->Timecodes().Save(filename, provider ? provider->GetFrameCount() : -1);
-			config::mru->Add("Timecodes", filename);
-		}
-		catch (agi::Exception const& err) {
-			wxMessageBox(to_wx(err.GetMessage()), "Error saving timecodes", wxOK | wxICON_ERROR | wxCENTER, c->parent);
-		}
-	}
+        try {
+            auto provider = c->project->VideoProvider();
+            c->project->Timecodes().Save(filename, provider ? provider->GetFrameCount() : -1);
+            config::mru->Add("Timecodes", filename);
+        }
+        catch (agi::Exception const &err) {
+            wxMessageBox(to_wx(err.GetMessage()), "Error saving timecodes", wxOK | wxICON_ERROR | wxCENTER, c->parent);
+        }
+    }
 };
 }
 
 namespace cmd {
-	void init_timecode() {
-		reg(agi::make_unique<timecode_close>());
-		reg(agi::make_unique<timecode_open>());
-		reg(agi::make_unique<timecode_save>());
-	}
+void init_timecode()
+{
+    reg(agi::make_unique<timecode_close>());
+    reg(agi::make_unique<timecode_open>());
+    reg(agi::make_unique<timecode_save>());
+}
 }
diff --git a/src/command/tool.cpp b/src/command/tool.cpp
index 1e8539c6a25e418e3e6ec0b344aacb015a69ce8e..efce446f87b3f9a814a0d688a147e04e2467f6da 100644
--- a/src/command/tool.cpp
+++ b/src/command/tool.cpp
@@ -50,253 +50,254 @@
 #include <wx/utils.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct tool_assdraw final : public Command {
-	CMD_NAME("tool/assdraw")
-	CMD_ICON(assdraw)
-	STR_MENU("ASSDraw3...")
-	STR_DISP("ASSDraw3")
-	STR_HELP("Launch the ASSDraw3 tool for vector drawing")
-
-	void operator()(agi::Context *) override {
-		wxExecute("\"" + config::path->Decode("?data/ASSDraw3.exe").wstring() + "\"");
-	}
+    CMD_NAME("tool/assdraw")
+    CMD_ICON(assdraw)
+    STR_MENU("ASSDraw3...")
+    STR_DISP("ASSDraw3")
+    STR_HELP("Launch the ASSDraw3 tool for vector drawing")
+
+    void operator()(agi::Context *) override {
+        wxExecute("\"" + config::path->Decode("?data/ASSDraw3.exe").wstring() + "\"");
+    }
 };
 
 struct tool_export final : public Command {
-	CMD_NAME("tool/export")
-	CMD_ICON(export_menu)
-	STR_MENU("&Export Subtitles...")
-	STR_DISP("Export Subtitles")
-	STR_HELP("Save a copy of subtitles in a different format or with processing applied to it")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowExportDialog(c);
-	}
+    CMD_NAME("tool/export")
+    CMD_ICON(export_menu)
+    STR_MENU("&Export Subtitles...")
+    STR_DISP("Export Subtitles")
+    STR_HELP("Save a copy of subtitles in a different format or with processing applied to it")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowExportDialog(c);
+    }
 };
 
 struct tool_font_collector final : public Command {
-	CMD_NAME("tool/font_collector")
-	CMD_ICON(font_collector_button)
-	STR_MENU("&Fonts Collector...")
-	STR_DISP("Fonts Collector")
-	STR_HELP("Open fonts collector")
-
-	void operator()(agi::Context *c) override {
-		ShowFontsCollectorDialog(c);
-	}
+    CMD_NAME("tool/font_collector")
+    CMD_ICON(font_collector_button)
+    STR_MENU("&Fonts Collector...")
+    STR_DISP("Fonts Collector")
+    STR_HELP("Open fonts collector")
+
+    void operator()(agi::Context *c) override {
+        ShowFontsCollectorDialog(c);
+    }
 };
 
 struct tool_line_select final : public Command {
-	CMD_NAME("tool/line/select")
-	CMD_ICON(select_lines_button)
-	STR_MENU("S&elect Lines...")
-	STR_DISP("Select Lines")
-	STR_HELP("Select lines based on defined criteria")
-
-	void operator()(agi::Context *c) override {
-		ShowSelectLinesDialog(c);
-	}
+    CMD_NAME("tool/line/select")
+    CMD_ICON(select_lines_button)
+    STR_MENU("S&elect Lines...")
+    STR_DISP("Select Lines")
+    STR_HELP("Select lines based on defined criteria")
+
+    void operator()(agi::Context *c) override {
+        ShowSelectLinesDialog(c);
+    }
 };
 
 struct tool_resampleres final : public Command {
-	CMD_NAME("tool/resampleres")
-	CMD_ICON(resample_toolbutton)
-	STR_MENU("&Resample Resolution...")
-	STR_DISP("Resample Resolution")
-	STR_HELP("Resample subtitles to maintain their current appearance at a different script resolution")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ResampleSettings settings;
-		if (PromptForResampleSettings(c, settings))
-			ResampleResolution(c->ass.get(), settings);
-	}
+    CMD_NAME("tool/resampleres")
+    CMD_ICON(resample_toolbutton)
+    STR_MENU("&Resample Resolution...")
+    STR_DISP("Resample Resolution")
+    STR_HELP("Resample subtitles to maintain their current appearance at a different script resolution")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ResampleSettings settings;
+        if (PromptForResampleSettings(c, settings))
+            ResampleResolution(c->ass.get(), settings);
+    }
 };
 
 struct tool_style_assistant final : public Command {
-	CMD_NAME("tool/style/assistant")
-	CMD_ICON(styling_toolbutton)
-	STR_MENU("St&yling Assistant...")
-	STR_DISP("Styling Assistant")
-	STR_HELP("Open styling assistant")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Show<DialogStyling>(c);
-	}
+    CMD_NAME("tool/style/assistant")
+    CMD_ICON(styling_toolbutton)
+    STR_MENU("St&yling Assistant...")
+    STR_DISP("Styling Assistant")
+    STR_HELP("Open styling assistant")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Show<DialogStyling>(c);
+    }
 };
 
 struct tool_styling_assistant_validator : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return !!c->dialog->Get<DialogStyling>();
-	}
+    bool Validate(const agi::Context *c) override {
+        return !!c->dialog->Get<DialogStyling>();
+    }
 };
 
 struct tool_styling_assistant_commit final : public tool_styling_assistant_validator {
-	CMD_NAME("tool/styling_assistant/commit")
-	STR_MENU("&Accept changes")
-	STR_DISP("Accept changes")
-	STR_HELP("Commit changes and move to the next line")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogStyling>()->Commit(true);
-	}
+    CMD_NAME("tool/styling_assistant/commit")
+    STR_MENU("&Accept changes")
+    STR_DISP("Accept changes")
+    STR_HELP("Commit changes and move to the next line")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogStyling>()->Commit(true);
+    }
 };
 
 struct tool_styling_assistant_preview final : public tool_styling_assistant_validator {
-	CMD_NAME("tool/styling_assistant/preview")
-	STR_MENU("&Preview changes")
-	STR_DISP("Preview changes")
-	STR_HELP("Commit changes and stay on the current line")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogStyling>()->Commit(false);
-	}
+    CMD_NAME("tool/styling_assistant/preview")
+    STR_MENU("&Preview changes")
+    STR_DISP("Preview changes")
+    STR_HELP("Commit changes and stay on the current line")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogStyling>()->Commit(false);
+    }
 };
 
 struct tool_style_manager final : public Command {
-	CMD_NAME("tool/style/manager")
-	CMD_ICON(style_toolbutton)
-	STR_MENU("&Styles Manager...")
-	STR_DISP("Styles Manager")
-	STR_HELP("Open the styles manager")
-
-	void operator()(agi::Context *c) override {
-		ShowStyleManagerDialog(c);
-	}
+    CMD_NAME("tool/style/manager")
+    CMD_ICON(style_toolbutton)
+    STR_MENU("&Styles Manager...")
+    STR_DISP("Styles Manager")
+    STR_HELP("Open the styles manager")
+
+    void operator()(agi::Context *c) override {
+        ShowStyleManagerDialog(c);
+    }
 };
 
 struct tool_time_kanji final : public Command {
-	CMD_NAME("tool/time/kanji")
-	CMD_ICON(kara_timing_copier)
-	STR_MENU("&Kanji Timer...")
-	STR_DISP("Kanji Timer")
-	STR_HELP("Open the Kanji timer copier")
-
-	void operator()(agi::Context *c) override {
-		ShowKanjiTimerDialog(c);
-	}
+    CMD_NAME("tool/time/kanji")
+    CMD_ICON(kara_timing_copier)
+    STR_MENU("&Kanji Timer...")
+    STR_DISP("Kanji Timer")
+    STR_HELP("Open the Kanji timer copier")
+
+    void operator()(agi::Context *c) override {
+        ShowKanjiTimerDialog(c);
+    }
 };
 
 struct tool_time_postprocess final : public Command {
-	CMD_NAME("tool/time/postprocess")
-	CMD_ICON(timing_processor_toolbutton)
-	STR_MENU("&Timing Post-Processor...")
-	STR_DISP("Timing Post-Processor")
-	STR_HELP("Post-process the subtitle timing to add lead-ins and lead-outs, snap timing to scene changes, etc.")
-
-	void operator()(agi::Context *c) override {
-		ShowTimingProcessorDialog(c);
-	}
+    CMD_NAME("tool/time/postprocess")
+    CMD_ICON(timing_processor_toolbutton)
+    STR_MENU("&Timing Post-Processor...")
+    STR_DISP("Timing Post-Processor")
+    STR_HELP("Post-process the subtitle timing to add lead-ins and lead-outs, snap timing to scene changes, etc.")
+
+    void operator()(agi::Context *c) override {
+        ShowTimingProcessorDialog(c);
+    }
 };
 
 struct tool_translation_assistant final : public Command {
-	CMD_NAME("tool/translation_assistant")
-	CMD_ICON(translation_toolbutton)
-	STR_MENU("&Translation Assistant...")
-	STR_DISP("Translation Assistant")
-	STR_HELP("Open translation assistant")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		try {
-			c->dialog->ShowModal<DialogTranslation>(c);
-		}
-		catch (DialogTranslation::NothingToTranslate const&) {
-			wxMessageBox(_("There is nothing to translate in the file."));
-		}
-	}
+    CMD_NAME("tool/translation_assistant")
+    CMD_ICON(translation_toolbutton)
+    STR_MENU("&Translation Assistant...")
+    STR_DISP("Translation Assistant")
+    STR_HELP("Open translation assistant")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        try {
+            c->dialog->ShowModal<DialogTranslation>(c);
+        }
+        catch (DialogTranslation::NothingToTranslate const &) {
+            wxMessageBox(_("There is nothing to translate in the file."));
+        }
+    }
 };
 
 struct tool_translation_assistant_validator : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
+    CMD_TYPE(COMMAND_VALIDATE)
 
-	bool Validate(const agi::Context *c) override {
-		return !!c->dialog->Get<DialogTranslation>();
-	}
+    bool Validate(const agi::Context *c) override {
+        return !!c->dialog->Get<DialogTranslation>();
+    }
 };
 
 struct tool_translation_assistant_commit final : public tool_translation_assistant_validator {
-	CMD_NAME("tool/translation_assistant/commit")
-	STR_MENU("&Accept changes")
-	STR_DISP("Accept changes")
-	STR_HELP("Commit changes and move to the next line")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogTranslation>()->Commit(true);
-	}
+    CMD_NAME("tool/translation_assistant/commit")
+    STR_MENU("&Accept changes")
+    STR_DISP("Accept changes")
+    STR_HELP("Commit changes and move to the next line")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogTranslation>()->Commit(true);
+    }
 };
 
 struct tool_translation_assistant_preview final : public tool_translation_assistant_validator {
-	CMD_NAME("tool/translation_assistant/preview")
-	STR_MENU("&Preview changes")
-	STR_DISP("Preview changes")
-	STR_HELP("Commit changes and stay on the current line")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogTranslation>()->Commit(false);
-	}
+    CMD_NAME("tool/translation_assistant/preview")
+    STR_MENU("&Preview changes")
+    STR_DISP("Preview changes")
+    STR_HELP("Commit changes and stay on the current line")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogTranslation>()->Commit(false);
+    }
 };
 
 struct tool_translation_assistant_next final : public tool_translation_assistant_validator {
-	CMD_NAME("tool/translation_assistant/next")
-	STR_MENU("&Next Line")
-	STR_DISP("Next Line")
-	STR_HELP("Move to the next line without committing changes")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogTranslation>()->NextBlock();
-	}
+    CMD_NAME("tool/translation_assistant/next")
+    STR_MENU("&Next Line")
+    STR_DISP("Next Line")
+    STR_HELP("Move to the next line without committing changes")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogTranslation>()->NextBlock();
+    }
 };
 
 struct tool_translation_assistant_prev final : public tool_translation_assistant_validator {
-	CMD_NAME("tool/translation_assistant/prev")
-	STR_MENU("&Previous Line")
-	STR_DISP("Previous Line")
-	STR_HELP("Move to the previous line without committing changes")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogTranslation>()->PrevBlock();
-	}
+    CMD_NAME("tool/translation_assistant/prev")
+    STR_MENU("&Previous Line")
+    STR_DISP("Previous Line")
+    STR_HELP("Move to the previous line without committing changes")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogTranslation>()->PrevBlock();
+    }
 };
 
 struct tool_translation_assistant_insert final : public tool_translation_assistant_validator {
-	CMD_NAME("tool/translation_assistant/insert_original")
-	STR_MENU("&Insert Original")
-	STR_DISP("Insert Original")
-	STR_HELP("Insert the untranslated text")
-
-	void operator()(agi::Context *c) override {
-		c->dialog->Get<DialogTranslation>()->InsertOriginal();
-	}
+    CMD_NAME("tool/translation_assistant/insert_original")
+    STR_MENU("&Insert Original")
+    STR_DISP("Insert Original")
+    STR_HELP("Insert the untranslated text")
+
+    void operator()(agi::Context *c) override {
+        c->dialog->Get<DialogTranslation>()->InsertOriginal();
+    }
 };
 }
 
 namespace cmd {
-	void init_tool() {
-		reg(agi::make_unique<tool_export>());
-		reg(agi::make_unique<tool_font_collector>());
-		reg(agi::make_unique<tool_line_select>());
-		reg(agi::make_unique<tool_resampleres>());
-		reg(agi::make_unique<tool_style_assistant>());
-		reg(agi::make_unique<tool_styling_assistant_commit>());
-		reg(agi::make_unique<tool_styling_assistant_preview>());
-		reg(agi::make_unique<tool_style_manager>());
-		reg(agi::make_unique<tool_time_kanji>());
-		reg(agi::make_unique<tool_time_postprocess>());
-		reg(agi::make_unique<tool_translation_assistant>());
+void init_tool()
+{
+    reg(agi::make_unique<tool_export>());
+    reg(agi::make_unique<tool_font_collector>());
+    reg(agi::make_unique<tool_line_select>());
+    reg(agi::make_unique<tool_resampleres>());
+    reg(agi::make_unique<tool_style_assistant>());
+    reg(agi::make_unique<tool_styling_assistant_commit>());
+    reg(agi::make_unique<tool_styling_assistant_preview>());
+    reg(agi::make_unique<tool_style_manager>());
+    reg(agi::make_unique<tool_time_kanji>());
+    reg(agi::make_unique<tool_time_postprocess>());
+    reg(agi::make_unique<tool_translation_assistant>());
 #ifdef _WIN32
-		if (agi::fs::FileExists(config::path->Decode("?data/ASSDraw3.exe")))
-			reg(agi::make_unique<tool_assdraw>());
+    if (agi::fs::FileExists(config::path->Decode("?data/ASSDraw3.exe")))
+        reg(agi::make_unique<tool_assdraw>());
 #endif
-		reg(agi::make_unique<tool_translation_assistant_commit>());
-		reg(agi::make_unique<tool_translation_assistant_preview>());
-		reg(agi::make_unique<tool_translation_assistant_next>());
-		reg(agi::make_unique<tool_translation_assistant_prev>());
-		reg(agi::make_unique<tool_translation_assistant_insert>());
-	}
+    reg(agi::make_unique<tool_translation_assistant_commit>());
+    reg(agi::make_unique<tool_translation_assistant_preview>());
+    reg(agi::make_unique<tool_translation_assistant_next>());
+    reg(agi::make_unique<tool_translation_assistant_prev>());
+    reg(agi::make_unique<tool_translation_assistant_insert>());
+}
 }
diff --git a/src/command/video.cpp b/src/command/video.cpp
index d3ffaf7fe0d2f0cd391aedae56b162a89265fb54..0ea3ca9403e62f3fa5de2e2dd6290c2a28ccefd8 100644
--- a/src/command/video.cpp
+++ b/src/command/video.cpp
@@ -63,718 +63,721 @@
 #include <wx/textdlg.h>
 
 namespace {
-	using cmd::Command;
+using cmd::Command;
 
 struct validator_video_loaded : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return !!c->project->VideoProvider();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->VideoProvider();
+    }
 };
 
 struct validator_video_attached : public Command {
-	CMD_TYPE(COMMAND_VALIDATE)
-	bool Validate(const agi::Context *c) override {
-		return !!c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
-	}
+    CMD_TYPE(COMMAND_VALIDATE)
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->VideoProvider() && !c->dialog->Get<DialogDetachedVideo>();
+    }
 };
 
 struct video_aspect_cinematic final : public validator_video_loaded {
-	CMD_NAME("video/aspect/cinematic")
-	STR_MENU("&Cinematic (2.35)")
-	STR_DISP("Cinematic (2.35)")
-	STR_HELP("Force video to 2.35 aspect ratio")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/aspect/cinematic")
+    STR_MENU("&Cinematic (2.35)")
+    STR_DISP("Cinematic (2.35)")
+    STR_HELP("Force video to 2.35 aspect ratio")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoController->GetAspectRatioType() == AspectRatio::Cinematic;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoController->GetAspectRatioType() == AspectRatio::Cinematic;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoController->SetAspectRatio(AspectRatio::Cinematic);
-		c->frame->SetDisplayMode(1,-1);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoController->SetAspectRatio(AspectRatio::Cinematic);
+        c->frame->SetDisplayMode(1, -1);
+    }
 };
 
 struct video_aspect_custom final : public validator_video_loaded {
-	CMD_NAME("video/aspect/custom")
-	STR_MENU("C&ustom...")
-	STR_DISP("Custom")
-	STR_HELP("Force video to a custom aspect ratio")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-	bool IsActive(const agi::Context *c) override {
-		return c->videoController->GetAspectRatioType() == AspectRatio::Custom;
-	}
-
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-
-		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;
-
-		double numval = 0;
-		if (agi::util::try_parse(value, &numval)) {
-			//Nothing to see here, move along
-		}
-		else {
-			std::vector<std::string> 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 (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(numval);
-			c->frame->SetDisplayMode(1,-1);
-		}
-	}
+    CMD_NAME("video/aspect/custom")
+    STR_MENU("C&ustom...")
+    STR_DISP("Custom")
+    STR_HELP("Force video to a custom aspect ratio")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    bool IsActive(const agi::Context *c) override {
+        return c->videoController->GetAspectRatioType() == AspectRatio::Custom;
+    }
+
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+
+        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;
+
+        double numval = 0;
+        if (agi::util::try_parse(value, &numval)) {
+            //Nothing to see here, move along
+        }
+        else {
+            std::vector<std::string> 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 (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(numval);
+            c->frame->SetDisplayMode(1, -1);
+        }
+    }
 };
 
 struct video_aspect_default final : public validator_video_loaded {
-	CMD_NAME("video/aspect/default")
-	STR_MENU("&Default")
-	STR_DISP("Default")
-	STR_HELP("Use video's original aspect ratio")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/aspect/default")
+    STR_MENU("&Default")
+    STR_DISP("Default")
+    STR_HELP("Use video's original aspect ratio")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoController->GetAspectRatioType() == AspectRatio::Default;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoController->GetAspectRatioType() == AspectRatio::Default;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoController->SetAspectRatio(AspectRatio::Default);
-		c->frame->SetDisplayMode(1,-1);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoController->SetAspectRatio(AspectRatio::Default);
+        c->frame->SetDisplayMode(1, -1);
+    }
 };
 
 struct video_aspect_full final : public validator_video_loaded {
-	CMD_NAME("video/aspect/full")
-	STR_MENU("&Fullscreen (4:3)")
-	STR_DISP("Fullscreen (4:3)")
-	STR_HELP("Force video to 4:3 aspect ratio")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/aspect/full")
+    STR_MENU("&Fullscreen (4:3)")
+    STR_DISP("Fullscreen (4:3)")
+    STR_HELP("Force video to 4:3 aspect ratio")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoController->GetAspectRatioType() == AspectRatio::Fullscreen;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoController->GetAspectRatioType() == AspectRatio::Fullscreen;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoController->SetAspectRatio(AspectRatio::Fullscreen);
-		c->frame->SetDisplayMode(1,-1);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoController->SetAspectRatio(AspectRatio::Fullscreen);
+        c->frame->SetDisplayMode(1, -1);
+    }
 };
 
 struct video_aspect_wide final : public validator_video_loaded {
-	CMD_NAME("video/aspect/wide")
-	STR_MENU("&Widescreen (16:9)")
-	STR_DISP("Widescreen (16:9)")
-	STR_HELP("Force video to 16:9 aspect ratio")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/aspect/wide")
+    STR_MENU("&Widescreen (16:9)")
+    STR_DISP("Widescreen (16:9)")
+    STR_HELP("Force video to 16:9 aspect ratio")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoController->GetAspectRatioType() == AspectRatio::Widescreen;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoController->GetAspectRatioType() == AspectRatio::Widescreen;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoController->SetAspectRatio(AspectRatio::Widescreen);
-		c->frame->SetDisplayMode(1,-1);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoController->SetAspectRatio(AspectRatio::Widescreen);
+        c->frame->SetDisplayMode(1, -1);
+    }
 };
 
 struct video_close final : public validator_video_loaded {
-	CMD_NAME("video/close")
-	CMD_ICON(close_video_menu)
-	STR_MENU("&Close Video")
-	STR_DISP("Close Video")
-	STR_HELP("Close the currently open video file")
+    CMD_NAME("video/close")
+    CMD_ICON(close_video_menu)
+    STR_MENU("&Close Video")
+    STR_DISP("Close Video")
+    STR_HELP("Close the currently open video file")
 
-	void operator()(agi::Context *c) override {
-		c->project->CloseVideo();
-	}
+    void operator()(agi::Context *c) override {
+        c->project->CloseVideo();
+    }
 };
 
 struct video_copy_coordinates final : public validator_video_loaded {
-	CMD_NAME("video/copy_coordinates")
-	STR_MENU("Copy coordinates to Clipboard")
-	STR_DISP("Copy coordinates to Clipboard")
-	STR_HELP("Copy the current coordinates of the mouse over the video to the clipboard")
+    CMD_NAME("video/copy_coordinates")
+    STR_MENU("Copy coordinates to Clipboard")
+    STR_DISP("Copy coordinates to Clipboard")
+    STR_HELP("Copy the current coordinates of the mouse over the video to the clipboard")
 
-	void operator()(agi::Context *c) override {
-		SetClipboard(c->videoDisplay->GetMousePosition().Str());
-	}
+    void operator()(agi::Context *c) override {
+        SetClipboard(c->videoDisplay->GetMousePosition().Str());
+    }
 };
 
 struct video_cycle_subtitles_provider final : public cmd::Command {
-	CMD_NAME("video/subtitles_provider/cycle")
-	STR_MENU("Cycle active subtitles provider")
-	STR_DISP("Cycle active subtitles provider")
-	STR_HELP("Cycle through the available subtitles providers")
+    CMD_NAME("video/subtitles_provider/cycle")
+    STR_MENU("Cycle active subtitles provider")
+    STR_DISP("Cycle active subtitles provider")
+    STR_HELP("Cycle through the available subtitles providers")
 
-	void operator()(agi::Context *c) override {
-		auto providers = SubtitlesProviderFactory::GetClasses();
-		if (providers.empty()) return;
+    void operator()(agi::Context *c) override {
+        auto providers = SubtitlesProviderFactory::GetClasses();
+        if (providers.empty()) return;
 
-		auto it = find(begin(providers), end(providers), OPT_GET("Subtitle/Provider")->GetString());
-		if (it != end(providers)) ++it;
-		if (it == end(providers)) it = begin(providers);
+        auto it = find(begin(providers), end(providers), OPT_GET("Subtitle/Provider")->GetString());
+        if (it != end(providers)) ++it;
+        if (it == end(providers)) it = begin(providers);
 
-		OPT_SET("Subtitle/Provider")->SetString(*it);
-		c->frame->StatusTimeout(fmt_tl("Subtitles provider set to %s", *it), 5000);
-	}
+        OPT_SET("Subtitle/Provider")->SetString(*it);
+        c->frame->StatusTimeout(fmt_tl("Subtitles provider set to %s", *it), 5000);
+    }
 };
 
 struct video_detach final : public validator_video_loaded {
-	CMD_NAME("video/detach")
-	CMD_ICON(detach_video_menu)
-	STR_MENU("&Detach Video")
-	STR_DISP("Detach Video")
-	STR_HELP("Detach the video display from the main window, displaying it in a separate Window")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_TOGGLE)
+    CMD_NAME("video/detach")
+    CMD_ICON(detach_video_menu)
+    STR_MENU("&Detach Video")
+    STR_DISP("Detach Video")
+    STR_HELP("Detach the video display from the main window, displaying it in a separate Window")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_TOGGLE)
 
-	bool IsActive(const agi::Context *c) override {
-		return !!c->dialog->Get<DialogDetachedVideo>();
-	}
+    bool IsActive(const agi::Context *c) override {
+        return !!c->dialog->Get<DialogDetachedVideo>();
+    }
 
-	void operator()(agi::Context *c) override {
-		if (DialogDetachedVideo *d = c->dialog->Get<DialogDetachedVideo>())
-			d->Close();
-		else
-			c->dialog->Show<DialogDetachedVideo>(c);
-	}
+    void operator()(agi::Context *c) override {
+        if (DialogDetachedVideo *d = c->dialog->Get<DialogDetachedVideo>())
+            d->Close();
+        else
+            c->dialog->Show<DialogDetachedVideo>(c);
+    }
 };
 
 struct video_details final : public validator_video_loaded {
-	CMD_NAME("video/details")
-	CMD_ICON(show_video_details_menu)
-	STR_MENU("Show &Video Details")
-	STR_DISP("Show Video Details")
-	STR_HELP("Show video details")
+    CMD_NAME("video/details")
+    CMD_ICON(show_video_details_menu)
+    STR_MENU("Show &Video Details")
+    STR_DISP("Show Video Details")
+    STR_HELP("Show video details")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowVideoDetailsDialog(c);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowVideoDetailsDialog(c);
+    }
 };
 
 struct video_focus_seek final : public validator_video_loaded {
-	CMD_NAME("video/focus_seek")
-	STR_MENU("Toggle video slider focus")
-	STR_DISP("Toggle video slider focus")
-	STR_HELP("Toggle focus between the video slider and the previous thing to have focus")
-
-	void operator()(agi::Context *c) override {
-		wxWindow *curFocus = wxWindow::FindFocus();
-		if (curFocus == c->videoSlider) {
-			if (c->previousFocus) c->previousFocus->SetFocus();
-		}
-		else {
-			c->previousFocus = curFocus;
-			c->videoSlider->SetFocus();
-		}
-	}
-};
-
-wxImage get_image(agi::Context *c, bool raw) {
-	auto frame = c->videoController->GetFrameN();
-	return GetImage(*c->project->VideoProvider()->GetFrame(frame, c->project->Timecodes().TimeAtFrame(frame), raw));
+    CMD_NAME("video/focus_seek")
+    STR_MENU("Toggle video slider focus")
+    STR_DISP("Toggle video slider focus")
+    STR_HELP("Toggle focus between the video slider and the previous thing to have focus")
+
+    void operator()(agi::Context *c) override {
+        wxWindow *curFocus = wxWindow::FindFocus();
+        if (curFocus == c->videoSlider) {
+            if (c->previousFocus) c->previousFocus->SetFocus();
+        }
+        else {
+            c->previousFocus = curFocus;
+            c->videoSlider->SetFocus();
+        }
+    }
+};
+
+wxImage get_image(agi::Context *c, bool raw)
+{
+    auto frame = c->videoController->GetFrameN();
+    return GetImage(*c->project->VideoProvider()->GetFrame(frame, c->project->Timecodes().TimeAtFrame(frame), raw));
 }
 
 struct video_frame_copy final : public validator_video_loaded {
-	CMD_NAME("video/frame/copy")
-	STR_MENU("Copy image to Clipboard")
-	STR_DISP("Copy image to Clipboard")
-	STR_HELP("Copy the currently displayed frame to the clipboard")
+    CMD_NAME("video/frame/copy")
+    STR_MENU("Copy image to Clipboard")
+    STR_DISP("Copy image to Clipboard")
+    STR_HELP("Copy the currently displayed frame to the clipboard")
 
-	void operator()(agi::Context *c) override {
-		SetClipboard(wxBitmap(get_image(c, false), 24));
-	}
+    void operator()(agi::Context *c) override {
+        SetClipboard(wxBitmap(get_image(c, false), 24));
+    }
 };
 
 struct video_frame_copy_raw final : public validator_video_loaded {
-	CMD_NAME("video/frame/copy/raw")
-	STR_MENU("Copy image to Clipboard (no subtitles)")
-	STR_DISP("Copy image to Clipboard (no subtitles)")
-	STR_HELP("Copy the currently displayed frame to the clipboard, without the subtitles")
+    CMD_NAME("video/frame/copy/raw")
+    STR_MENU("Copy image to Clipboard (no subtitles)")
+    STR_DISP("Copy image to Clipboard (no subtitles)")
+    STR_HELP("Copy the currently displayed frame to the clipboard, without the subtitles")
 
-	void operator()(agi::Context *c) override {
-		SetClipboard(wxBitmap(get_image(c, true), 24));
-	}
+    void operator()(agi::Context *c) override {
+        SetClipboard(wxBitmap(get_image(c, true), 24));
+    }
 };
 
 struct video_frame_next final : public validator_video_loaded {
-	CMD_NAME("video/frame/next")
-	STR_MENU("Next Frame")
-	STR_DISP("Next Frame")
-	STR_HELP("Seek to the next frame")
+    CMD_NAME("video/frame/next")
+    STR_MENU("Next Frame")
+    STR_DISP("Next Frame")
+    STR_HELP("Seek to the next frame")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->NextFrame();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->NextFrame();
+    }
 };
 
 struct video_frame_next_boundary final : public validator_video_loaded {
-	CMD_NAME("video/frame/next/boundary")
-	STR_MENU("Next Boundary")
-	STR_DISP("Next Boundary")
-	STR_HELP("Seek to the next beginning or end of a subtitle")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *active_line = c->selectionController->GetActiveLine();
-		if (!active_line) return;
-
-		int target = c->videoController->FrameAtTime(active_line->Start, agi::vfr::START);
-		if (target > c->videoController->GetFrameN()) {
-			c->videoController->JumpToFrame(target);
-			return;
-		}
-
-		target = c->videoController->FrameAtTime(active_line->End, agi::vfr::END);
-		if (target > c->videoController->GetFrameN()) {
-			c->videoController->JumpToFrame(target);
-			return;
-		}
-
-		c->selectionController->NextLine();
-		AssDialogue *new_line = c->selectionController->GetActiveLine();
-		if (new_line != active_line)
-		c->videoController->JumpToTime(new_line->Start);
-	}
+    CMD_NAME("video/frame/next/boundary")
+    STR_MENU("Next Boundary")
+    STR_DISP("Next Boundary")
+    STR_HELP("Seek to the next beginning or end of a subtitle")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *active_line = c->selectionController->GetActiveLine();
+        if (!active_line) return;
+
+        int target = c->videoController->FrameAtTime(active_line->Start, agi::vfr::START);
+        if (target > c->videoController->GetFrameN()) {
+            c->videoController->JumpToFrame(target);
+            return;
+        }
+
+        target = c->videoController->FrameAtTime(active_line->End, agi::vfr::END);
+        if (target > c->videoController->GetFrameN()) {
+            c->videoController->JumpToFrame(target);
+            return;
+        }
+
+        c->selectionController->NextLine();
+        AssDialogue *new_line = c->selectionController->GetActiveLine();
+        if (new_line != active_line)
+            c->videoController->JumpToTime(new_line->Start);
+    }
 };
 
 struct video_frame_next_keyframe final : public validator_video_loaded {
-	CMD_NAME("video/frame/next/keyframe")
-	STR_MENU("Next Keyframe")
-	STR_DISP("Next Keyframe")
-	STR_HELP("Seek to the next keyframe")
+    CMD_NAME("video/frame/next/keyframe")
+    STR_MENU("Next Keyframe")
+    STR_DISP("Next Keyframe")
+    STR_HELP("Seek to the next keyframe")
 
-	void operator()(agi::Context *c) override {
-		auto const& kf = c->project->Keyframes();
-		auto pos = lower_bound(kf.begin(), kf.end(), c->videoController->GetFrameN() + 1);
+    void operator()(agi::Context *c) override {
+        auto const &kf = c->project->Keyframes();
+        auto pos = lower_bound(kf.begin(), kf.end(), c->videoController->GetFrameN() + 1);
 
-		c->videoController->JumpToFrame(pos == kf.end() ? c->project->VideoProvider()->GetFrameCount() - 1 : *pos);
-	}
+        c->videoController->JumpToFrame(pos == kf.end() ? c->project->VideoProvider()->GetFrameCount() - 1 : *pos);
+    }
 };
 
 struct video_frame_next_large final : public validator_video_loaded {
-	CMD_NAME("video/frame/next/large")
-	STR_MENU("Fast jump forward")
-	STR_DISP("Fast jump forward")
-	STR_HELP("Fast jump forward")
+    CMD_NAME("video/frame/next/large")
+    STR_MENU("Fast jump forward")
+    STR_DISP("Fast jump forward")
+    STR_HELP("Fast jump forward")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->JumpToFrame(
-			c->videoController->GetFrameN() +
-			OPT_GET("Video/Slider/Fast Jump Step")->GetInt());
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->JumpToFrame(
+            c->videoController->GetFrameN() +
+            OPT_GET("Video/Slider/Fast Jump Step")->GetInt());
+    }
 };
 
 struct video_frame_prev final : public validator_video_loaded {
-	CMD_NAME("video/frame/prev")
-	STR_MENU("Previous Frame")
-	STR_DISP("Previous Frame")
-	STR_HELP("Seek to the previous frame")
+    CMD_NAME("video/frame/prev")
+    STR_MENU("Previous Frame")
+    STR_DISP("Previous Frame")
+    STR_HELP("Seek to the previous frame")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->PrevFrame();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->PrevFrame();
+    }
 };
 
 struct video_frame_prev_boundary final : public validator_video_loaded {
-	CMD_NAME("video/frame/prev/boundary")
-	STR_MENU("Previous Boundary")
-	STR_DISP("Previous Boundary")
-	STR_HELP("Seek to the previous beginning or end of a subtitle")
-
-	void operator()(agi::Context *c) override {
-		AssDialogue *active_line = c->selectionController->GetActiveLine();
-		if (!active_line) return;
-
-		int target = c->videoController->FrameAtTime(active_line->End, agi::vfr::END);
-		if (target < c->videoController->GetFrameN()) {
-			c->videoController->JumpToFrame(target);
-			return;
-		}
-
-		target = c->videoController->FrameAtTime(active_line->Start, agi::vfr::START);
-		if (target < c->videoController->GetFrameN()) {
-			c->videoController->JumpToFrame(target);
-			return;
-		}
-
-		c->selectionController->PrevLine();
-		AssDialogue *new_line = c->selectionController->GetActiveLine();
-		if (new_line != active_line)
-			c->videoController->JumpToTime(new_line->End, agi::vfr::END);
-	}
+    CMD_NAME("video/frame/prev/boundary")
+    STR_MENU("Previous Boundary")
+    STR_DISP("Previous Boundary")
+    STR_HELP("Seek to the previous beginning or end of a subtitle")
+
+    void operator()(agi::Context *c) override {
+        AssDialogue *active_line = c->selectionController->GetActiveLine();
+        if (!active_line) return;
+
+        int target = c->videoController->FrameAtTime(active_line->End, agi::vfr::END);
+        if (target < c->videoController->GetFrameN()) {
+            c->videoController->JumpToFrame(target);
+            return;
+        }
+
+        target = c->videoController->FrameAtTime(active_line->Start, agi::vfr::START);
+        if (target < c->videoController->GetFrameN()) {
+            c->videoController->JumpToFrame(target);
+            return;
+        }
+
+        c->selectionController->PrevLine();
+        AssDialogue *new_line = c->selectionController->GetActiveLine();
+        if (new_line != active_line)
+            c->videoController->JumpToTime(new_line->End, agi::vfr::END);
+    }
 };
 
 struct video_frame_prev_keyframe final : public validator_video_loaded {
-	CMD_NAME("video/frame/prev/keyframe")
-	STR_MENU("Previous Keyframe")
-	STR_DISP("Previous Keyframe")
-	STR_HELP("Seek to the previous keyframe")
+    CMD_NAME("video/frame/prev/keyframe")
+    STR_MENU("Previous Keyframe")
+    STR_DISP("Previous Keyframe")
+    STR_HELP("Seek to the previous keyframe")
 
-	void operator()(agi::Context *c) override {
-		auto const& kf = c->project->Keyframes();
-		if (kf.empty()) {
-			c->videoController->JumpToFrame(0);
-			return;
-		}
+    void operator()(agi::Context *c) override {
+        auto const &kf = c->project->Keyframes();
+        if (kf.empty()) {
+            c->videoController->JumpToFrame(0);
+            return;
+        }
 
-		auto pos = lower_bound(kf.begin(), kf.end(), c->videoController->GetFrameN());
+        auto pos = lower_bound(kf.begin(), kf.end(), c->videoController->GetFrameN());
 
-		if (pos != kf.begin())
-			--pos;
+        if (pos != kf.begin())
+            --pos;
 
-		c->videoController->JumpToFrame(*pos);
-	}
+        c->videoController->JumpToFrame(*pos);
+    }
 };
 
 struct video_frame_prev_large final : public validator_video_loaded {
-	CMD_NAME("video/frame/prev/large")
-	STR_MENU("Fast jump backwards")
-	STR_DISP("Fast jump backwards")
-	STR_HELP("Fast jump backwards")
-
-	void operator()(agi::Context *c) override {
-		c->videoController->JumpToFrame(
-			c->videoController->GetFrameN() -
-			OPT_GET("Video/Slider/Fast Jump Step")->GetInt());
-	}
-};
-
-static void save_snapshot(agi::Context *c, bool raw) {
-	auto option = OPT_GET("Path/Screenshot")->GetString();
-	agi::fs::path basepath;
-
-	auto videoname = c->project->VideoName();
-	bool is_dummy = boost::starts_with(videoname.string(), "?dummy");
-
-	// 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 (boost::starts_with(option, "?video") && is_dummy) {
-			// So try the script location instead
-			option = "?script";
-		}
-		// Find out where the ?specifier points to
-		basepath = c->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
-			basepath = wxGetHomeDir().c_str();
-		}
-	}
-	// Actual fixed (possibly relative) path, decode it
-	else
-		basepath = c->path->MakeAbsolute(option, "?user/");
-
-	basepath /= is_dummy ? "dummy" : videoname.stem();
-
-	// Get full path
-	int session_shot_count = 1;
-	std::string path;
-	do {
-		path = agi::format("%s_%03d_%d.png", basepath.string(), session_shot_count++, c->videoController->GetFrameN());
-	} while (agi::fs::FileExists(path));
-
-	get_image(c, raw).SaveFile(to_wx(path), wxBITMAP_TYPE_PNG);
+    CMD_NAME("video/frame/prev/large")
+    STR_MENU("Fast jump backwards")
+    STR_DISP("Fast jump backwards")
+    STR_HELP("Fast jump backwards")
+
+    void operator()(agi::Context *c) override {
+        c->videoController->JumpToFrame(
+            c->videoController->GetFrameN() -
+            OPT_GET("Video/Slider/Fast Jump Step")->GetInt());
+    }
+};
+
+static void save_snapshot(agi::Context *c, bool raw)
+{
+    auto option = OPT_GET("Path/Screenshot")->GetString();
+    agi::fs::path basepath;
+
+    auto videoname = c->project->VideoName();
+    bool is_dummy = boost::starts_with(videoname.string(), "?dummy");
+
+    // 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 (boost::starts_with(option, "?video") && is_dummy) {
+            // So try the script location instead
+            option = "?script";
+        }
+        // Find out where the ?specifier points to
+        basepath = c->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
+            basepath = wxGetHomeDir().c_str();
+        }
+    }
+    // Actual fixed (possibly relative) path, decode it
+    else
+        basepath = c->path->MakeAbsolute(option, "?user/");
+
+    basepath /= is_dummy ? "dummy" : videoname.stem();
+
+    // Get full path
+    int session_shot_count = 1;
+    std::string path;
+    do {
+        path = agi::format("%s_%03d_%d.png", basepath.string(), session_shot_count++, c->videoController->GetFrameN());
+    } while (agi::fs::FileExists(path));
+
+    get_image(c, raw).SaveFile(to_wx(path), wxBITMAP_TYPE_PNG);
 }
 
 struct video_frame_save final : public validator_video_loaded {
-	CMD_NAME("video/frame/save")
-	STR_MENU("Save PNG snapshot")
-	STR_DISP("Save PNG snapshot")
-	STR_HELP("Save the currently displayed frame to a PNG file in the video's directory")
+    CMD_NAME("video/frame/save")
+    STR_MENU("Save PNG snapshot")
+    STR_DISP("Save PNG snapshot")
+    STR_HELP("Save the currently displayed frame to a PNG file in the video's directory")
 
-	void operator()(agi::Context *c) override {
-		save_snapshot(c, false);
-	}
+    void operator()(agi::Context *c) override {
+        save_snapshot(c, false);
+    }
 };
 
 struct video_frame_save_raw final : public validator_video_loaded {
-	CMD_NAME("video/frame/save/raw")
-	STR_MENU("Save PNG snapshot (no subtitles)")
-	STR_DISP("Save PNG snapshot (no subtitles)")
-	STR_HELP("Save the currently displayed frame without the subtitles to a PNG file in the video's directory")
+    CMD_NAME("video/frame/save/raw")
+    STR_MENU("Save PNG snapshot (no subtitles)")
+    STR_DISP("Save PNG snapshot (no subtitles)")
+    STR_HELP("Save the currently displayed frame without the subtitles to a PNG file in the video's directory")
 
-	void operator()(agi::Context *c) override {
-		save_snapshot(c, true);
-	}
+    void operator()(agi::Context *c) override {
+        save_snapshot(c, true);
+    }
 };
 
 struct video_jump final : public validator_video_loaded {
-	CMD_NAME("video/jump")
-	CMD_ICON(jumpto_button)
-	STR_MENU("&Jump to...")
-	STR_DISP("Jump to")
-	STR_HELP("Jump to frame or time")
+    CMD_NAME("video/jump")
+    CMD_ICON(jumpto_button)
+    STR_MENU("&Jump to...")
+    STR_DISP("Jump to")
+    STR_HELP("Jump to frame or time")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		ShowJumpToDialog(c);
-		c->videoSlider->SetFocus();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        ShowJumpToDialog(c);
+        c->videoSlider->SetFocus();
+    }
 };
 
 struct video_jump_end final : public validator_video_loaded {
-	CMD_NAME("video/jump/end")
-	CMD_ICON(video_to_subend)
-	STR_MENU("Jump Video to &End")
-	STR_DISP("Jump Video to End")
-	STR_HELP("Jump the video to the end frame of current subtitle")
+    CMD_NAME("video/jump/end")
+    CMD_ICON(video_to_subend)
+    STR_MENU("Jump Video to &End")
+    STR_DISP("Jump Video to End")
+    STR_HELP("Jump the video to the end frame of current subtitle")
 
-	void operator()(agi::Context *c) override {
-		if (auto active_line = c->selectionController->GetActiveLine())
-			c->videoController->JumpToTime(active_line->End, agi::vfr::END);
-	}
+    void operator()(agi::Context *c) override {
+        if (auto active_line = c->selectionController->GetActiveLine())
+            c->videoController->JumpToTime(active_line->End, agi::vfr::END);
+    }
 };
 
 struct video_jump_start final : public validator_video_loaded {
-	CMD_NAME("video/jump/start")
-	CMD_ICON(video_to_substart)
-	STR_MENU("Jump Video to &Start")
-	STR_DISP("Jump Video to Start")
-	STR_HELP("Jump the video to the start frame of current subtitle")
+    CMD_NAME("video/jump/start")
+    CMD_ICON(video_to_substart)
+    STR_MENU("Jump Video to &Start")
+    STR_DISP("Jump Video to Start")
+    STR_HELP("Jump the video to the start frame of current subtitle")
 
-	void operator()(agi::Context *c) override {
-		if (auto active_line = c->selectionController->GetActiveLine())
-			c->videoController->JumpToTime(active_line->Start);
-	}
+    void operator()(agi::Context *c) override {
+        if (auto active_line = c->selectionController->GetActiveLine())
+            c->videoController->JumpToTime(active_line->Start);
+    }
 };
 
 struct video_open final : public Command {
-	CMD_NAME("video/open")
-	CMD_ICON(open_video_menu)
-	STR_MENU("&Open Video...")
-	STR_DISP("Open Video")
-	STR_HELP("Open a video file")
+    CMD_NAME("video/open")
+    CMD_ICON(open_video_menu)
+    STR_MENU("&Open Video...")
+    STR_DISP("Open Video")
+    STR_HELP("Open a video file")
 
-	void operator()(agi::Context *c) override {
-		auto str = from_wx(_("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.h264,*.hevc,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts,*.y4m,*.yuv)|*.asf;*.avi;*.avs;*.d2v;*.h264;*.hevc;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts;*.y4m;*.yuv|"
-		         + _("All Files") + " (*.*)|*.*");
-		auto filename = OpenFileSelector(_("Open video file"), "Path/Last/Video", "", "", str, c->parent);
-		if (!filename.empty())
-			c->project->LoadVideo(filename);
-	}
+    void operator()(agi::Context *c) override {
+        auto str = from_wx(_("Video Formats") + " (*.asf,*.avi,*.avs,*.d2v,*.h264,*.hevc,*.m2ts,*.m4v,*.mkv,*.mov,*.mp4,*.mpeg,*.mpg,*.ogm,*.webm,*.wmv,*.ts,*.y4m,*.yuv)|*.asf;*.avi;*.avs;*.d2v;*.h264;*.hevc;*.m2ts;*.m4v;*.mkv;*.mov;*.mp4;*.mpeg;*.mpg;*.ogm;*.webm;*.wmv;*.ts;*.y4m;*.yuv|"
+                           + _("All Files") + " (*.*)|*.*");
+        auto filename = OpenFileSelector(_("Open video file"), "Path/Last/Video", "", "", str, c->parent);
+        if (!filename.empty())
+            c->project->LoadVideo(filename);
+    }
 };
 
 struct video_open_dummy final : public Command {
-	CMD_NAME("video/open/dummy")
-	CMD_ICON(use_dummy_video_menu)
-	STR_MENU("&Use Dummy Video...")
-	STR_DISP("Use Dummy Video")
-	STR_HELP("Open a placeholder video clip with solid color")
+    CMD_NAME("video/open/dummy")
+    CMD_ICON(use_dummy_video_menu)
+    STR_MENU("&Use Dummy Video...")
+    STR_DISP("Use Dummy Video")
+    STR_HELP("Open a placeholder video clip with solid color")
 
-	void operator()(agi::Context *c) override {
-		std::string fn = CreateDummyVideo(c->parent);
-		if (!fn.empty())
-			c->project->LoadVideo(fn);
-	}
+    void operator()(agi::Context *c) override {
+        std::string fn = CreateDummyVideo(c->parent);
+        if (!fn.empty())
+            c->project->LoadVideo(fn);
+    }
 };
 
 struct video_opt_autoscroll final : public Command {
-	CMD_NAME("video/opt/autoscroll")
-	CMD_ICON(toggle_video_autoscroll)
-	STR_MENU("Toggle autoscroll of video")
-	STR_DISP("Toggle autoscroll of video")
-	STR_HELP("Toggle automatically seeking video to the start time of selected lines")
-	CMD_TYPE(COMMAND_TOGGLE)
+    CMD_NAME("video/opt/autoscroll")
+    CMD_ICON(toggle_video_autoscroll)
+    STR_MENU("Toggle autoscroll of video")
+    STR_DISP("Toggle autoscroll of video")
+    STR_HELP("Toggle automatically seeking video to the start time of selected lines")
+    CMD_TYPE(COMMAND_TOGGLE)
 
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Video/Subtitle Sync")->GetBool();
-	}
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Video/Subtitle Sync")->GetBool();
+    }
 
-	void operator()(agi::Context *) override {
-		OPT_SET("Video/Subtitle Sync")->SetBool(!OPT_GET("Video/Subtitle Sync")->GetBool());
-	}
+    void operator()(agi::Context *) override {
+        OPT_SET("Video/Subtitle Sync")->SetBool(!OPT_GET("Video/Subtitle Sync")->GetBool());
+    }
 };
 
 struct video_play final : public validator_video_loaded {
-	CMD_NAME("video/play")
-	CMD_ICON(button_play)
-	STR_MENU("Play")
-	STR_DISP("Play")
-	STR_HELP("Play video starting on this position")
+    CMD_NAME("video/play")
+    CMD_ICON(button_play)
+    STR_MENU("Play")
+    STR_DISP("Play")
+    STR_HELP("Play video starting on this position")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Play();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Play();
+    }
 };
 
 struct video_play_line final : public validator_video_loaded {
-	CMD_NAME("video/play/line")
-	CMD_ICON(button_playline)
-	STR_MENU("Play line")
-	STR_DISP("Play line")
-	STR_HELP("Play current line")
+    CMD_NAME("video/play/line")
+    CMD_ICON(button_playline)
+    STR_MENU("Play line")
+    STR_DISP("Play line")
+    STR_HELP("Play current line")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->PlayLine();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->PlayLine();
+    }
 };
 
 struct video_show_overscan final : public validator_video_loaded {
-	CMD_NAME("video/show_overscan")
-	STR_MENU("Show &Overscan Mask")
-	STR_DISP("Show Overscan Mask")
-	STR_HELP("Show a mask over the video, indicating areas that might get cropped off by overscan on televisions")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_TOGGLE)
+    CMD_NAME("video/show_overscan")
+    STR_MENU("Show &Overscan Mask")
+    STR_DISP("Show Overscan Mask")
+    STR_HELP("Show a mask over the video, indicating areas that might get cropped off by overscan on televisions")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_TOGGLE)
 
-	bool IsActive(const agi::Context *) override {
-		return OPT_GET("Video/Overscan Mask")->GetBool();
-	}
+    bool IsActive(const agi::Context *) override {
+        return OPT_GET("Video/Overscan Mask")->GetBool();
+    }
 
-	void operator()(agi::Context *c) override {
-		OPT_SET("Video/Overscan Mask")->SetBool(!OPT_GET("Video/Overscan Mask")->GetBool());
-		c->videoDisplay->Render();
-	}
+    void operator()(agi::Context *c) override {
+        OPT_SET("Video/Overscan Mask")->SetBool(!OPT_GET("Video/Overscan Mask")->GetBool());
+        c->videoDisplay->Render();
+    }
 };
 
 class video_zoom_100: public validator_video_attached {
 public:
-	CMD_NAME("video/zoom/100")
-	STR_MENU("&100%")
-	STR_DISP("100%")
-	STR_HELP("Set zoom to 100%")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/zoom/100")
+    STR_MENU("&100%")
+    STR_DISP("100%")
+    STR_HELP("Set zoom to 100%")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoDisplay->GetZoom() == 1.;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoDisplay->GetZoom() == 1.;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoDisplay->SetZoom(1.);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoDisplay->SetZoom(1.);
+    }
 };
 
 class video_stop: public validator_video_loaded {
 public:
-	CMD_NAME("video/stop")
-	CMD_ICON(button_pause)
-	STR_MENU("Stop video")
-	STR_DISP("Stop video")
-	STR_HELP("Stop video playback")
+    CMD_NAME("video/stop")
+    CMD_ICON(button_pause)
+    STR_MENU("Stop video")
+    STR_DISP("Stop video")
+    STR_HELP("Stop video playback")
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+    }
 };
 
 class video_zoom_200: public validator_video_attached {
 public:
-	CMD_NAME("video/zoom/200")
-	STR_MENU("&200%")
-	STR_DISP("200%")
-	STR_HELP("Set zoom to 200%")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/zoom/200")
+    STR_MENU("&200%")
+    STR_DISP("200%")
+    STR_HELP("Set zoom to 200%")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoDisplay->GetZoom() == 2.;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoDisplay->GetZoom() == 2.;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoDisplay->SetZoom(2.);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoDisplay->SetZoom(2.);
+    }
 };
 
 class video_zoom_50: public validator_video_attached {
 public:
-	CMD_NAME("video/zoom/50")
-	STR_MENU("&50%")
-	STR_DISP("50%")
-	STR_HELP("Set zoom to 50%")
-	CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+    CMD_NAME("video/zoom/50")
+    STR_MENU("&50%")
+    STR_DISP("50%")
+    STR_HELP("Set zoom to 50%")
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
-	bool IsActive(const agi::Context *c) override {
-		return c->videoDisplay->GetZoom() == .5;
-	}
+    bool IsActive(const agi::Context *c) override {
+        return c->videoDisplay->GetZoom() == .5;
+    }
 
-	void operator()(agi::Context *c) override {
-		c->videoController->Stop();
-		c->videoDisplay->SetZoom(.5);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoController->Stop();
+        c->videoDisplay->SetZoom(.5);
+    }
 };
 
 struct video_zoom_in final : public validator_video_attached {
-	CMD_NAME("video/zoom/in")
-	CMD_ICON(zoom_in_button)
-	STR_MENU("Zoom In")
-	STR_DISP("Zoom In")
-	STR_HELP("Zoom video in")
+    CMD_NAME("video/zoom/in")
+    CMD_ICON(zoom_in_button)
+    STR_MENU("Zoom In")
+    STR_DISP("Zoom In")
+    STR_HELP("Zoom video in")
 
-	void operator()(agi::Context *c) override {
-		c->videoDisplay->SetZoom(c->videoDisplay->GetZoom() + .125);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoDisplay->SetZoom(c->videoDisplay->GetZoom() + .125);
+    }
 };
 
 struct video_zoom_out final : public validator_video_attached {
-	CMD_NAME("video/zoom/out")
-	CMD_ICON(zoom_out_button)
-	STR_MENU("Zoom Out")
-	STR_DISP("Zoom Out")
-	STR_HELP("Zoom video out")
+    CMD_NAME("video/zoom/out")
+    CMD_ICON(zoom_out_button)
+    STR_MENU("Zoom Out")
+    STR_DISP("Zoom Out")
+    STR_HELP("Zoom video out")
 
-	void operator()(agi::Context *c) override {
-		c->videoDisplay->SetZoom(c->videoDisplay->GetZoom() - .125);
-	}
+    void operator()(agi::Context *c) override {
+        c->videoDisplay->SetZoom(c->videoDisplay->GetZoom() - .125);
+    }
 };
 }
 
 namespace cmd {
-	void init_video() {
-		reg(agi::make_unique<video_aspect_cinematic>());
-		reg(agi::make_unique<video_aspect_custom>());
-		reg(agi::make_unique<video_aspect_default>());
-		reg(agi::make_unique<video_aspect_full>());
-		reg(agi::make_unique<video_aspect_wide>());
-		reg(agi::make_unique<video_close>());
-		reg(agi::make_unique<video_copy_coordinates>());
-		reg(agi::make_unique<video_cycle_subtitles_provider>());
-		reg(agi::make_unique<video_detach>());
-		reg(agi::make_unique<video_details>());
-		reg(agi::make_unique<video_focus_seek>());
-		reg(agi::make_unique<video_frame_copy>());
-		reg(agi::make_unique<video_frame_copy_raw>());
-		reg(agi::make_unique<video_frame_next>());
-		reg(agi::make_unique<video_frame_next_boundary>());
-		reg(agi::make_unique<video_frame_next_keyframe>());
-		reg(agi::make_unique<video_frame_next_large>());
-		reg(agi::make_unique<video_frame_prev>());
-		reg(agi::make_unique<video_frame_prev_boundary>());
-		reg(agi::make_unique<video_frame_prev_keyframe>());
-		reg(agi::make_unique<video_frame_prev_large>());
-		reg(agi::make_unique<video_frame_save>());
-		reg(agi::make_unique<video_frame_save_raw>());
-		reg(agi::make_unique<video_jump>());
-		reg(agi::make_unique<video_jump_end>());
-		reg(agi::make_unique<video_jump_start>());
-		reg(agi::make_unique<video_open>());
-		reg(agi::make_unique<video_open_dummy>());
-		reg(agi::make_unique<video_opt_autoscroll>());
-		reg(agi::make_unique<video_play>());
-		reg(agi::make_unique<video_play_line>());
-		reg(agi::make_unique<video_show_overscan>());
-		reg(agi::make_unique<video_stop>());
-		reg(agi::make_unique<video_zoom_100>());
-		reg(agi::make_unique<video_zoom_200>());
-		reg(agi::make_unique<video_zoom_50>());
-		reg(agi::make_unique<video_zoom_in>());
-		reg(agi::make_unique<video_zoom_out>());
-	}
+void init_video()
+{
+    reg(agi::make_unique<video_aspect_cinematic>());
+    reg(agi::make_unique<video_aspect_custom>());
+    reg(agi::make_unique<video_aspect_default>());
+    reg(agi::make_unique<video_aspect_full>());
+    reg(agi::make_unique<video_aspect_wide>());
+    reg(agi::make_unique<video_close>());
+    reg(agi::make_unique<video_copy_coordinates>());
+    reg(agi::make_unique<video_cycle_subtitles_provider>());
+    reg(agi::make_unique<video_detach>());
+    reg(agi::make_unique<video_details>());
+    reg(agi::make_unique<video_focus_seek>());
+    reg(agi::make_unique<video_frame_copy>());
+    reg(agi::make_unique<video_frame_copy_raw>());
+    reg(agi::make_unique<video_frame_next>());
+    reg(agi::make_unique<video_frame_next_boundary>());
+    reg(agi::make_unique<video_frame_next_keyframe>());
+    reg(agi::make_unique<video_frame_next_large>());
+    reg(agi::make_unique<video_frame_prev>());
+    reg(agi::make_unique<video_frame_prev_boundary>());
+    reg(agi::make_unique<video_frame_prev_keyframe>());
+    reg(agi::make_unique<video_frame_prev_large>());
+    reg(agi::make_unique<video_frame_save>());
+    reg(agi::make_unique<video_frame_save_raw>());
+    reg(agi::make_unique<video_jump>());
+    reg(agi::make_unique<video_jump_end>());
+    reg(agi::make_unique<video_jump_start>());
+    reg(agi::make_unique<video_open>());
+    reg(agi::make_unique<video_open_dummy>());
+    reg(agi::make_unique<video_opt_autoscroll>());
+    reg(agi::make_unique<video_play>());
+    reg(agi::make_unique<video_play_line>());
+    reg(agi::make_unique<video_show_overscan>());
+    reg(agi::make_unique<video_stop>());
+    reg(agi::make_unique<video_zoom_100>());
+    reg(agi::make_unique<video_zoom_200>());
+    reg(agi::make_unique<video_zoom_50>());
+    reg(agi::make_unique<video_zoom_in>());
+    reg(agi::make_unique<video_zoom_out>());
+}
 }
diff --git a/src/command/vis_tool.cpp b/src/command/vis_tool.cpp
index 168f427ee81e49ddb019e4f1587dcc4bc3aa8745..b43e881fb1209f0e73a62a52b3658ce41cb616ce 100644
--- a/src/command/vis_tool.cpp
+++ b/src/command/vis_tool.cpp
@@ -31,90 +31,91 @@
 #include <libaegisub/make_unique.h>
 
 namespace {
-	using cmd::Command;
-
-	template<class T>
-	struct visual_tool_command : public Command {
-		CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
-
-		bool Validate(const agi::Context *c) override {
-			return !!c->project->VideoProvider();
-		}
-
-		bool IsActive(const agi::Context *c) override {
-			return c->videoDisplay->ToolIsType(typeid(T));
-		}
-
-		void operator()(agi::Context *c) override {
-			c->videoDisplay->SetTool(agi::make_unique<T>(c->videoDisplay, c));
-		}
-	};
-
-	struct visual_mode_cross final : public visual_tool_command<VisualToolCross> {
-		CMD_NAME("video/tool/cross")
-		CMD_ICON(visual_standard)
-		STR_MENU("Standard")
-		STR_DISP("Standard")
-		STR_HELP("Standard mode, double click sets position")
-	};
-
-	struct visual_mode_drag final : public visual_tool_command<VisualToolDrag> {
-		CMD_NAME("video/tool/drag")
-		CMD_ICON(visual_move)
-		STR_MENU("Drag")
-		STR_DISP("Drag")
-		STR_HELP("Drag subtitles")
-	};
-
-	struct visual_mode_rotate_z final : public visual_tool_command<VisualToolRotateZ> {
-		CMD_NAME("video/tool/rotate/z")
-		CMD_ICON(visual_rotatez)
-		STR_MENU("Rotate Z")
-		STR_DISP("Rotate Z")
-		STR_HELP("Rotate subtitles on their Z axis")
-	};
-
-	struct visual_mode_rotate_xy final : public visual_tool_command<VisualToolRotateXY> {
-		CMD_NAME("video/tool/rotate/xy")
-		CMD_ICON(visual_rotatexy)
-		STR_MENU("Rotate XY")
-		STR_DISP("Rotate XY")
-		STR_HELP("Rotate subtitles on their X and Y axes")
-	};
-
-	struct visual_mode_scale final : public visual_tool_command<VisualToolScale> {
-		CMD_NAME("video/tool/scale")
-		CMD_ICON(visual_scale)
-		STR_MENU("Scale")
-		STR_DISP("Scale")
-		STR_HELP("Scale subtitles on X and Y axes")
-	};
-
-	struct visual_mode_clip final : public visual_tool_command<VisualToolClip> {
-		CMD_NAME("video/tool/clip")
-		CMD_ICON(visual_clip)
-		STR_MENU("Clip")
-		STR_DISP("Clip")
-		STR_HELP("Clip subtitles to a rectangle")
-	};
-
-	struct visual_mode_vector_clip final : public visual_tool_command<VisualToolVectorClip> {
-		CMD_NAME("video/tool/vector_clip")
-		CMD_ICON(visual_vector_clip)
-		STR_MENU("Vector Clip")
-		STR_DISP("Vector Clip")
-		STR_HELP("Clip subtitles to a vectorial area")
-	};
+using cmd::Command;
+
+template<class T>
+struct visual_tool_command : public Command {
+    CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
+
+    bool Validate(const agi::Context *c) override {
+        return !!c->project->VideoProvider();
+    }
+
+    bool IsActive(const agi::Context *c) override {
+        return c->videoDisplay->ToolIsType(typeid(T));
+    }
+
+    void operator()(agi::Context *c) override {
+        c->videoDisplay->SetTool(agi::make_unique<T>(c->videoDisplay, c));
+    }
+};
+
+struct visual_mode_cross final : public visual_tool_command<VisualToolCross> {
+    CMD_NAME("video/tool/cross")
+    CMD_ICON(visual_standard)
+    STR_MENU("Standard")
+    STR_DISP("Standard")
+    STR_HELP("Standard mode, double click sets position")
+};
+
+struct visual_mode_drag final : public visual_tool_command<VisualToolDrag> {
+    CMD_NAME("video/tool/drag")
+    CMD_ICON(visual_move)
+    STR_MENU("Drag")
+    STR_DISP("Drag")
+    STR_HELP("Drag subtitles")
+};
+
+struct visual_mode_rotate_z final : public visual_tool_command<VisualToolRotateZ> {
+    CMD_NAME("video/tool/rotate/z")
+    CMD_ICON(visual_rotatez)
+    STR_MENU("Rotate Z")
+    STR_DISP("Rotate Z")
+    STR_HELP("Rotate subtitles on their Z axis")
+};
+
+struct visual_mode_rotate_xy final : public visual_tool_command<VisualToolRotateXY> {
+    CMD_NAME("video/tool/rotate/xy")
+    CMD_ICON(visual_rotatexy)
+    STR_MENU("Rotate XY")
+    STR_DISP("Rotate XY")
+    STR_HELP("Rotate subtitles on their X and Y axes")
+};
+
+struct visual_mode_scale final : public visual_tool_command<VisualToolScale> {
+    CMD_NAME("video/tool/scale")
+    CMD_ICON(visual_scale)
+    STR_MENU("Scale")
+    STR_DISP("Scale")
+    STR_HELP("Scale subtitles on X and Y axes")
+};
+
+struct visual_mode_clip final : public visual_tool_command<VisualToolClip> {
+    CMD_NAME("video/tool/clip")
+    CMD_ICON(visual_clip)
+    STR_MENU("Clip")
+    STR_DISP("Clip")
+    STR_HELP("Clip subtitles to a rectangle")
+};
+
+struct visual_mode_vector_clip final : public visual_tool_command<VisualToolVectorClip> {
+    CMD_NAME("video/tool/vector_clip")
+    CMD_ICON(visual_vector_clip)
+    STR_MENU("Vector Clip")
+    STR_DISP("Vector Clip")
+    STR_HELP("Clip subtitles to a vectorial area")
+};
 }
 
 namespace cmd {
-	void init_visual_tools() {
-		reg(agi::make_unique<visual_mode_cross>());
-		reg(agi::make_unique<visual_mode_drag>());
-		reg(agi::make_unique<visual_mode_rotate_z>());
-		reg(agi::make_unique<visual_mode_rotate_xy>());
-		reg(agi::make_unique<visual_mode_scale>());
-		reg(agi::make_unique<visual_mode_clip>());
-		reg(agi::make_unique<visual_mode_vector_clip>());
-	}
+void init_visual_tools()
+{
+    reg(agi::make_unique<visual_mode_cross>());
+    reg(agi::make_unique<visual_mode_drag>());
+    reg(agi::make_unique<visual_mode_rotate_z>());
+    reg(agi::make_unique<visual_mode_rotate_xy>());
+    reg(agi::make_unique<visual_mode_scale>());
+    reg(agi::make_unique<visual_mode_clip>());
+    reg(agi::make_unique<visual_mode_vector_clip>());
+}
 }
diff --git a/src/compat.cpp b/src/compat.cpp
index 62ac07f04722ec5d463c9ba4c1753460fc2aaecf..bdd3724796560a5b5662dd998d75a4c49a801fbe 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -4,34 +4,40 @@
 
 #include <algorithm>
 
-wxArrayString lagi_MRU_wxAS(const char *list) {
-	auto const& vec = *config::mru->Get(list);
-	wxArrayString ret;
-	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 char *list)
+{
+    auto const &vec = *config::mru->Get(list);
+    wxArrayString ret;
+    ret.reserve(vec.size());
+    transform(vec.begin(), vec.end(), std::back_inserter(ret),
+    [](agi::fs::path const & p) { return p.wstring(); });
+    return ret;
 }
 
-wxArrayString to_wx(std::vector<std::string> const& vec) {
-	wxArrayString ret;
-	ret.reserve(vec.size());
-	transform(vec.begin(), vec.end(), std::back_inserter(ret), (wxString (*)(std::string const&))to_wx);
-	return ret;
+wxArrayString to_wx(std::vector<std::string> const &vec)
+{
+    wxArrayString ret;
+    ret.reserve(vec.size());
+    transform(vec.begin(), vec.end(), std::back_inserter(ret), (wxString (*)(std::string const &))to_wx);
+    return ret;
 }
 
-wxColour to_wx(agi::Color color) {
-	return wxColour(color.r, color.g, color.b, 255 - color.a);
+wxColour to_wx(agi::Color color)
+{
+    return wxColour(color.r, color.g, color.b, 255 - color.a);
 }
 
-wxString to_wx(std::string const& str) {
-	return wxString(str.c_str(), wxConvUTF8);
+wxString to_wx(std::string const &str)
+{
+    return wxString(str.c_str(), wxConvUTF8);
 }
 
-agi::Color from_wx(wxColour color) {
-	return agi::Color(color.Red(), color.Green(), color.Blue(), 255 - color.Alpha());
+agi::Color from_wx(wxColour color)
+{
+    return agi::Color(color.Red(), color.Green(), color.Blue(), 255 - color.Alpha());
 }
 
-std::string from_wx(wxString const& str) {
-	return std::string(str.utf8_str());
+std::string from_wx(wxString const &str)
+{
+    return std::string(str.utf8_str());
 }
diff --git a/src/compat.h b/src/compat.h
index 88c9c4b3897da24b5104ec4270c88001710853c9..52153f6419a1c8da81c32f5a9a4a0c19a9c2cb6d 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -10,10 +10,10 @@
 #include <libaegisub/color.h>
 
 wxColour to_wx(agi::Color color);
-wxString to_wx(std::string const& str);
-wxArrayString to_wx(std::vector<std::string> const& vec);
+wxString to_wx(std::string const &str);
+wxArrayString to_wx(std::vector<std::string> const &vec);
 
 agi::Color from_wx(wxColour color);
-std::string from_wx(wxString const& str);
+std::string from_wx(wxString const &str);
 
 wxArrayString lagi_MRU_wxAS(const char *list);
diff --git a/src/context.cpp b/src/context.cpp
index a2203e026303303180154a8c9dd2d84f29ed029d..1faaaf84632e82dfff30f3a8874d8ff2eb6aa8c2 100644
--- a/src/context.cpp
+++ b/src/context.cpp
@@ -34,20 +34,20 @@
 
 namespace agi {
 Context::Context()
-: ass(make_unique<AssFile>())
-, textSelectionController(make_unique<TextSelectionController>())
-, subsController(make_unique<SubsController>(this))
-, project(make_unique<Project>(this))
-, local_scripts(make_unique<Automation4::LocalScriptManager>(this))
-, selectionController(make_unique<SelectionController>(this))
-, videoController(make_unique<VideoController>(this))
-, audioController(make_unique<AudioController>(this))
-, initialLineState(make_unique<InitialLineState>(this))
-, search(make_unique<SearchReplaceEngine>(this))
-, path(make_unique<Path>(*config::path))
-, dialog(make_unique<DialogManager>())
+    : ass(make_unique<AssFile>())
+    , textSelectionController(make_unique<TextSelectionController>())
+    , subsController(make_unique<SubsController>(this))
+    , project(make_unique<Project>(this))
+    , local_scripts(make_unique<Automation4::LocalScriptManager>(this))
+    , selectionController(make_unique<SelectionController>(this))
+    , videoController(make_unique<VideoController>(this))
+    , audioController(make_unique<AudioController>(this))
+    , initialLineState(make_unique<InitialLineState>(this))
+    , search(make_unique<SearchReplaceEngine>(this))
+    , path(make_unique<Path>(*config::path))
+    , dialog(make_unique<DialogManager>())
 {
-	subsController->SetSelectionController(selectionController.get());
+    subsController->SetSelectionController(selectionController.get());
 }
 
 Context::~Context() = default;
diff --git a/src/crash_writer.cpp b/src/crash_writer.cpp
index ab521be5f43cde730f43dfd8e3c6556640c1ba7d..2c31a0abf55a33526306ec5173d12f61bb901c4f 100644
--- a/src/crash_writer.cpp
+++ b/src/crash_writer.cpp
@@ -32,61 +32,63 @@ fs::path crashlog_path;
 
 #if wxUSE_STACKWALKER == 1
 class StackWalker : public wxStackWalker {
-	boost::filesystem::ofstream fp;
+    boost::filesystem::ofstream fp;
 
 public:
-	StackWalker(std::string const& cause)
-	: fp(crashlog_path, std::ios::app)
-	{
-		if (!fp.good()) return;
+    StackWalker(std::string const &cause)
+        : fp(crashlog_path, std::ios::app) {
+        if (!fp.good()) return;
 
-		fp << util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
-		fp << agi::format("VER - %s\n", GetAegisubLongVersionString());
-		fp << agi::format("FTL - Beginning stack dump for \"%s\": \n", cause);
-	}
+        fp << util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
+        fp << agi::format("VER - %s\n", GetAegisubLongVersionString());
+        fp << agi::format("FTL - Beginning stack dump for \"%s\": \n", cause);
+    }
 
-	~StackWalker() {
-		if (!fp.good()) return;
+    ~StackWalker() {
+        if (!fp.good()) return;
 
-		fp << "End of stack dump.\n";
-		fp << "----------------------------------------\n\n";
-	}
+        fp << "End of stack dump.\n";
+        fp << "----------------------------------------\n\n";
+    }
 
-	void OnStackFrame(wxStackFrame const& frame) override final {
-		if (!fp.good()) return;
+    void OnStackFrame(wxStackFrame const &frame) override final {
+        if (!fp.good()) return;
 
-		fp << agi::format("%03u - %p: %s", frame.GetLevel(), frame.GetAddress(), frame.GetName().utf8_str().data());
-		if (frame.HasSourceLocation())
-			fp << agi::format(" on %s:%u", frame.GetFileName().utf8_str().data(), frame.GetLine());
+        fp << agi::format("%03u - %p: %s", frame.GetLevel(), frame.GetAddress(), frame.GetName().utf8_str().data());
+        if (frame.HasSourceLocation())
+            fp << agi::format(" on %s:%u", frame.GetFileName().utf8_str().data(), frame.GetLine());
 
-		fp << "\n";
-	}
+        fp << "\n";
+    }
 };
 #endif
 }
 
 namespace crash_writer {
-void Initialize(fs::path const& path) {
-	crashlog_path = path / "crashlog.txt";
+void Initialize(fs::path const &path)
+{
+    crashlog_path = path / "crashlog.txt";
 }
 
 void Cleanup() { }
 
-void Write() {
+void Write()
+{
 #if wxUSE_STACKWALKER == 1
-	StackWalker walker("Fatal exception");
-	walker.WalkFromException();
+    StackWalker walker("Fatal exception");
+    walker.WalkFromException();
 #endif
 }
 
-void Write(std::string const& error) {
-	boost::filesystem::ofstream file(crashlog_path, std::ios::app);
-	if (file.is_open()) {
-		file << util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
-		file << agi::format("VER - %s\n", GetAegisubLongVersionString());
-		file << agi::format("EXC - Aegisub has crashed with unhandled exception \"%s\".\n", error);
-		file << "----------------------------------------\n\n";
-		file.close();
-	}
+void Write(std::string const &error)
+{
+    boost::filesystem::ofstream file(crashlog_path, std::ios::app);
+    if (file.is_open()) {
+        file << util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
+        file << agi::format("VER - %s\n", GetAegisubLongVersionString());
+        file << agi::format("EXC - Aegisub has crashed with unhandled exception \"%s\".\n", error);
+        file << "----------------------------------------\n\n";
+        file.close();
+    }
 }
 }
diff --git a/src/crash_writer.h b/src/crash_writer.h
index cf3a2e0a0d538fc3b4700fc4d4dd5c67b55ba4da..4cf6d4a838bb53a84b6683b0abe7de54fe55ce40 100644
--- a/src/crash_writer.h
+++ b/src/crash_writer.h
@@ -19,9 +19,9 @@
 #include <string>
 
 namespace crash_writer {
-	void Initialize(agi::fs::path const& path);
-	void Cleanup();
+void Initialize(agi::fs::path const &path);
+void Cleanup();
 
-	void Write();
-	void Write(std::string const& error);
+void Write();
+void Write(std::string const &error);
 }
diff --git a/src/crash_writer_minidump.cpp b/src/crash_writer_minidump.cpp
index 5481273cda7a8d8ef5063cf9fa64187b0a69a970..c880af3624d8c03a184196fa3db467fcd3010fa3 100644
--- a/src/crash_writer_minidump.cpp
+++ b/src/crash_writer_minidump.cpp
@@ -39,113 +39,117 @@ wchar_t crash_dump_path[MAX_PATH];
 agi::fs::path crashlog_path;
 
 using MiniDumpWriteDump = BOOL(WINAPI *)(
-	HANDLE hProcess,
-	DWORD dwPid,
-	HANDLE hFile,
-	MINIDUMP_TYPE DumpType,
-	CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
-	CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
-	CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+                              HANDLE hProcess,
+                              DWORD dwPid,
+                              HANDLE hFile,
+                              MINIDUMP_TYPE DumpType,
+                              CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+                              CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+                              CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
 
 struct dump_thread_state {
-	std::mutex start_mutex;
-	std::condition_variable start_cv;
-
-	std::atomic<bool> exit = false;
-	EXCEPTION_POINTERS *ep = nullptr;
-	DWORD thread_id = 0;
-
-	// Must be last so everything else is initialized before it
-	std::thread thread;
-
-	dump_thread_state() : thread([&] { main(); }) { }
-
-	void main() {
-		auto module = LoadLibrary(L"dbghelp.dll");
-		if (!module) return;
-
-		auto fn = reinterpret_cast<MiniDumpWriteDump>(GetProcAddress(module, "MiniDumpWriteDump"));
-		if (!fn) {
-			FreeLibrary(module);
-			return;
-		}
-
-		std::unique_lock<std::mutex> lock(start_mutex);
-		start_cv.wait(lock, [&] { return ep || exit; });
-		if (ep)
-			write_dump(fn);
-		FreeLibrary(module);
-	}
-
-	void write_dump(MiniDumpWriteDump fn) {
-		auto file = CreateFile(crash_dump_path,
-			GENERIC_WRITE,
-			0,  // no sharing
-			nullptr,
-			CREATE_NEW,
-			FILE_ATTRIBUTE_NORMAL,
-			nullptr);
-		if (file == INVALID_HANDLE_VALUE) return;
-
-		MINIDUMP_EXCEPTION_INFORMATION info;
-		info.ThreadId = thread_id;
-		info.ExceptionPointers = ep;
-		info.ClientPointers = FALSE;
-
-		fn(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &info, nullptr, nullptr);
-
-		CloseHandle(file);
-	}
+    std::mutex start_mutex;
+    std::condition_variable start_cv;
+
+    std::atomic<bool> exit = false;
+    EXCEPTION_POINTERS *ep = nullptr;
+    DWORD thread_id = 0;
+
+    // Must be last so everything else is initialized before it
+    std::thread thread;
+
+    dump_thread_state() : thread([ & ] { main(); }) { }
+
+    void main() {
+        auto module = LoadLibrary(L"dbghelp.dll");
+        if (!module) return;
+
+        auto fn = reinterpret_cast<MiniDumpWriteDump>(GetProcAddress(module, "MiniDumpWriteDump"));
+        if (!fn) {
+            FreeLibrary(module);
+            return;
+        }
+
+        std::unique_lock<std::mutex> lock(start_mutex);
+        start_cv.wait(lock, [&] { return ep || exit; });
+        if (ep)
+            write_dump(fn);
+        FreeLibrary(module);
+    }
+
+    void write_dump(MiniDumpWriteDump fn) {
+        auto file = CreateFile(crash_dump_path,
+                               GENERIC_WRITE,
+                               0,  // no sharing
+                               nullptr,
+                               CREATE_NEW,
+                               FILE_ATTRIBUTE_NORMAL,
+                               nullptr);
+        if (file == INVALID_HANDLE_VALUE) return;
+
+        MINIDUMP_EXCEPTION_INFORMATION info;
+        info.ThreadId = thread_id;
+        info.ExceptionPointers = ep;
+        info.ClientPointers = FALSE;
+
+        fn(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &info, nullptr, nullptr);
+
+        CloseHandle(file);
+    }
 };
 
 std::unique_ptr<dump_thread_state> dump_thread;
 }
 
 namespace crash_writer {
-void Initialize(agi::fs::path const& path) {
-	crashlog_path = path / "crashlog.txt";
+void Initialize(agi::fs::path const &path)
+{
+    crashlog_path = path / "crashlog.txt";
 
-	auto dump_path = path / "crashdumps";
-	agi::fs::CreateDirectory(dump_path);
+    auto dump_path = path / "crashdumps";
+    agi::fs::CreateDirectory(dump_path);
 
-	const auto path_str = (dump_path / GetVersionNumber()).wstring();
-	wcscpy_s(crash_dump_path, path_str.c_str());
-	auto len = path_str.size();
+    const auto path_str = (dump_path / GetVersionNumber()).wstring();
+    wcscpy_s(crash_dump_path, path_str.c_str());
+    auto len = path_str.size();
 
-	const auto t = time(nullptr);
-	struct tm tm;
-	localtime_s(&tm, &t);
+    const auto t = time(nullptr);
+    struct tm tm;
+    localtime_s(&tm, &t);
 
-	len += wcsftime(crash_dump_path + len, MAX_PATH - len, L"-%Y-%m-%d-%H-%M-%S-", &tm);
-	len += swprintf_s(crash_dump_path + len, MAX_PATH - len, L"%d", GetCurrentProcessId());
-	wcscpy_s(crash_dump_path + len, MAX_PATH - len, L".dmp");
+    len += wcsftime(crash_dump_path + len, MAX_PATH - len, L"-%Y-%m-%d-%H-%M-%S-", &tm);
+    len += swprintf_s(crash_dump_path + len, MAX_PATH - len, L"%d", GetCurrentProcessId());
+    wcscpy_s(crash_dump_path + len, MAX_PATH - len, L".dmp");
 
-	if (!dump_thread)
-		dump_thread = agi::make_unique<dump_thread_state>();
+    if (!dump_thread)
+        dump_thread = agi::make_unique<dump_thread_state>();
 }
 
-void Cleanup() {
-	dump_thread->exit = true;
-	dump_thread->start_cv.notify_all();
-	dump_thread->thread.join();
-	dump_thread.reset();
+void Cleanup()
+{
+    dump_thread->exit = true;
+    dump_thread->start_cv.notify_all();
+    dump_thread->thread.join();
+    dump_thread.reset();
 }
 
-void Write() {
-	dump_thread->ep = wxGlobalSEInformation;
-	dump_thread->thread_id = GetCurrentThreadId();
-	dump_thread->start_cv.notify_all();
-	dump_thread->thread.join();
-	dump_thread.reset();
+void Write()
+{
+    dump_thread->ep = wxGlobalSEInformation;
+    dump_thread->thread_id = GetCurrentThreadId();
+    dump_thread->start_cv.notify_all();
+    dump_thread->thread.join();
+    dump_thread.reset();
 }
 
-void Write(std::string const& error) {
-	boost::filesystem::ofstream file(crashlog_path, std::ios::app);
-	if (file.is_open()) {
-		file << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
-		agi::format(file, "VER - %s\n", GetAegisubLongVersionString());
-		agi::format(file, "EXC - Aegisub has crashed with unhandled exception \"%s\".\n", error);
-		file << "----------------------------------------\n\n";
-	}
+void Write(std::string const &error)
+{
+    boost::filesystem::ofstream file(crashlog_path, std::ios::app);
+    if (file.is_open()) {
+        file << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
+        agi::format(file, "VER - %s\n", GetAegisubLongVersionString());
+        agi::format(file, "EXC - Aegisub has crashed with unhandled exception \"%s\".\n", error);
+        file << "----------------------------------------\n\n";
+    }
 }
 }
diff --git a/src/dialog_about.cpp b/src/dialog_about.cpp
index efbc0080ffa8102d19063d38b63da7f6df9b6552..7262c15ae1e92e78e68bf317b7566924aa441828 100644
--- a/src/dialog_about.cpp
+++ b/src/dialog_about.cpp
@@ -40,105 +40,106 @@
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 
-void ShowAboutDialog(wxWindow *parent) {
-	wxDialog d(parent, -1, _("About Aegisub"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX);
+void ShowAboutDialog(wxWindow *parent)
+{
+    wxDialog d(parent, -1, _("About Aegisub"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX);
 
-	wxString translatorCredit = _("Translated into LANGUAGE by PERSON\n");
-	if (translatorCredit == "Translated into LANGUAGE by PERSON\n")
-		translatorCredit.clear();
+    wxString translatorCredit = _("Translated into LANGUAGE by PERSON\n");
+    if (translatorCredit == "Translated into LANGUAGE by PERSON\n")
+        translatorCredit.clear();
 
-	// Generate about string
-	wxString aboutString = wxString("Aegisub ") + GetAegisubShortVersionString() + ".\n"
-		"Copyright (c) 2005-2014 Rodrigo Braz Monteiro, Niels Martin Hansen, Thomas Goyne et al.\n\n"
-		"Programmers:\n"
-		"    Alysson Souza e Silva\n"
-		"    Amar Takhar\n"
-		"    Dan Donovan\n"
-		"    Daniel Moscoviter\n"
-		"    David Conrad\n"
-		"    David Lamparter\n"
-		"    Eric Batalitzky\n"
-		"    Evgeniy Stepanov\n"
-		"    Fredrik Mellbin\n"
-		"    Grigori Goronzy\n"
-		"    Karl Blomster\n"
-		"    Mike Matsnev\n"
-		"    Moritz Brunner\n"
-		"    Muhammad Lukman Nasaruddin\n"
-		"    Niels Martin Hansen\n"
-		"    Patryk Pomykalski\n"
-		"    Ravi Pinjala\n"
-		"    Rodrigo Braz Monteiro\n"
-		"    Simone Cociancich\n"
-		"    Thomas Goyne\n"
-		"User manual written by:\n"
-		"    Karl Blomster\n"
-		"    Niels Martin Hansen\n"
-		"    Rodrigo Braz Monteiro\n"
-		"Icons by:\n"
-		"    Philip Cash\n"
-		"Additional thanks to:\n"
-		"    Mentar\n"
-		"    Sigurd Tao Lyngse\n"
-		"    Everyone in the Aegisub IRC channel\n"
-		"    Everyone who ever reported a bug\n"
-		+ translatorCredit + "\n"
-		"Aegisub includes portions from the following other projects:\n"
-		"    wxWidgets - Copyright (c) Julian Smart, Robert Roebling et al;\n"
-		"    wxStyledTextCtrl - Copyright (c) Robin Dunn, Neil Hodgson;\n"
-		"    Scintilla - Copyright (c) Neil Hodgson;\n"
-		"    Boost - Copyright (c) Beman Dawes, David Abrahams et al;\n"
-		"    UniversalCharDet - Copyright (c) Netscape Communications Corp.;\n"
-		"    ICU - Copyright (c) International Business Machines Corp.;\n"
-		"    Lua - Copyright (c) Lua.org, PUC-Rio;\n"
-		"    LuaJIT - Copyright (c) Mike Pall;\n"
-		"    luabins - Copyright (c) Alexander Gladysh;\n"
+    // Generate about string
+    wxString aboutString = wxString("Aegisub ") + GetAegisubShortVersionString() + ".\n"
+                           "Copyright (c) 2005-2014 Rodrigo Braz Monteiro, Niels Martin Hansen, Thomas Goyne et al.\n\n"
+                           "Programmers:\n"
+                           "    Alysson Souza e Silva\n"
+                           "    Amar Takhar\n"
+                           "    Dan Donovan\n"
+                           "    Daniel Moscoviter\n"
+                           "    David Conrad\n"
+                           "    David Lamparter\n"
+                           "    Eric Batalitzky\n"
+                           "    Evgeniy Stepanov\n"
+                           "    Fredrik Mellbin\n"
+                           "    Grigori Goronzy\n"
+                           "    Karl Blomster\n"
+                           "    Mike Matsnev\n"
+                           "    Moritz Brunner\n"
+                           "    Muhammad Lukman Nasaruddin\n"
+                           "    Niels Martin Hansen\n"
+                           "    Patryk Pomykalski\n"
+                           "    Ravi Pinjala\n"
+                           "    Rodrigo Braz Monteiro\n"
+                           "    Simone Cociancich\n"
+                           "    Thomas Goyne\n"
+                           "User manual written by:\n"
+                           "    Karl Blomster\n"
+                           "    Niels Martin Hansen\n"
+                           "    Rodrigo Braz Monteiro\n"
+                           "Icons by:\n"
+                           "    Philip Cash\n"
+                           "Additional thanks to:\n"
+                           "    Mentar\n"
+                           "    Sigurd Tao Lyngse\n"
+                           "    Everyone in the Aegisub IRC channel\n"
+                           "    Everyone who ever reported a bug\n"
+                           + translatorCredit + "\n"
+                           "Aegisub includes portions from the following other projects:\n"
+                           "    wxWidgets - Copyright (c) Julian Smart, Robert Roebling et al;\n"
+                           "    wxStyledTextCtrl - Copyright (c) Robin Dunn, Neil Hodgson;\n"
+                           "    Scintilla - Copyright (c) Neil Hodgson;\n"
+                           "    Boost - Copyright (c) Beman Dawes, David Abrahams et al;\n"
+                           "    UniversalCharDet - Copyright (c) Netscape Communications Corp.;\n"
+                           "    ICU - Copyright (c) International Business Machines Corp.;\n"
+                           "    Lua - Copyright (c) Lua.org, PUC-Rio;\n"
+                           "    LuaJIT - Copyright (c) Mike Pall;\n"
+                           "    luabins - Copyright (c) Alexander Gladysh;\n"
 #ifdef WITH_HUNSPELL
-		"    Hunspell - Copyright (c) Kevin Hendricks;\n"
+                           "    Hunspell - Copyright (c) Kevin Hendricks;\n"
 #endif
 #ifdef WITH_PORTAUDIO
-		"    PortAudio - Copyright (c) Ross Bencina, Phil Burk;\n"
+                           "    PortAudio - Copyright (c) Ross Bencina, Phil Burk;\n"
 #endif
 #ifdef WITH_FFMS2
-		"    FFmpeg - Copyright (c) Fabrice Bellard;\n"
-		"    FFMS2 - Copyright (c) Fredrik Mellbin;\n"
+                           "    FFmpeg - Copyright (c) Fabrice Bellard;\n"
+                           "    FFMS2 - Copyright (c) Fredrik Mellbin;\n"
 #endif
 #ifdef WITH_AVISYNTH
-		"    Avisynth 2.5 - Copyright (c) Ben Rudiak-Gould et al;\n"
+                           "    Avisynth 2.5 - Copyright (c) Ben Rudiak-Gould et al;\n"
 #endif
 #ifdef WITH_CSRI
-		"    csri - Copyright (c) David Lamparter;\n"
+                           "    csri - Copyright (c) David Lamparter;\n"
 # ifdef __WINDOWS__
-		"    vsfilter - Copyright (c) Gabest et al;\n"
+                           "    vsfilter - Copyright (c) Gabest et al;\n"
 # endif
 #endif
-		"    libass - Copyright (c) Evgeniy Stepanov, Grigori Goronzy;\n"
-		"    Matroska Parser - Copyright (c) Mike Matsnev;\n"
-		"    Freetype - Copyright (c) David Turner, Robert Wilhelm, Werner Lemberg;\n"
-		"    Fontconfig - Copyright (c) Keith Packard et al;\n"
+                           "    libass - Copyright (c) Evgeniy Stepanov, Grigori Goronzy;\n"
+                           "    Matroska Parser - Copyright (c) Mike Matsnev;\n"
+                           "    Freetype - Copyright (c) David Turner, Robert Wilhelm, Werner Lemberg;\n"
+                           "    Fontconfig - Copyright (c) Keith Packard et al;\n"
 #ifdef WITH_FFTW3
-		"    FFTW - Copyright (c) Matteo Frigo, Massachusetts Institute of Technology;\n"
+                           "    FFTW - Copyright (c) Matteo Frigo, Massachusetts Institute of Technology;\n"
 #endif
-		+ _("\nSee the help file for full credits.\n")
+                           + _("\nSee the help file for full credits.\n")
 #ifdef BUILD_CREDIT
-		+ fmt_tl("Built by %s on %s.", GetAegisubBuildCredit(), GetAegisubBuildTime())
+                           + fmt_tl("Built by %s on %s.", GetAegisubBuildCredit(), GetAegisubBuildTime())
 #endif
-		;
+                           ;
 
-	// Replace copyright symbol
-	wxChar copySymbol = 0xA9;
-	aboutString.Replace("(c)", wxString(copySymbol));
+    // Replace copyright symbol
+    wxChar copySymbol = 0xA9;
+    aboutString.Replace("(c)", wxString(copySymbol));
 
-	wxTextCtrl *textctrl = new wxTextCtrl(&d, -1, aboutString, wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE);
+    wxTextCtrl *textctrl = new wxTextCtrl(&d, -1, aboutString, wxDefaultPosition, wxSize(-1, 200), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_NONE);
 
-	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(new wxStaticBitmap(&d, -1, GETIMAGE(splash)), 0, wxCENTER, 0);
-	MainSizer->Add(new wxStaticLine(&d, wxID_ANY), 0, wxEXPAND | wxALL, 0);
-	MainSizer->Add(textctrl, 0, wxEXPAND | wxALL, 0);
-	MainSizer->Add(new wxStaticLine(&d, wxID_ANY), 0, wxEXPAND | wxALL, 0);
-	MainSizer->Add(d.CreateButtonSizer(wxOK), 0, wxEXPAND | wxALL, 6);
+    wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(new wxStaticBitmap(&d, -1, GETIMAGE(splash)), 0, wxCENTER, 0);
+    MainSizer->Add(new wxStaticLine(&d, wxID_ANY), 0, wxEXPAND | wxALL, 0);
+    MainSizer->Add(textctrl, 0, wxEXPAND | wxALL, 0);
+    MainSizer->Add(new wxStaticLine(&d, wxID_ANY), 0, wxEXPAND | wxALL, 0);
+    MainSizer->Add(d.CreateButtonSizer(wxOK), 0, wxEXPAND | wxALL, 6);
 
-	d.SetSizerAndFit(MainSizer);
-	d.CentreOnParent();
-	d.ShowModal();
+    d.SetSizerAndFit(MainSizer);
+    d.CentreOnParent();
+    d.ShowModal();
 }
diff --git a/src/dialog_attachments.cpp b/src/dialog_attachments.cpp
index 38ff5302787c02f107773ba2fcf0aaf0bb0098fe..c4431a5a8bed49e04e857f9678a2cb952c6c548f 100644
--- a/src/dialog_attachments.cpp
+++ b/src/dialog_attachments.cpp
@@ -44,162 +44,170 @@
 
 namespace {
 struct DialogAttachments {
-	wxDialog d;
-	AssFile *ass;
+    wxDialog d;
+    AssFile *ass;
 
-	wxListView *listView;
-	wxButton *extractButton;
-	wxButton *deleteButton;
+    wxListView *listView;
+    wxButton *extractButton;
+    wxButton *deleteButton;
 
-	void OnAttachFont(wxCommandEvent &event);
-	void OnAttachGraphics(wxCommandEvent &event);
-	void OnExtract(wxCommandEvent &event);
-	void OnDelete(wxCommandEvent &event);
-	void OnListClick(wxListEvent &event);
+    void OnAttachFont(wxCommandEvent &event);
+    void OnAttachGraphics(wxCommandEvent &event);
+    void OnExtract(wxCommandEvent &event);
+    void OnDelete(wxCommandEvent &event);
+    void OnListClick(wxListEvent &event);
 
-	void UpdateList();
-	void AttachFile(wxFileDialog &diag, wxString const& commit_msg);
+    void UpdateList();
+    void AttachFile(wxFileDialog &diag, wxString const &commit_msg);
 
 public:
-	DialogAttachments(wxWindow *parent, AssFile *ass);
+    DialogAttachments(wxWindow *parent, AssFile *ass);
 };
 
 DialogAttachments::DialogAttachments(wxWindow *parent, AssFile *ass)
-: d(parent, -1, _("Attachment List"))
-, ass(ass)
+    : d(parent, -1, _("Attachment List"))
+    , ass(ass)
 {
-	d.SetIcon(GETICON(attach_button_16));
-
-	listView = new wxListView(&d, -1, wxDefaultPosition, wxSize(500, 200));
-	UpdateList();
-
-	auto attachFont = new wxButton(&d, -1, _("Attach &Font"));
-	auto attachGraphics = new wxButton(&d, -1, _("Attach &Graphics"));
-	extractButton = new wxButton(&d, -1, _("E&xtract"));
-	deleteButton = new wxButton(&d, -1, _("&Delete"));
-	extractButton->Enable(false);
-	deleteButton->Enable(false);
-
-	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(&d, "Attachment Manager"), 1, wxLEFT, 5);
-	buttonSizer->Add(new wxButton(&d, wxID_CANCEL, _("&Close")), 1);
-
-	auto mainSizer = new wxBoxSizer(wxVERTICAL);
-	mainSizer->Add(listView, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 5);
-	mainSizer->Add(buttonSizer, 0, wxALL | wxEXPAND, 5);
-	d.SetSizerAndFit(mainSizer);
-	d.CenterOnParent();
-
-	attachFont->Bind(wxEVT_BUTTON, &DialogAttachments::OnAttachFont, this);
-	attachGraphics->Bind(wxEVT_BUTTON, &DialogAttachments::OnAttachGraphics, this);
-	extractButton->Bind(wxEVT_BUTTON, &DialogAttachments::OnExtract, this);
-	deleteButton->Bind(wxEVT_BUTTON, &DialogAttachments::OnDelete, this);
-
-	listView->Bind(wxEVT_LIST_ITEM_SELECTED, &DialogAttachments::OnListClick, this);
-	listView->Bind(wxEVT_LIST_ITEM_DESELECTED, &DialogAttachments::OnListClick, this);
-	listView->Bind(wxEVT_LIST_ITEM_FOCUSED, &DialogAttachments::OnListClick, this);
+    d.SetIcon(GETICON(attach_button_16));
+
+    listView = new wxListView(&d, -1, wxDefaultPosition, wxSize(500, 200));
+    UpdateList();
+
+    auto attachFont = new wxButton(&d, -1, _("Attach &Font"));
+    auto attachGraphics = new wxButton(&d, -1, _("Attach &Graphics"));
+    extractButton = new wxButton(&d, -1, _("E&xtract"));
+    deleteButton = new wxButton(&d, -1, _("&Delete"));
+    extractButton->Enable(false);
+    deleteButton->Enable(false);
+
+    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(&d, "Attachment Manager"), 1, wxLEFT, 5);
+    buttonSizer->Add(new wxButton(&d, wxID_CANCEL, _("&Close")), 1);
+
+    auto mainSizer = new wxBoxSizer(wxVERTICAL);
+    mainSizer->Add(listView, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 5);
+    mainSizer->Add(buttonSizer, 0, wxALL | wxEXPAND, 5);
+    d.SetSizerAndFit(mainSizer);
+    d.CenterOnParent();
+
+    attachFont->Bind(wxEVT_BUTTON, &DialogAttachments::OnAttachFont, this);
+    attachGraphics->Bind(wxEVT_BUTTON, &DialogAttachments::OnAttachGraphics, this);
+    extractButton->Bind(wxEVT_BUTTON, &DialogAttachments::OnExtract, this);
+    deleteButton->Bind(wxEVT_BUTTON, &DialogAttachments::OnDelete, this);
+
+    listView->Bind(wxEVT_LIST_ITEM_SELECTED, &DialogAttachments::OnListClick, this);
+    listView->Bind(wxEVT_LIST_ITEM_DESELECTED, &DialogAttachments::OnListClick, this);
+    listView->Bind(wxEVT_LIST_ITEM_FOCUSED, &DialogAttachments::OnListClick, this);
 }
 
-void DialogAttachments::UpdateList() {
-	listView->ClearAll();
-
-	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);
-
-	for (auto& attach : ass->Attachments) {
-		int row = listView->GetItemCount();
-		listView->InsertItem(row, to_wx(attach.GetFileName(true)));
-		listView->SetItem(row, 1, PrettySize(attach.GetSize()));
-		listView->SetItem(row, 2, to_wx(attach.GroupHeader()));
-	}
+void DialogAttachments::UpdateList()
+{
+    listView->ClearAll();
+
+    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);
+
+    for (auto &attach : ass->Attachments) {
+        int row = listView->GetItemCount();
+        listView->InsertItem(row, to_wx(attach.GetFileName(true)));
+        listView->SetItem(row, 1, PrettySize(attach.GetSize()));
+        listView->SetItem(row, 2, to_wx(attach.GroupHeader()));
+    }
 }
 
-void DialogAttachments::AttachFile(wxFileDialog &diag, wxString const& commit_msg) {
-	if (diag.ShowModal() == wxID_CANCEL) return;
+void DialogAttachments::AttachFile(wxFileDialog &diag, wxString const &commit_msg)
+{
+    if (diag.ShowModal() == wxID_CANCEL) return;
 
-	wxArrayString paths;
-	diag.GetPaths(paths);
+    wxArrayString paths;
+    diag.GetPaths(paths);
 
-	for (auto const& fn : paths)
-		ass->InsertAttachment(agi::fs::path(fn.wx_str()));
+    for (auto const &fn : paths)
+        ass->InsertAttachment(agi::fs::path(fn.wx_str()));
 
-	ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT);
+    ass->Commit(commit_msg, AssFile::COMMIT_ATTACHMENT);
 
-	UpdateList();
+    UpdateList();
 }
 
-void DialogAttachments::OnAttachFont(wxCommandEvent &) {
-	wxFileDialog diag(&d,
-		_("Choose file to be attached"),
-		to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()), "", "Font Files (*.ttf)|*.ttf",
-		wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
+void DialogAttachments::OnAttachFont(wxCommandEvent &)
+{
+    wxFileDialog diag(&d,
+                      _("Choose file to be attached"),
+                      to_wx(OPT_GET("Path/Fonts Collector Destination")->GetString()), "", "Font Files (*.ttf)|*.ttf",
+                      wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
 
-	AttachFile(diag, _("attach font file"));
+    AttachFile(diag, _("attach font file"));
 }
 
-void DialogAttachments::OnAttachGraphics(wxCommandEvent &) {
-	wxFileDialog diag(&d,
-		_("Choose file to be attached"),
-		"", "",
-		"Graphic Files (*.bmp, *.gif, *.jpg, *.ico, *.wmf)|*.bmp;*.gif;*.jpg;*.ico;*.wmf",
-		wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
+void DialogAttachments::OnAttachGraphics(wxCommandEvent &)
+{
+    wxFileDialog diag(&d,
+                      _("Choose file to be attached"),
+                      "", "",
+                      "Graphic Files (*.bmp, *.gif, *.jpg, *.ico, *.wmf)|*.bmp;*.gif;*.jpg;*.ico;*.wmf",
+                      wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
 
-	AttachFile(diag, _("attach graphics file"));
+    AttachFile(diag, _("attach graphics file"));
 }
 
-void DialogAttachments::OnExtract(wxCommandEvent &) {
-	int i = listView->GetFirstSelected();
-	if (i == -1) return;
-
-	agi::fs::path 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())).c_str();
-	else {
-		path = SaveFileSelector(
-			_("Select the path to save the file to:"),
-			"Path/Fonts Collector Destination",
-			ass->Attachments[i].GetFileName(),
-			".ttf", "Font Files (*.ttf)|*.ttf",
-			&d);
-		fullPath = true;
-	}
-	if (path.empty()) return;
-
-	// Loop through items in list
-	while (i != -1) {
-		auto& attach = ass->Attachments[i];
-		attach.Extract(fullPath ? path : path/attach.GetFileName());
-		i = listView->GetNextSelected(i);
-	}
+void DialogAttachments::OnExtract(wxCommandEvent &)
+{
+    int i = listView->GetFirstSelected();
+    if (i == -1) return;
+
+    agi::fs::path 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())).c_str();
+    else {
+        path = SaveFileSelector(
+                   _("Select the path to save the file to:"),
+                   "Path/Fonts Collector Destination",
+                   ass->Attachments[i].GetFileName(),
+                   ".ttf", "Font Files (*.ttf)|*.ttf",
+                   &d);
+        fullPath = true;
+    }
+    if (path.empty()) return;
+
+    // Loop through items in list
+    while (i != -1) {
+        auto &attach = ass->Attachments[i];
+        attach.Extract(fullPath ? path : path / attach.GetFileName());
+        i = listView->GetNextSelected(i);
+    }
 }
 
-void DialogAttachments::OnDelete(wxCommandEvent &) {
-	size_t removed = 0;
-	for (auto i = listView->GetFirstSelected(); i != -1; i = listView->GetNextSelected(i))
-		ass->Attachments.erase(ass->Attachments.begin() + i - removed++);
+void DialogAttachments::OnDelete(wxCommandEvent &)
+{
+    size_t removed = 0;
+    for (auto i = listView->GetFirstSelected(); i != -1; i = listView->GetNextSelected(i))
+        ass->Attachments.erase(ass->Attachments.begin() + i - removed++);
 
-	ass->Commit(_("remove attachment"), AssFile::COMMIT_ATTACHMENT);
+    ass->Commit(_("remove attachment"), AssFile::COMMIT_ATTACHMENT);
 
-	UpdateList();
-	extractButton->Enable(false);
-	deleteButton->Enable(false);
+    UpdateList();
+    extractButton->Enable(false);
+    deleteButton->Enable(false);
 }
 
-void DialogAttachments::OnListClick(wxListEvent &) {
-	bool hasSel = listView->GetFirstSelected() != -1;
-	extractButton->Enable(hasSel);
-	deleteButton->Enable(hasSel);
+void DialogAttachments::OnListClick(wxListEvent &)
+{
+    bool hasSel = listView->GetFirstSelected() != -1;
+    extractButton->Enable(hasSel);
+    deleteButton->Enable(hasSel);
 }
 }
 
-void ShowAttachmentsDialog(wxWindow *parent, AssFile *file) {
-	DialogAttachments(parent, file).d.ShowModal();
+void ShowAttachmentsDialog(wxWindow *parent, AssFile *file)
+{
+    DialogAttachments(parent, file).d.ShowModal();
 }
diff --git a/src/dialog_automation.cpp b/src/dialog_automation.cpp
index dd6b35259c1fb9c0e640bac6f3f6e7552babf727..257757d57990751d23a69f37fcc33dbc32ecb160 100644
--- a/src/dialog_automation.cpp
+++ b/src/dialog_automation.cpp
@@ -54,263 +54,264 @@
 namespace {
 /// Struct to attach a flag for global/local to scripts
 struct ExtraScriptInfo {
-	Automation4::Script *script;
-	bool is_global;
+    Automation4::Script *script;
+    bool is_global;
 };
 
 class DialogAutomation final : public wxDialog {
-	agi::Context *context;
+    agi::Context *context;
 
-	/// Currently loaded scripts
-	std::vector<ExtraScriptInfo> script_info;
+    /// Currently loaded scripts
+    std::vector<ExtraScriptInfo> script_info;
 
-	/// File-local script manager
-	Automation4::ScriptManager *local_manager;
+    /// File-local script manager
+    Automation4::ScriptManager *local_manager;
 
-	/// Listener for external changes to the local scripts
-	agi::signal::Connection local_scripts_changed;
+    /// Listener for external changes to the local scripts
+    agi::signal::Connection local_scripts_changed;
 
-	/// Global script manager
-	Automation4::ScriptManager *global_manager;
+    /// Global script manager
+    Automation4::ScriptManager *global_manager;
 
-	/// Listener for external changes to the global scripts
-	agi::signal::Connection global_scripts_changed;
+    /// Listener for external changes to the global scripts
+    agi::signal::Connection global_scripts_changed;
 
 
-	/// List of loaded scripts
-	wxListView *list;
+    /// List of loaded scripts
+    wxListView *list;
 
-	/// Unload a local script
-	wxButton *remove_button;
+    /// Unload a local script
+    wxButton *remove_button;
 
-	/// Reload a script
-	wxButton *reload_button;
+    /// Reload a script
+    wxButton *reload_button;
 
-	void RebuildList();
-	void AddScript(Automation4::Script *script, bool is_global);
-	void SetScriptInfo(int i, Automation4::Script *script);
-	void UpdateDisplay();
+    void RebuildList();
+    void AddScript(Automation4::Script *script, bool is_global);
+    void SetScriptInfo(int i, Automation4::Script *script);
+    void UpdateDisplay();
 
-	void OnAdd(wxCommandEvent &);
-	void OnRemove(wxCommandEvent &);
-	void OnReload(wxCommandEvent &);
+    void OnAdd(wxCommandEvent &);
+    void OnRemove(wxCommandEvent &);
+    void OnReload(wxCommandEvent &);
 
-	void OnInfo(wxCommandEvent &);
-	void OnReloadAutoload(wxCommandEvent &);
+    void OnInfo(wxCommandEvent &);
+    void OnReloadAutoload(wxCommandEvent &);
 
 public:
-	DialogAutomation(agi::Context *context);
+    DialogAutomation(agi::Context *context);
 };
 
 DialogAutomation::DialogAutomation(agi::Context *c)
-: wxDialog(c->parent, -1, _("Automation Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
-, context(c)
-, local_manager(c->local_scripts.get())
-, local_scripts_changed(local_manager->AddScriptChangeListener(&DialogAutomation::RebuildList, this))
-, global_manager(config::global_scripts)
-, global_scripts_changed(global_manager->AddScriptChangeListener(&DialogAutomation::RebuildList, this))
+    : wxDialog(c->parent, -1, _("Automation Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+    , context(c)
+    , local_manager(c->local_scripts.get())
+    , local_scripts_changed(local_manager->AddScriptChangeListener(&DialogAutomation::RebuildList, this))
+    , global_manager(config::global_scripts)
+    , global_scripts_changed(global_manager->AddScriptChangeListener(&DialogAutomation::RebuildList, this))
 {
-	SetIcon(GETICON(automation_toolbutton_16));
-
-	// create main controls
-	list = new wxListView(this, -1, wxDefaultPosition, wxSize(600, 175), wxLC_REPORT|wxLC_SINGLE_SEL);
-	wxButton *add_button = new wxButton(this, -1, _("&Add"));
-	remove_button = new wxButton(this, -1, _("&Remove"));
-	reload_button = new wxButton(this, -1, _("Re&load"));
-	wxButton *info_button = new wxButton(this, -1, _("Show &Info"));
-	wxButton *reload_autoload_button = new wxButton(this, -1, _("Re&scan Autoload Dir"));
-	wxButton *close_button = new wxButton(this, wxID_CANCEL, _("&Close"));
-
-	list->Bind(wxEVT_LIST_ITEM_SELECTED, std::bind(&DialogAutomation::UpdateDisplay, this));
-	list->Bind(wxEVT_LIST_ITEM_DESELECTED, std::bind(&DialogAutomation::UpdateDisplay, this));
-	add_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnAdd, this);
-	remove_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnRemove, this);
-	reload_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnReload, this);
-	info_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnInfo, this);
-	reload_autoload_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnReloadAutoload, this);
-
-	// add headers to list view
-	list->InsertColumn(0, "", wxLIST_FORMAT_CENTER, 20);
-	list->InsertColumn(1, _("Name"), wxLIST_FORMAT_LEFT, 140);
-	list->InsertColumn(2, _("Filename"), wxLIST_FORMAT_LEFT, 90);
-	list->InsertColumn(3, _("Description"), wxLIST_FORMAT_LEFT, 330);
-
-	// button layout
-	wxSizer *button_box = new wxBoxSizer(wxHORIZONTAL);
-	button_box->AddStretchSpacer(2);
-	button_box->Add(add_button, 0);
-	button_box->Add(remove_button, 0);
-	button_box->AddSpacer(10);
-	button_box->Add(reload_button, 0);
-	button_box->Add(info_button, 0);
-	button_box->AddSpacer(10);
-	button_box->Add(reload_autoload_button, 0);
-	button_box->AddSpacer(10);
-	button_box->Add(new HelpButton(this,"Automation Manager"), 0);
-	button_box->Add(close_button, 0);
-	button_box->AddStretchSpacer(2);
-
-	// main layout
-	wxSizer *main_box = new wxBoxSizer(wxVERTICAL);
-	main_box->Add(list, wxSizerFlags(1).Expand().Border());
-	main_box->Add(button_box, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	SetSizerAndFit(main_box);
-	Center();
-
-	// why doesn't this work... the button gets the "default" decoration but doesn't answer to Enter
-	// ("esc" does work)
-	SetDefaultItem(close_button);
-	SetAffirmativeId(wxID_CANCEL);
-	close_button->SetDefault();
-
-	RebuildList();
+    SetIcon(GETICON(automation_toolbutton_16));
+
+    // create main controls
+    list = new wxListView(this, -1, wxDefaultPosition, wxSize(600, 175), wxLC_REPORT | wxLC_SINGLE_SEL);
+    wxButton *add_button = new wxButton(this, -1, _("&Add"));
+    remove_button = new wxButton(this, -1, _("&Remove"));
+    reload_button = new wxButton(this, -1, _("Re&load"));
+    wxButton *info_button = new wxButton(this, -1, _("Show &Info"));
+    wxButton *reload_autoload_button = new wxButton(this, -1, _("Re&scan Autoload Dir"));
+    wxButton *close_button = new wxButton(this, wxID_CANCEL, _("&Close"));
+
+    list->Bind(wxEVT_LIST_ITEM_SELECTED, std::bind(&DialogAutomation::UpdateDisplay, this));
+    list->Bind(wxEVT_LIST_ITEM_DESELECTED, std::bind(&DialogAutomation::UpdateDisplay, this));
+    add_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnAdd, this);
+    remove_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnRemove, this);
+    reload_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnReload, this);
+    info_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnInfo, this);
+    reload_autoload_button->Bind(wxEVT_BUTTON, &DialogAutomation::OnReloadAutoload, this);
+
+    // add headers to list view
+    list->InsertColumn(0, "", wxLIST_FORMAT_CENTER, 20);
+    list->InsertColumn(1, _("Name"), wxLIST_FORMAT_LEFT, 140);
+    list->InsertColumn(2, _("Filename"), wxLIST_FORMAT_LEFT, 90);
+    list->InsertColumn(3, _("Description"), wxLIST_FORMAT_LEFT, 330);
+
+    // button layout
+    wxSizer *button_box = new wxBoxSizer(wxHORIZONTAL);
+    button_box->AddStretchSpacer(2);
+    button_box->Add(add_button, 0);
+    button_box->Add(remove_button, 0);
+    button_box->AddSpacer(10);
+    button_box->Add(reload_button, 0);
+    button_box->Add(info_button, 0);
+    button_box->AddSpacer(10);
+    button_box->Add(reload_autoload_button, 0);
+    button_box->AddSpacer(10);
+    button_box->Add(new HelpButton(this, "Automation Manager"), 0);
+    button_box->Add(close_button, 0);
+    button_box->AddStretchSpacer(2);
+
+    // main layout
+    wxSizer *main_box = new wxBoxSizer(wxVERTICAL);
+    main_box->Add(list, wxSizerFlags(1).Expand().Border());
+    main_box->Add(button_box, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    SetSizerAndFit(main_box);
+    Center();
+
+    // why doesn't this work... the button gets the "default" decoration but doesn't answer to Enter
+    // ("esc" does work)
+    SetDefaultItem(close_button);
+    SetAffirmativeId(wxID_CANCEL);
+    close_button->SetDefault();
+
+    RebuildList();
 }
 
 void DialogAutomation::RebuildList()
 {
-	script_info.clear();
-	list->DeleteAllItems();
+    script_info.clear();
+    list->DeleteAllItems();
 
-	for (auto& script : local_manager->GetScripts()) AddScript(script.get(), false);
-	for (auto& script : global_manager->GetScripts()) AddScript(script.get(), true);
+    for (auto &script : local_manager->GetScripts()) AddScript(script.get(), false);
+    for (auto &script : global_manager->GetScripts()) AddScript(script.get(), true);
 
-	UpdateDisplay();
+    UpdateDisplay();
 }
 
 void DialogAutomation::SetScriptInfo(int i, Automation4::Script *script)
 {
-	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
-		list->SetItemBackgroundColour(i, list->GetBackgroundColour());
+    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
+        list->SetItemBackgroundColour(i, list->GetBackgroundColour());
 }
 
 void DialogAutomation::AddScript(Automation4::Script *script, bool is_global)
 {
-	ExtraScriptInfo ei = { script, is_global };
-	script_info.push_back(ei);
-
-	wxListItem itm;
-	itm.SetText(is_global ? "G" : "L");
-	itm.SetData((int)script_info.size()-1);
-	itm.SetId(list->GetItemCount());
-	SetScriptInfo(list->InsertItem(itm), script);
+    ExtraScriptInfo ei = { script, is_global };
+    script_info.push_back(ei);
+
+    wxListItem itm;
+    itm.SetText(is_global ? "G" : "L");
+    itm.SetData((int)script_info.size() - 1);
+    itm.SetId(list->GetItemCount());
+    SetScriptInfo(list->InsertItem(itm), script);
 }
 
 void DialogAutomation::UpdateDisplay()
 {
-	int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-	bool selected = i >= 0;
-	bool local = selected && !script_info[list->GetItemData(i)].is_global;
-	remove_button->Enable(local);
-	reload_button->Enable(selected);
+    int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+    bool selected = i >= 0;
+    bool local = selected && !script_info[list->GetItemData(i)].is_global;
+    remove_button->Enable(local);
+    reload_button->Enable(selected);
 }
 
 template<class Container>
-static bool has_file(Container const& c, agi::fs::path const& fn)
+static bool has_file(Container const &c, agi::fs::path const &fn)
 {
-	return any_of(c.begin(), c.end(),
-		[&](std::unique_ptr<Automation4::Script> const& s) { return fn == s->GetFilename(); });
+    return any_of(c.begin(), c.end(),
+    [&](std::unique_ptr<Automation4::Script> const & s) { return fn == s->GetFilename(); });
 }
 
 void DialogAutomation::OnAdd(wxCommandEvent &)
 {
-	wxFileDialog diag(this,
-		_("Add Automation script"),
-		to_wx(OPT_GET("Path/Last/Automation")->GetString()),
-		"",
-		to_wx(Automation4::ScriptFactory::GetWildcardStr()),
-		wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
+    wxFileDialog diag(this,
+                      _("Add Automation script"),
+                      to_wx(OPT_GET("Path/Last/Automation")->GetString()),
+                      "",
+                      to_wx(Automation4::ScriptFactory::GetWildcardStr()),
+                      wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE);
 
-	if (diag.ShowModal() == wxID_CANCEL) return;
+    if (diag.ShowModal() == wxID_CANCEL) return;
 
-	wxArrayString fnames;
-	diag.GetPaths(fnames);
+    wxArrayString fnames;
+    diag.GetPaths(fnames);
 
-	for (auto const& fname : fnames) {
-		agi::fs::path fnpath(fname.wx_str());
-		OPT_SET("Path/Last/Automation")->SetString(fnpath.parent_path().string());
+    for (auto const &fname : fnames) {
+        agi::fs::path fnpath(fname.wx_str());
+        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;
-		}
+        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(fnpath, true));
-	}
+        local_manager->Add(Automation4::ScriptFactory::CreateFromFile(fnpath, true));
+    }
 }
 
 void DialogAutomation::OnRemove(wxCommandEvent &)
 {
-	int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-	if (i < 0) return;
-	const ExtraScriptInfo &ei = script_info[list->GetItemData(i)];
-	if (ei.is_global) return;
+    int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+    if (i < 0) return;
+    const ExtraScriptInfo &ei = script_info[list->GetItemData(i)];
+    if (ei.is_global) return;
 
-	local_manager->Remove(ei.script);
+    local_manager->Remove(ei.script);
 }
 
 void DialogAutomation::OnReload(wxCommandEvent &)
 {
-	int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-	if (i < 0) return;
-
-	ExtraScriptInfo const& ei = script_info[list->GetItemData(i)];
-	if (ei.is_global)
-		global_manager->Reload(ei.script);
-	else
-		local_manager->Reload(ei.script);
+    int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+    if (i < 0) return;
+
+    ExtraScriptInfo const &ei = script_info[list->GetItemData(i)];
+    if (ei.is_global)
+        global_manager->Reload(ei.script);
+    else
+        local_manager->Reload(ei.script);
 }
 
 void DialogAutomation::OnInfo(wxCommandEvent &)
 {
-	int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-	ExtraScriptInfo *ei = i >= 0 ? &script_info[list->GetItemData(i)] : nullptr;
-
-	wxArrayString info;
-	std::back_insert_iterator<wxArrayString> append_info(info);
-
-	info.push_back(fmt_tl(
-		"Total scripts loaded: %d\nGlobal scripts loaded: %d\nLocal scripts loaded: %d\n",
-		local_manager->GetScripts().size() + global_manager->GetScripts().size(),
-		global_manager->GetScripts().size(),
-		local_manager->GetScripts().size()));
-
-	info.push_back(_("Scripting engines installed:"));
-	boost::transform(Automation4::ScriptFactory::GetFactories(), append_info,
-		[](std::unique_ptr<Automation4::ScriptFactory> const& f) {
-			return fmt_wx("- %s (%s)", f->GetEngineName(), f->GetFilenamePattern());
-		});
-
-	if (ei) {
-		info.push_back(fmt_tl("\nScript info:\nName: %s\nDescription: %s\nAuthor: %s\nVersion: %s\nFull path: %s\nState: %s\n\nFeatures provided by script:",
-			ei->script->GetName(),
-			ei->script->GetDescription(),
-			ei->script->GetAuthor(),
-			ei->script->GetVersion(),
-			ei->script->GetFilename().wstring(),
-			ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load")));
-
-		boost::transform(ei->script->GetMacros(), append_info, [=](const cmd::Command *f) {
-			return fmt_tl("    Macro: %s (%s)", f->StrDisplay(context), f->name());
-		});
-		boost::transform(ei->script->GetFilters(), append_info, [](const Automation4::ExportFilter* f) {
-			return fmt_tl("    Export filter: %s", f->GetName());
-		});
-	}
-
-	wxMessageBox(wxJoin(info, '\n', 0), _("Automation Script Info"));
+    int i = list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+    ExtraScriptInfo *ei = i >= 0 ? &script_info[list->GetItemData(i)] : nullptr;
+
+    wxArrayString info;
+    std::back_insert_iterator<wxArrayString> append_info(info);
+
+    info.push_back(fmt_tl(
+                       "Total scripts loaded: %d\nGlobal scripts loaded: %d\nLocal scripts loaded: %d\n",
+                       local_manager->GetScripts().size() + global_manager->GetScripts().size(),
+                       global_manager->GetScripts().size(),
+                       local_manager->GetScripts().size()));
+
+    info.push_back(_("Scripting engines installed:"));
+    boost::transform(Automation4::ScriptFactory::GetFactories(), append_info,
+    [](std::unique_ptr<Automation4::ScriptFactory> const & f) {
+        return fmt_wx("- %s (%s)", f->GetEngineName(), f->GetFilenamePattern());
+    });
+
+    if (ei) {
+        info.push_back(fmt_tl("\nScript info:\nName: %s\nDescription: %s\nAuthor: %s\nVersion: %s\nFull path: %s\nState: %s\n\nFeatures provided by script:",
+                              ei->script->GetName(),
+                              ei->script->GetDescription(),
+                              ei->script->GetAuthor(),
+                              ei->script->GetVersion(),
+                              ei->script->GetFilename().wstring(),
+                              ei->script->GetLoadedState() ? _("Correctly loaded") : _("Failed to load")));
+
+        boost::transform(ei->script->GetMacros(), append_info, [ = ](const cmd::Command * f) {
+            return fmt_tl("    Macro: %s (%s)", f->StrDisplay(context), f->name());
+        });
+        boost::transform(ei->script->GetFilters(), append_info, [](const Automation4::ExportFilter * f) {
+            return fmt_tl("    Export filter: %s", f->GetName());
+        });
+    }
+
+    wxMessageBox(wxJoin(info, '\n', 0), _("Automation Script Info"));
 }
 
 void DialogAutomation::OnReloadAutoload(wxCommandEvent &)
 {
-	global_manager->Reload();
+    global_manager->Reload();
 }
 }
 
-void ShowAutomationDialog(agi::Context *c) {
-	c->dialog->Show<DialogAutomation>(c);
+void ShowAutomationDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogAutomation>(c);
 }
diff --git a/src/dialog_autosave.cpp b/src/dialog_autosave.cpp
index c5a5f61cbac90265214854f6ef256e127943ba0c..254fd9dade9b6b10af299d266ff92deee1d5447b 100644
--- a/src/dialog_autosave.cpp
+++ b/src/dialog_autosave.cpp
@@ -35,143 +35,147 @@
 
 namespace {
 struct Version {
-	wxString filename;
-	wxDateTime date;
-	wxString display;
+    wxString filename;
+    wxDateTime date;
+    wxString display;
 };
 
 struct AutosaveFile {
-	wxString name;
-	std::vector<Version> versions;
+    wxString name;
+    std::vector<Version> versions;
 };
 
 class DialogAutosave {
-	wxDialog d;
-	std::vector<AutosaveFile> files;
+    wxDialog d;
+    std::vector<AutosaveFile> files;
 
-	wxListBox *file_list;
-	wxListBox *version_list;
+    wxListBox *file_list;
+    wxListBox *version_list;
 
-	void Populate(std::map<wxString, AutosaveFile> &files_map, std::string const& path, wxString const& filter, wxString const& name_fmt);
-	void OnSelectFile(wxCommandEvent&);
+    void Populate(std::map<wxString, AutosaveFile> &files_map, std::string const &path, wxString const &filter, wxString const &name_fmt);
+    void OnSelectFile(wxCommandEvent &);
 
 public:
-	DialogAutosave(wxWindow *parent);
-	std::string ChosenFile() const;
+    DialogAutosave(wxWindow *parent);
+    std::string ChosenFile() const;
 
-	int ShowModal() { return d.ShowModal(); }
+    int ShowModal() { return d.ShowModal(); }
 };
 
 DialogAutosave::DialogAutosave(wxWindow *parent)
-: d(parent, -1, _("Open autosave file"), wxDefaultPosition, wxSize(800, 350), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+    : d(parent, -1, _("Open autosave file"), wxDefaultPosition, wxSize(800, 350), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
 {
-	d.SetIcon(GETICON(open_toolbutton_16));
-
-	wxSizer *files_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Files"));
-	file_list = new wxListBox(&d, -1);
-	file_list->Bind(wxEVT_LISTBOX, &DialogAutosave::OnSelectFile, this);
-	files_box->Add(file_list, wxSizerFlags(1).Expand().Border());
-
-	wxSizer *versions_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Versions"));
-	version_list = new wxListBox(&d, -1);
-	version_list->Bind(wxEVT_LISTBOX_DCLICK, [=](wxCommandEvent&) { d.EndModal(wxID_OK); });
-	versions_box->Add(version_list, wxSizerFlags(1).Expand().Border());
-
-	wxSizer *boxes_sizer = new wxBoxSizer(wxHORIZONTAL);
-	boxes_sizer->Add(files_box, wxSizerFlags(1).Expand().Border());
-	boxes_sizer->Add(versions_box, wxSizerFlags(1).Expand().Border());
-
-	auto *btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL);
-	btn_sizer->GetAffirmativeButton()->SetLabelText(_("Open"));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(boxes_sizer, wxSizerFlags(1).Expand().Border());
-	main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	d.SetSizer(main_sizer);
-
-	std::map<wxString, AutosaveFile> files_map;
-	Populate(files_map, OPT_GET("Path/Auto/Save")->GetString(), ".AUTOSAVE.ass", "%s");
-	Populate(files_map, OPT_GET("Path/Auto/Backup")->GetString(), ".ORIGINAL.ass", _("%s [ORIGINAL BACKUP]"));
-	Populate(files_map, "?user/recovered", ".ass", _("%s [RECOVERED]"));
-
-	for (auto& file : files_map | boost::adaptors::map_values)
-		files.emplace_back(std::move(file));
-
-	for (auto& file : files) {
-		sort(begin(file.versions), end(file.versions),
-			[](Version const& a, Version const& b) { return a.date > b.date; });
-	}
-
-	sort(begin(files), end(files),
-		[](AutosaveFile const& a, AutosaveFile const& b) { return a.versions[0].date > b.versions[0].date; });
-
-	for (auto const& file : files)
-		file_list->Append(file.name);
-
-	if (file_list->IsEmpty())
-		btn_sizer->GetAffirmativeButton()->Disable();
-	else {
-		file_list->SetSelection(0);
-		wxCommandEvent evt;
-		OnSelectFile(evt);
-	}
+    d.SetIcon(GETICON(open_toolbutton_16));
+
+    wxSizer *files_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Files"));
+    file_list = new wxListBox(&d, -1);
+    file_list->Bind(wxEVT_LISTBOX, &DialogAutosave::OnSelectFile, this);
+    files_box->Add(file_list, wxSizerFlags(1).Expand().Border());
+
+    wxSizer *versions_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Versions"));
+    version_list = new wxListBox(&d, -1);
+    version_list->Bind(wxEVT_LISTBOX_DCLICK, [ = ](wxCommandEvent &) { d.EndModal(wxID_OK); });
+    versions_box->Add(version_list, wxSizerFlags(1).Expand().Border());
+
+    wxSizer *boxes_sizer = new wxBoxSizer(wxHORIZONTAL);
+    boxes_sizer->Add(files_box, wxSizerFlags(1).Expand().Border());
+    boxes_sizer->Add(versions_box, wxSizerFlags(1).Expand().Border());
+
+    auto *btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL);
+    btn_sizer->GetAffirmativeButton()->SetLabelText(_("Open"));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(boxes_sizer, wxSizerFlags(1).Expand().Border());
+    main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    d.SetSizer(main_sizer);
+
+    std::map<wxString, AutosaveFile> files_map;
+    Populate(files_map, OPT_GET("Path/Auto/Save")->GetString(), ".AUTOSAVE.ass", "%s");
+    Populate(files_map, OPT_GET("Path/Auto/Backup")->GetString(), ".ORIGINAL.ass", _("%s [ORIGINAL BACKUP]"));
+    Populate(files_map, "?user/recovered", ".ass", _("%s [RECOVERED]"));
+
+    for (auto &file : files_map | boost::adaptors::map_values)
+        files.emplace_back(std::move(file));
+
+    for (auto &file : files) {
+        sort(begin(file.versions), end(file.versions),
+        [](Version const & a, Version const & b) { return a.date > b.date; });
+    }
+
+    sort(begin(files), end(files),
+    [](AutosaveFile const & a, AutosaveFile const & b) { return a.versions[0].date > b.versions[0].date; });
+
+    for (auto const &file : files)
+        file_list->Append(file.name);
+
+    if (file_list->IsEmpty())
+        btn_sizer->GetAffirmativeButton()->Disable();
+    else {
+        file_list->SetSelection(0);
+        wxCommandEvent evt;
+        OnSelectFile(evt);
+    }
 }
 
-void DialogAutosave::Populate(std::map<wxString, AutosaveFile> &files_map, std::string const& path, wxString const& filter, wxString const& name_fmt) {
-	wxString directory(config::path->Decode(path).wstring());
-
-	wxDir dir;
-	if (!dir.Open(directory)) return;
-
-	wxString fn;
-	if (!dir.GetFirst(&fn, "*" + filter, wxDIR_FILES))
-		return;
-
-	do {
-		wxDateTime date;
-
-		wxString date_str;
-		wxString name = fn.Left(fn.size() - filter.size()).BeforeLast('.', &date_str);
-		if (!name)
-			name = date_str;
-		else {
-			if (!date.ParseFormat(date_str, "%Y-%m-%d-%H-%M-%S"))
-				name += "." + date_str;
-		}
-		if (!date.IsValid())
-			date = wxFileName(directory, fn).GetModificationTime();
-
-		auto it = files_map.find(name);
-		if (it == files_map.end())
-			it = files_map.insert({name, AutosaveFile{name}}).first;
-		it->second.versions.push_back(Version{wxFileName(directory, fn).GetFullPath(), date, agi::wxformat(name_fmt, date.Format())});
-	} while (dir.GetNext(&fn));
+void DialogAutosave::Populate(std::map<wxString, AutosaveFile> &files_map, std::string const &path, wxString const &filter, wxString const &name_fmt)
+{
+    wxString directory(config::path->Decode(path).wstring());
+
+    wxDir dir;
+    if (!dir.Open(directory)) return;
+
+    wxString fn;
+    if (!dir.GetFirst(&fn, "*" + filter, wxDIR_FILES))
+        return;
+
+    do {
+        wxDateTime date;
+
+        wxString date_str;
+        wxString name = fn.Left(fn.size() - filter.size()).BeforeLast('.', &date_str);
+        if (!name)
+            name = date_str;
+        else {
+            if (!date.ParseFormat(date_str, "%Y-%m-%d-%H-%M-%S"))
+                name += "." + date_str;
+        }
+        if (!date.IsValid())
+            date = wxFileName(directory, fn).GetModificationTime();
+
+        auto it = files_map.find(name);
+        if (it == files_map.end())
+            it = files_map.insert({name, AutosaveFile{name}}).first;
+        it->second.versions.push_back(Version{wxFileName(directory, fn).GetFullPath(), date, agi::wxformat(name_fmt, date.Format())});
+    } while (dir.GetNext(&fn));
 }
 
-void DialogAutosave::OnSelectFile(wxCommandEvent&) {
-	version_list->Clear();
-	int sel_file = file_list->GetSelection();
-	if (sel_file < 0) return;
+void DialogAutosave::OnSelectFile(wxCommandEvent &)
+{
+    version_list->Clear();
+    int sel_file = file_list->GetSelection();
+    if (sel_file < 0) return;
 
-	for (auto const& version : files[sel_file].versions)
-		version_list->Append(version.display);
-	version_list->SetSelection(0);
+    for (auto const &version : files[sel_file].versions)
+        version_list->Append(version.display);
+    version_list->SetSelection(0);
 }
 
-std::string DialogAutosave::ChosenFile() const {
-	int sel_file = file_list->GetSelection();
-	if (sel_file < 0) return "";
+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 "";
+    int sel_version = version_list->GetSelection();
+    if (sel_version < 0) return "";
 
-	return from_wx(files[sel_file].versions[sel_version].filename);
+    return from_wx(files[sel_file].versions[sel_version].filename);
 }
 }
 
-std::string PickAutosaveFile(wxWindow *parent) {
-	DialogAutosave dialog(parent);
-	if (dialog.ShowModal() == wxID_OK)
-		return dialog.ChosenFile();
-	return "";
+std::string PickAutosaveFile(wxWindow *parent)
+{
+    DialogAutosave dialog(parent);
+    if (dialog.ShowModal() == wxID_OK)
+        return dialog.ChosenFile();
+    return "";
 }
diff --git a/src/dialog_colorpicker.cpp b/src/dialog_colorpicker.cpp
index 510d2a6cec8f10252d51d9137d794716afa7e9a6..62c99d6c04e5a945af064ae1f9f15ccab15fdf5d 100644
--- a/src/dialog_colorpicker.cpp
+++ b/src/dialog_colorpicker.cpp
@@ -67,9 +67,9 @@
 namespace {
 
 enum class PickerDirection {
-	HorzVert,
-	Horz,
-	Vert
+    HorzVert,
+    Horz,
+    Vert
 };
 
 static const int spectrum_horz_vert_arrow_size = 4;
@@ -77,158 +77,157 @@ static const int spectrum_horz_vert_arrow_size = 4;
 wxDEFINE_EVENT(EVT_SPECTRUM_CHANGE, wxCommandEvent);
 
 class ColorPickerSpectrum final : public wxControl {
-	int x;
-	int y;
-
-	wxBitmap *background;
-	PickerDirection direction;
-
-	void OnPaint(wxPaintEvent &evt) {
-		if (!background) return;
-
-		int height = background->GetHeight();
-		int width = background->GetWidth();
-		wxPaintDC dc(this);
-
-		wxMemoryDC memdc;
-		memdc.SelectObject(*background);
-		dc.Blit(1, 1, width, height, &memdc, 0, 0);
-
-		wxPoint arrow[3];
-		wxRect arrow_box;
-
-		wxPen invpen(*wxWHITE, 3);
-		invpen.SetCap(wxCAP_BUTT);
-		dc.SetLogicalFunction(wxXOR);
-		dc.SetPen(invpen);
-
-		switch (direction) {
-			case PickerDirection::HorzVert:
-				// Make a little cross
-				dc.DrawLine(x-4, y+1, x+7, y+1);
-				dc.DrawLine(x+1, y-4, x+1, y+7);
-				break;
-			case PickerDirection::Horz:
-				// Make a vertical line stretching all the way across
-				dc.DrawLine(x+1, 1, x+1, height+1);
-				// Points for arrow
-				arrow[0] = wxPoint(x+1, height+2);
-				arrow[1] = wxPoint(x+1-spectrum_horz_vert_arrow_size, height+2+spectrum_horz_vert_arrow_size);
-				arrow[2] = wxPoint(x+1+spectrum_horz_vert_arrow_size, height+2+spectrum_horz_vert_arrow_size);
-
-				arrow_box.SetLeft(0);
-				arrow_box.SetTop(height + 2);
-				arrow_box.SetRight(width + 1 + spectrum_horz_vert_arrow_size);
-				arrow_box.SetBottom(height + 2 + spectrum_horz_vert_arrow_size);
-				break;
-			case PickerDirection::Vert:
-				// Make a horizontal line stretching all the way across
-				dc.DrawLine(1, y+1, width+1, y+1);
-				// Points for arrow
-				arrow[0] = wxPoint(width+2, y+1);
-				arrow[1] = wxPoint(width+2+spectrum_horz_vert_arrow_size, y+1-spectrum_horz_vert_arrow_size);
-				arrow[2] = wxPoint(width+2+spectrum_horz_vert_arrow_size, y+1+spectrum_horz_vert_arrow_size);
-
-				arrow_box.SetLeft(width + 2);
-				arrow_box.SetTop(0);
-				arrow_box.SetRight(width + 2 + spectrum_horz_vert_arrow_size);
-				arrow_box.SetBottom(height + 1 + spectrum_horz_vert_arrow_size);
-				break;
-		}
-
-		if (direction == PickerDirection::Horz || direction == PickerDirection::Vert) {
-			wxBrush bgBrush;
-			bgBrush.SetColour(GetBackgroundColour());
-			dc.SetLogicalFunction(wxCOPY);
-			dc.SetPen(*wxTRANSPARENT_PEN);
-			dc.SetBrush(bgBrush);
-			dc.DrawRectangle(arrow_box);
-
-			// Arrow pointing at current point
-			dc.SetBrush(*wxBLACK_BRUSH);
-			dc.DrawPolygon(3, arrow);
-		}
-
-		// Border around the spectrum
-		wxPen blkpen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 1);
-		blkpen.SetCap(wxCAP_BUTT);
-
-		dc.SetLogicalFunction(wxCOPY);
-		dc.SetPen(blkpen);
-		dc.SetBrush(*wxTRANSPARENT_BRUSH);
-		dc.DrawRectangle(0, 0, background->GetWidth()+2, background->GetHeight()+2);
-	}
-
-	void OnMouse(wxMouseEvent &evt) {
-		evt.Skip();
-
-		// We only care about mouse move events during a drag
-		if (evt.Moving())
-			return;
-
-		if (evt.LeftDown()) {
-			CaptureMouse();
-			SetCursor(wxCursor(wxCURSOR_BLANK));
-		}
-		else if (evt.LeftUp() && HasCapture()) {
-			ReleaseMouse();
-			SetCursor(wxNullCursor);
-		}
-
-		if (evt.LeftDown() || (HasCapture() && evt.LeftIsDown())) {
-			// Adjust for the 1px black border around the control
-			int newx = mid(0, evt.GetX() - 1, GetClientSize().x - 3);
-			int newy = mid(0, evt.GetY() - 1, GetClientSize().y - 3);
-			SetXY(newx, newy);
-			wxCommandEvent evt2(EVT_SPECTRUM_CHANGE, GetId());
-			AddPendingEvent(evt2);
-		}
-	}
-
-	bool AcceptsFocusFromKeyboard() const override { return false; }
+    int x;
+    int y;
+
+    wxBitmap *background;
+    PickerDirection direction;
+
+    void OnPaint(wxPaintEvent &evt) {
+        if (!background) return;
+
+        int height = background->GetHeight();
+        int width = background->GetWidth();
+        wxPaintDC dc(this);
+
+        wxMemoryDC memdc;
+        memdc.SelectObject(*background);
+        dc.Blit(1, 1, width, height, &memdc, 0, 0);
+
+        wxPoint arrow[3];
+        wxRect arrow_box;
+
+        wxPen invpen(*wxWHITE, 3);
+        invpen.SetCap(wxCAP_BUTT);
+        dc.SetLogicalFunction(wxXOR);
+        dc.SetPen(invpen);
+
+        switch (direction) {
+        case PickerDirection::HorzVert:
+            // Make a little cross
+            dc.DrawLine(x - 4, y + 1, x + 7, y + 1);
+            dc.DrawLine(x + 1, y - 4, x + 1, y + 7);
+            break;
+        case PickerDirection::Horz:
+            // Make a vertical line stretching all the way across
+            dc.DrawLine(x + 1, 1, x + 1, height + 1);
+            // Points for arrow
+            arrow[0] = wxPoint(x + 1, height + 2);
+            arrow[1] = wxPoint(x + 1 - spectrum_horz_vert_arrow_size, height + 2 + spectrum_horz_vert_arrow_size);
+            arrow[2] = wxPoint(x + 1 + spectrum_horz_vert_arrow_size, height + 2 + spectrum_horz_vert_arrow_size);
+
+            arrow_box.SetLeft(0);
+            arrow_box.SetTop(height + 2);
+            arrow_box.SetRight(width + 1 + spectrum_horz_vert_arrow_size);
+            arrow_box.SetBottom(height + 2 + spectrum_horz_vert_arrow_size);
+            break;
+        case PickerDirection::Vert:
+            // Make a horizontal line stretching all the way across
+            dc.DrawLine(1, y + 1, width + 1, y + 1);
+            // Points for arrow
+            arrow[0] = wxPoint(width + 2, y + 1);
+            arrow[1] = wxPoint(width + 2 + spectrum_horz_vert_arrow_size, y + 1 - spectrum_horz_vert_arrow_size);
+            arrow[2] = wxPoint(width + 2 + spectrum_horz_vert_arrow_size, y + 1 + spectrum_horz_vert_arrow_size);
+
+            arrow_box.SetLeft(width + 2);
+            arrow_box.SetTop(0);
+            arrow_box.SetRight(width + 2 + spectrum_horz_vert_arrow_size);
+            arrow_box.SetBottom(height + 1 + spectrum_horz_vert_arrow_size);
+            break;
+        }
+
+        if (direction == PickerDirection::Horz || direction == PickerDirection::Vert) {
+            wxBrush bgBrush;
+            bgBrush.SetColour(GetBackgroundColour());
+            dc.SetLogicalFunction(wxCOPY);
+            dc.SetPen(*wxTRANSPARENT_PEN);
+            dc.SetBrush(bgBrush);
+            dc.DrawRectangle(arrow_box);
+
+            // Arrow pointing at current point
+            dc.SetBrush(*wxBLACK_BRUSH);
+            dc.DrawPolygon(3, arrow);
+        }
+
+        // Border around the spectrum
+        wxPen blkpen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT), 1);
+        blkpen.SetCap(wxCAP_BUTT);
+
+        dc.SetLogicalFunction(wxCOPY);
+        dc.SetPen(blkpen);
+        dc.SetBrush(*wxTRANSPARENT_BRUSH);
+        dc.DrawRectangle(0, 0, background->GetWidth() + 2, background->GetHeight() + 2);
+    }
+
+    void OnMouse(wxMouseEvent &evt) {
+        evt.Skip();
+
+        // We only care about mouse move events during a drag
+        if (evt.Moving())
+            return;
+
+        if (evt.LeftDown()) {
+            CaptureMouse();
+            SetCursor(wxCursor(wxCURSOR_BLANK));
+        }
+        else if (evt.LeftUp() && HasCapture()) {
+            ReleaseMouse();
+            SetCursor(wxNullCursor);
+        }
+
+        if (evt.LeftDown() || (HasCapture() && evt.LeftIsDown())) {
+            // Adjust for the 1px black border around the control
+            int newx = mid(0, evt.GetX() - 1, GetClientSize().x - 3);
+            int newy = mid(0, evt.GetY() - 1, GetClientSize().y - 3);
+            SetXY(newx, newy);
+            wxCommandEvent evt2(EVT_SPECTRUM_CHANGE, GetId());
+            AddPendingEvent(evt2);
+        }
+    }
+
+    bool AcceptsFocusFromKeyboard() const override { return false; }
 
 public:
-	ColorPickerSpectrum(wxWindow *parent, PickerDirection direction, wxSize size)
-	: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
-	, x(-1)
-	, y(-1)
-	, background(nullptr)
-	, direction(direction)
-	{
-		size.x += 2;
-		size.y += 2;
-
-		if (direction == PickerDirection::Vert) size.x += spectrum_horz_vert_arrow_size + 1;
-		if (direction == PickerDirection::Horz) size.y += spectrum_horz_vert_arrow_size + 1;
-
-		SetClientSize(size);
-		SetMinSize(GetSize());
-
-		Bind(wxEVT_LEFT_DOWN, &ColorPickerSpectrum::OnMouse, this);
-		Bind(wxEVT_LEFT_UP, &ColorPickerSpectrum::OnMouse, this);
-		Bind(wxEVT_MOTION, &ColorPickerSpectrum::OnMouse, this);
-		Bind(wxEVT_PAINT, &ColorPickerSpectrum::OnPaint, this);
-	}
-
-	int GetX() const { return x; }
-	int GetY() const { return y; }
-
-	void SetXY(int xx, int yy) {
-		if (x != xx || y != yy) {
-			x = xx;
-			y = yy;
-			Refresh(false);
-		}
-	}
-
-	/// @brief Set the background image for this spectrum
-	/// @param new_background New background image
-	/// @param force Repaint even if it appears to be the same image
-	void SetBackground(wxBitmap *new_background, bool force = false) {
-		if (background == new_background && !force) return;
-		background = new_background;
-		Refresh(false);
-	}
+    ColorPickerSpectrum(wxWindow *parent, PickerDirection direction, wxSize size)
+        : wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
+        , x(-1)
+        , y(-1)
+        , background(nullptr)
+        , direction(direction) {
+        size.x += 2;
+        size.y += 2;
+
+        if (direction == PickerDirection::Vert) size.x += spectrum_horz_vert_arrow_size + 1;
+        if (direction == PickerDirection::Horz) size.y += spectrum_horz_vert_arrow_size + 1;
+
+        SetClientSize(size);
+        SetMinSize(GetSize());
+
+        Bind(wxEVT_LEFT_DOWN, &ColorPickerSpectrum::OnMouse, this);
+        Bind(wxEVT_LEFT_UP, &ColorPickerSpectrum::OnMouse, this);
+        Bind(wxEVT_MOTION, &ColorPickerSpectrum::OnMouse, this);
+        Bind(wxEVT_PAINT, &ColorPickerSpectrum::OnPaint, this);
+    }
+
+    int GetX() const { return x; }
+    int GetY() const { return y; }
+
+    void SetXY(int xx, int yy) {
+        if (x != xx || y != yy) {
+            x = xx;
+            y = yy;
+            Refresh(false);
+        }
+    }
+
+    /// @brief Set the background image for this spectrum
+    /// @param new_background New background image
+    /// @param force Repaint even if it appears to be the same image
+    void SetBackground(wxBitmap *new_background, bool force = false) {
+        if (background == new_background && !force) return;
+        background = new_background;
+        Refresh(false);
+    }
 };
 
 #ifdef WIN32
@@ -242,871 +241,902 @@ wxDEFINE_EVENT(EVT_RECENT_SELECT, ValueEvent<agi::Color>);
 /// @class ColorPickerRecent
 /// @brief A grid of recently used colors which can be selected by clicking on them
 class ColorPickerRecent final : public wxStaticBitmap {
-	int rows;     ///< Number of rows of colors
-	int cols;     ///< Number of cols of colors
-	int cellsize; ///< Width/Height of each cell
+    int rows;     ///< Number of rows of colors
+    int cols;     ///< Number of cols of colors
+    int cellsize; ///< Width/Height of each cell
 
-	/// The colors currently displayed in the control
-	std::vector<agi::Color> colors;
+    /// The colors currently displayed in the control
+    std::vector<agi::Color> colors;
 
-	void OnClick(wxMouseEvent &evt) {
-		wxSize cs = GetClientSize();
-		int cx = evt.GetX() * cols / cs.x;
-		int cy = evt.GetY() * rows / cs.y;
-		if (cx < 0 || cx > cols || cy < 0 || cy > rows) return;
-		int i = cols*cy + cx;
+    void OnClick(wxMouseEvent &evt) {
+        wxSize cs = GetClientSize();
+        int cx = evt.GetX() * cols / cs.x;
+        int cy = evt.GetY() * rows / cs.y;
+        if (cx < 0 || cx > cols || cy < 0 || cy > rows) return;
+        int i = cols * cy + cx;
 
-		if (i >= 0 && i < (int)colors.size())
-			AddPendingEvent(ValueEvent<agi::Color>(EVT_RECENT_SELECT, GetId(), colors[i]));
-	}
+        if (i >= 0 && i < (int)colors.size())
+            AddPendingEvent(ValueEvent<agi::Color>(EVT_RECENT_SELECT, GetId(), colors[i]));
+    }
 
-	void UpdateBitmap() {
-		wxSize sz = GetClientSize();
+    void UpdateBitmap() {
+        wxSize sz = GetClientSize();
 
-		wxBitmap background(sz.x, sz.y);
-		wxMemoryDC dc(background);
+        wxBitmap background(sz.x, sz.y);
+        wxMemoryDC dc(background);
 
-		dc.SetPen(*wxTRANSPARENT_PEN);
+        dc.SetPen(*wxTRANSPARENT_PEN);
 
-		for (int cy = 0; cy < rows; cy++) {
-			for (int cx = 0; cx < cols; cx++) {
-				int x = cx * cellsize;
-				int y = cy * cellsize;
+        for (int cy = 0; cy < rows; cy++) {
+            for (int cx = 0; cx < cols; cx++) {
+                int x = cx * cellsize;
+                int y = cy * cellsize;
 
-				dc.SetBrush(wxBrush(to_wx(colors[cy * cols + cx])));
-				dc.DrawRectangle(x, y, x+cellsize, y+cellsize);
-			}
-		}
+                dc.SetBrush(wxBrush(to_wx(colors[cy * cols + cx])));
+                dc.DrawRectangle(x, y, x + cellsize, y + cellsize);
+            }
+        }
 
-		{
-			wxEventBlocker blocker(this);
-			SetBitmap(background);
-		}
+        {
+            wxEventBlocker blocker(this);
+            SetBitmap(background);
+        }
 
-		Refresh(false);
-	}
+        Refresh(false);
+    }
 
-	bool AcceptsFocusFromKeyboard() const override { return false; }
+    bool AcceptsFocusFromKeyboard() const override { return false; }
 
 public:
-	ColorPickerRecent(wxWindow *parent, int cols, int rows, int cellsize)
-	: wxStaticBitmap(parent, -1, wxBitmap(), wxDefaultPosition, wxDefaultSize, STATIC_BORDER_FLAG)
-	, rows(rows)
-	, cols(cols)
-	, cellsize(cellsize)
-	{
-		colors.resize(rows * cols);
-		SetClientSize(cols*cellsize, rows*cellsize);
-		SetMinSize(GetSize());
-		SetMaxSize(GetSize());
-		SetCursor(*wxCROSS_CURSOR);
-
-		Bind(wxEVT_LEFT_DOWN, &ColorPickerRecent::OnClick, this);
-		Bind(wxEVT_SIZE, [=](wxSizeEvent&) { UpdateBitmap(); });
-	}
-
-	/// Load the colors to show
-	void Load(std::vector<agi::Color> const& recent_colors) {
-		colors = recent_colors;
-		colors.resize(rows * cols);
-		UpdateBitmap();
-	}
-
-	/// Get the list of recent colors
-	std::vector<agi::Color> Save() const { return colors; }
-
-	/// Add a color to the beginning of the recent list
-	void AddColor(agi::Color color) {
-		auto existing = find(colors.begin(), colors.end(), color);
-		if (existing != colors.end())
-			rotate(colors.begin(), existing, existing + 1);
-		else {
-			colors.insert(colors.begin(), color);
-			colors.pop_back();
-		}
-
-		UpdateBitmap();
-	}
+    ColorPickerRecent(wxWindow *parent, int cols, int rows, int cellsize)
+        : wxStaticBitmap(parent, -1, wxBitmap(), wxDefaultPosition, wxDefaultSize, STATIC_BORDER_FLAG)
+        , rows(rows)
+        , cols(cols)
+        , cellsize(cellsize) {
+        colors.resize(rows * cols);
+        SetClientSize(cols * cellsize, rows * cellsize);
+        SetMinSize(GetSize());
+        SetMaxSize(GetSize());
+        SetCursor(*wxCROSS_CURSOR);
+
+        Bind(wxEVT_LEFT_DOWN, &ColorPickerRecent::OnClick, this);
+        Bind(wxEVT_SIZE, [ = ](wxSizeEvent &) { UpdateBitmap(); });
+    }
+
+    /// Load the colors to show
+    void Load(std::vector<agi::Color> const &recent_colors) {
+        colors = recent_colors;
+        colors.resize(rows * cols);
+        UpdateBitmap();
+    }
+
+    /// Get the list of recent colors
+    std::vector<agi::Color> Save() const { return colors; }
+
+    /// Add a color to the beginning of the recent list
+    void AddColor(agi::Color color) {
+        auto existing = find(colors.begin(), colors.end(), color);
+        if (existing != colors.end())
+            rotate(colors.begin(), existing, existing + 1);
+        else {
+            colors.insert(colors.begin(), color);
+            colors.pop_back();
+        }
+
+        UpdateBitmap();
+    }
 };
 
 wxDEFINE_EVENT(EVT_DROPPER_SELECT, ValueEvent<agi::Color>);
 
 class ColorPickerScreenDropper final : public wxControl {
-	wxBitmap capture;
+    wxBitmap capture;
 
-	int resx, resy;
-	int magnification;
+    int resx, resy;
+    int magnification;
 
-	void OnMouse(wxMouseEvent &evt) {
-		int x = evt.GetX();
-		int y = evt.GetY();
+    void OnMouse(wxMouseEvent &evt) {
+        int x = evt.GetX();
+        int y = evt.GetY();
 
-		if (x >= 0 && x < capture.GetWidth() && y >= 0 && y < capture.GetHeight()) {
-			wxNativePixelData pd(capture, wxRect(x, y, 1, 1));
-			wxNativePixelData::Iterator pdi(pd.GetPixels());
-			agi::Color color(pdi.Red(), pdi.Green(), pdi.Blue(), 0);
+        if (x >= 0 && x < capture.GetWidth() && y >= 0 && y < capture.GetHeight()) {
+            wxNativePixelData pd(capture, wxRect(x, y, 1, 1));
+            wxNativePixelData::Iterator pdi(pd.GetPixels());
+            agi::Color color(pdi.Red(), pdi.Green(), pdi.Blue(), 0);
 
-			wxThreadEvent evnt(EVT_DROPPER_SELECT, GetId());
-			AddPendingEvent(ValueEvent<agi::Color>(EVT_DROPPER_SELECT, GetId(), color));
-		}
-	}
+            wxThreadEvent evnt(EVT_DROPPER_SELECT, GetId());
+            AddPendingEvent(ValueEvent<agi::Color>(EVT_DROPPER_SELECT, GetId(), color));
+        }
+    }
 
-	void OnPaint(wxPaintEvent &evt) {
-		wxPaintDC(this).DrawBitmap(capture, 0, 0);
-	}
+    void OnPaint(wxPaintEvent &evt) {
+        wxPaintDC(this).DrawBitmap(capture, 0, 0);
+    }
 
-	bool AcceptsFocusFromKeyboard() const override { return false; }
+    bool AcceptsFocusFromKeyboard() const override { return false; }
 
 public:
-	ColorPickerScreenDropper(wxWindow *parent, int resx, int resy, int magnification)
-	: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, STATIC_BORDER_FLAG)
-	, capture(resx * magnification, resy * magnification, wxNativePixelFormat::BitsPerPixel)
-	, resx(resx)
-	, resy(resy)
-	, magnification(magnification)
-	{
-		SetClientSize(resx*magnification, resy*magnification);
-		SetMinSize(GetSize());
-		SetMaxSize(GetSize());
-		SetCursor(*wxCROSS_CURSOR);
-
-		wxMemoryDC capdc(capture);
-		capdc.SetPen(*wxTRANSPARENT_PEN);
-		capdc.SetBrush(*wxWHITE_BRUSH);
-		capdc.DrawRectangle(0, 0, capture.GetWidth(), capture.GetHeight());
-
-		Bind(wxEVT_PAINT, &ColorPickerScreenDropper::OnPaint, this);
-		Bind(wxEVT_LEFT_DOWN, &ColorPickerScreenDropper::OnMouse, this);
-	}
-
-	void DropFromScreenXY(int x, int y);
+    ColorPickerScreenDropper(wxWindow *parent, int resx, int resy, int magnification)
+        : wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, STATIC_BORDER_FLAG)
+        , capture(resx * magnification, resy * magnification, wxNativePixelFormat::BitsPerPixel)
+        , resx(resx)
+        , resy(resy)
+        , magnification(magnification) {
+        SetClientSize(resx * magnification, resy * magnification);
+        SetMinSize(GetSize());
+        SetMaxSize(GetSize());
+        SetCursor(*wxCROSS_CURSOR);
+
+        wxMemoryDC capdc(capture);
+        capdc.SetPen(*wxTRANSPARENT_PEN);
+        capdc.SetBrush(*wxWHITE_BRUSH);
+        capdc.DrawRectangle(0, 0, capture.GetWidth(), capture.GetHeight());
+
+        Bind(wxEVT_PAINT, &ColorPickerScreenDropper::OnPaint, this);
+        Bind(wxEVT_LEFT_DOWN, &ColorPickerScreenDropper::OnMouse, this);
+    }
+
+    void DropFromScreenXY(int x, int y);
 };
 
-void ColorPickerScreenDropper::DropFromScreenXY(int x, int y) {
-	wxMemoryDC capdc(capture);
-	capdc.SetPen(*wxTRANSPARENT_PEN);
+void ColorPickerScreenDropper::DropFromScreenXY(int x, int y)
+{
+    wxMemoryDC capdc(capture);
+    capdc.SetPen(*wxTRANSPARENT_PEN);
 #ifndef __WXMAC__
-	wxScreenDC screen;
-	capdc.StretchBlit(0, 0, resx * magnification, resy * magnification,
-		&screen, x - resx / 2, y - resy / 2, resx, resy);
+    wxScreenDC screen;
+    capdc.StretchBlit(0, 0, resx * magnification, resy * magnification,
+                      &screen, x - resx / 2, y - resy / 2, resx, resy);
 #else
-	// wxScreenDC doesn't work on recent versions of OS X so do it manually
-
-	// Doesn't bother handling the case where the rect overlaps two monitors
-	CGDirectDisplayID display_id;
-	uint32_t display_count;
-	CGGetDisplaysWithPoint(CGPointMake(x, y), 1, &display_id, &display_count);
-
-	agi::scoped_holder<CGImageRef> img(CGDisplayCreateImageForRect(display_id, CGRectMake(x - resx / 2, y - resy / 2, resx, resy)), CGImageRelease);
-	NSUInteger width = CGImageGetWidth(img);
-	NSUInteger height = CGImageGetHeight(img);
-	std::vector<uint8_t> imgdata(height * width * 4);
-
-	agi::scoped_holder<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB(), CGColorSpaceRelease);
-	agi::scoped_holder<CGContextRef> bmp_context(CGBitmapContextCreate(&imgdata[0], width, height, 8, 4 * width, colorspace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big), CGContextRelease);
-
-	CGContextDrawImage(bmp_context, CGRectMake(0, 0, width, height), img);
-
-	for (int x = 0; x < resx; x++) {
-		for (int y = 0; y < resy; y++) {
-			uint8_t *pixel = &imgdata[y * width * 4 + x * 4];
-			capdc.SetBrush(wxBrush(wxColour(pixel[0], pixel[1], pixel[2])));
-			capdc.DrawRectangle(x * magnification, y * magnification, magnification, magnification);
-		}
-	}
+    // wxScreenDC doesn't work on recent versions of OS X so do it manually
+
+    // Doesn't bother handling the case where the rect overlaps two monitors
+    CGDirectDisplayID display_id;
+    uint32_t display_count;
+    CGGetDisplaysWithPoint(CGPointMake(x, y), 1, &display_id, &display_count);
+
+    agi::scoped_holder<CGImageRef> img(CGDisplayCreateImageForRect(display_id, CGRectMake(x - resx / 2, y - resy / 2, resx, resy)), CGImageRelease);
+    NSUInteger width = CGImageGetWidth(img);
+    NSUInteger height = CGImageGetHeight(img);
+    std::vector<uint8_t> imgdata(height * width * 4);
+
+    agi::scoped_holder<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB(), CGColorSpaceRelease);
+    agi::scoped_holder<CGContextRef> bmp_context(CGBitmapContextCreate(&imgdata[0], width, height, 8, 4 * width, colorspace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big), CGContextRelease);
+
+    CGContextDrawImage(bmp_context, CGRectMake(0, 0, width, height), img);
+
+    for (int x = 0; x < resx; x++) {
+        for (int y = 0; y < resy; y++) {
+            uint8_t *pixel = &imgdata[y * width * 4 + x * 4];
+            capdc.SetBrush(wxBrush(wxColour(pixel[0], pixel[1], pixel[2])));
+            capdc.DrawRectangle(x * magnification, y * magnification, magnification, magnification);
+        }
+    }
 #endif
 
-	Refresh(false);
+    Refresh(false);
 }
 
 class DialogColorPicker final : public wxDialog {
-	std::unique_ptr<PersistLocation> persist;
+    std::unique_ptr<PersistLocation> persist;
 
-	agi::Color cur_color; ///< Currently selected colour
+    agi::Color cur_color; ///< Currently selected colour
 
-	bool spectrum_dirty; ///< Does the spectrum image need to be regenerated?
-	ColorPickerSpectrum *spectrum; ///< The 2D color spectrum
-	ColorPickerSpectrum *slider; ///< The 1D slider for the color component not in the slider
-	ColorPickerSpectrum *alpha_slider;
+    bool spectrum_dirty; ///< Does the spectrum image need to be regenerated?
+    ColorPickerSpectrum *spectrum; ///< The 2D color spectrum
+    ColorPickerSpectrum *slider; ///< The 1D slider for the color component not in the slider
+    ColorPickerSpectrum *alpha_slider;
 
-	wxChoice *colorspace_choice; ///< The dropdown list to select colorspaces
+    wxChoice *colorspace_choice; ///< The dropdown list to select colorspaces
 
-	wxSpinCtrl *rgb_input[3];
-	wxBitmap rgb_spectrum[3]; ///< x/y spectrum bitmap where color "i" is excluded from
-	wxBitmap rgb_slider[3];   ///< z spectrum for color "i"
+    wxSpinCtrl *rgb_input[3];
+    wxBitmap rgb_spectrum[3]; ///< x/y spectrum bitmap where color "i" is excluded from
+    wxBitmap rgb_slider[3];   ///< z spectrum for color "i"
 
-	wxSpinCtrl *hsl_input[3];
-	wxBitmap hsl_spectrum; ///< h/s spectrum
-	wxBitmap hsl_slider;   ///< l spectrum
+    wxSpinCtrl *hsl_input[3];
+    wxBitmap hsl_spectrum; ///< h/s spectrum
+    wxBitmap hsl_slider;   ///< l spectrum
 
-	wxSpinCtrl *hsv_input[3];
-	wxBitmap hsv_spectrum; ///< s/v spectrum
-	wxBitmap hsv_slider;   ///< h spectrum
-	wxBitmap alpha_slider_img;
+    wxSpinCtrl *hsv_input[3];
+    wxBitmap hsv_spectrum; ///< s/v spectrum
+    wxBitmap hsv_slider;   ///< h spectrum
+    wxBitmap alpha_slider_img;
 
-	wxTextCtrl *ass_input;
-	wxTextCtrl *html_input;
-	wxSpinCtrl *alpha_input;
+    wxTextCtrl *ass_input;
+    wxTextCtrl *html_input;
+    wxSpinCtrl *alpha_input;
 
-	/// The eyedropper is set to a blank icon when it's clicked, so store its normal bitmap
-	wxBitmap eyedropper_bitmap;
+    /// The eyedropper is set to a blank icon when it's clicked, so store its normal bitmap
+    wxBitmap eyedropper_bitmap;
 
-	/// The point where the eyedropper was click, used to make it possible to either
-	/// click the eyedropper or drag the eyedropper
-	wxPoint eyedropper_grab_point;
+    /// The point where the eyedropper was click, used to make it possible to either
+    /// click the eyedropper or drag the eyedropper
+    wxPoint eyedropper_grab_point;
 
-	bool eyedropper_is_grabbed;
+    bool eyedropper_is_grabbed;
 
-	wxStaticBitmap *preview_box; ///< A box which simply shows the current color
-	ColorPickerRecent *recent_box; ///< A grid of recently used colors
+    wxStaticBitmap *preview_box; ///< A box which simply shows the current color
+    ColorPickerRecent *recent_box; ///< A grid of recently used colors
 
-	ColorPickerScreenDropper *screen_dropper;
+    ColorPickerScreenDropper *screen_dropper;
 
-	wxStaticBitmap *screen_dropper_icon;
+    wxStaticBitmap *screen_dropper_icon;
 
-	/// Update all other controls as a result of modifying an RGB control
-	void UpdateFromRGB(bool dirty = true);
-	/// Update all other controls as a result of modifying an HSL control
-	void UpdateFromHSL(bool dirty = true);
-	/// Update all other controls as a result of modifying an HSV control
-	void UpdateFromHSV(bool dirty = true);
-	/// Update all other controls as a result of modifying the ASS format control
-	void UpdateFromAss();
-	/// Update all other controls as a result of modifying the HTML format control
-	void UpdateFromHTML();
-	void UpdateFromAlpha();
+    /// Update all other controls as a result of modifying an RGB control
+    void UpdateFromRGB(bool dirty = true);
+    /// Update all other controls as a result of modifying an HSL control
+    void UpdateFromHSL(bool dirty = true);
+    /// Update all other controls as a result of modifying an HSV control
+    void UpdateFromHSV(bool dirty = true);
+    /// Update all other controls as a result of modifying the ASS format control
+    void UpdateFromAss();
+    /// Update all other controls as a result of modifying the HTML format control
+    void UpdateFromHTML();
+    void UpdateFromAlpha();
 
-	void SetRGB(agi::Color new_color);
-	void SetHSL(unsigned char r, unsigned char g, unsigned char b);
-	void SetHSV(unsigned char r, unsigned char g, unsigned char b);
+    void SetRGB(agi::Color new_color);
+    void SetHSL(unsigned char r, unsigned char g, unsigned char b);
+    void SetHSV(unsigned char r, unsigned char g, unsigned char b);
 
-	/// Redraw the spectrum display
-	void UpdateSpectrumDisplay();
+    /// Redraw the spectrum display
+    void UpdateSpectrumDisplay();
 
-	wxBitmap *MakeGBSpectrum();
-	wxBitmap *MakeRBSpectrum();
-	wxBitmap *MakeRGSpectrum();
-	wxBitmap *MakeHSSpectrum();
-	wxBitmap *MakeSVSpectrum();
+    wxBitmap *MakeGBSpectrum();
+    wxBitmap *MakeRBSpectrum();
+    wxBitmap *MakeRGSpectrum();
+    wxBitmap *MakeHSSpectrum();
+    wxBitmap *MakeSVSpectrum();
 
-	/// Constructor helper function for making the color input box sizers
-	template<int N, class Control>
-	wxSizer *MakeColorInputSizer(wxString (&labels)[N], Control *(&inputs)[N]);
+    /// Constructor helper function for making the color input box sizers
+    template<int N, class Control>
+    wxSizer *MakeColorInputSizer(wxString (&labels)[N], Control * (&inputs)[N]);
 
-	void OnChangeMode(wxCommandEvent &evt);
-	void OnSpectrumChange(wxCommandEvent &evt);
-	void OnSliderChange(wxCommandEvent &evt);
-	void OnAlphaSliderChange(wxCommandEvent &evt);
-	void OnRecentSelect(ValueEvent<agi::Color>& evt); // also handles dropper pick
-	void OnDropperMouse(wxMouseEvent &evt);
-	void OnMouse(wxMouseEvent &evt);
-	void OnCaptureLost(wxMouseCaptureLostEvent&);
+    void OnChangeMode(wxCommandEvent &evt);
+    void OnSpectrumChange(wxCommandEvent &evt);
+    void OnSliderChange(wxCommandEvent &evt);
+    void OnAlphaSliderChange(wxCommandEvent &evt);
+    void OnRecentSelect(ValueEvent<agi::Color> &evt); // also handles dropper pick
+    void OnDropperMouse(wxMouseEvent &evt);
+    void OnMouse(wxMouseEvent &evt);
+    void OnCaptureLost(wxMouseCaptureLostEvent &);
 
-	std::function<void (agi::Color)> callback;
+    std::function<void (agi::Color)> callback;
 
 public:
-	DialogColorPicker(wxWindow *parent, agi::Color initial_color, std::function<void (agi::Color)> callback, bool alpha);
-	~DialogColorPicker();
+    DialogColorPicker(wxWindow *parent, agi::Color initial_color, std::function<void (agi::Color)> callback, bool alpha);
+    ~DialogColorPicker();
 
-	void SetColor(agi::Color new_color);
-	void AddColorToRecent();
+    void SetColor(agi::Color new_color);
+    void AddColorToRecent();
 };
 
 static const int slider_width = 10; ///< width in pixels of the color slider control
 static const int alpha_box_size = 5;
 
 template<typename Func>
-static wxBitmap make_slider_img(Func func) {
-	unsigned char *slid = (unsigned char *)calloc(slider_width * 256 * 3, 1);
-	func(slid);
-	wxImage img(slider_width, 256, slid);
-	return wxBitmap(img);
+static wxBitmap make_slider_img(Func func)
+{
+    unsigned char *slid = (unsigned char *)calloc(slider_width * 256 * 3, 1);
+    func(slid);
+    wxImage img(slider_width, 256, slid);
+    return wxBitmap(img);
 }
 
 template<typename Func>
-static wxBitmap make_slider(Func func) {
-	return make_slider_img([&](unsigned char *slid) {
-		for (int y = 0; y < 256; ++y) {
-			unsigned char rgb[3];
-			func(y, rgb);
-			for (int x = 0; x < slider_width; ++x)
-				memcpy(slid + y * slider_width * 3 + x * 3, rgb, 3);
-		}
-	});
+static wxBitmap make_slider(Func func)
+{
+    return make_slider_img([&](unsigned char *slid) {
+        for (int y = 0; y < 256; ++y) {
+            unsigned char rgb[3];
+            func(y, rgb);
+            for (int x = 0; x < slider_width; ++x)
+                memcpy(slid + y * slider_width * 3 + x * 3, rgb, 3);
+        }
+    });
 }
 
 DialogColorPicker::DialogColorPicker(wxWindow *parent, agi::Color initial_color, std::function<void (agi::Color)> callback, bool alpha)
-: wxDialog(parent, -1, _("Select Color"))
-, callback(std::move(callback))
+    : wxDialog(parent, -1, _("Select Color"))
+    , callback(std::move(callback))
 {
-	// generate spectrum slider bar images
-	for (int i = 0; i < 3; ++i) {
-		rgb_slider[i] = make_slider([=](int y, unsigned char *rgb) {
-			memset(rgb, 0, 3);
-			rgb[i] = y;
-		});
-	}
-	hsl_slider = make_slider([](int y, unsigned char *rgb) { memset(rgb, y, 3); });
-	hsv_slider = make_slider([](int y, unsigned char *rgb) { hsv_to_rgb(y, 255, 255, rgb, rgb + 1, rgb + 2); });
-
-	// Create the controls for the dialog
-	wxSizer *spectrum_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Color spectrum"));
-	spectrum = new ColorPickerSpectrum(this, PickerDirection::HorzVert, wxSize(256, 256));
-	slider = new ColorPickerSpectrum(this, PickerDirection::Vert, wxSize(slider_width, 256));
-	alpha_slider = new ColorPickerSpectrum(this, PickerDirection::Vert, wxSize(slider_width, 256));
-	wxString modes[] = { _("RGB/R"), _("RGB/G"), _("RGB/B"), _("HSL/L"), _("HSV/H") };
-	colorspace_choice = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize, 5, modes);
-
-	wxSize colorinput_size = GetTextExtent(" &H10117B& ");
-	colorinput_size.SetHeight(-1);
-	wxSize colorinput_labelsize(40, -1);
-
-	wxSizer *rgb_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("RGB color"));
-	wxSizer *hsl_box = new wxStaticBoxSizer(wxVERTICAL, this, _("HSL color"));
-	wxSizer *hsv_box = new wxStaticBoxSizer(wxVERTICAL, this, _("HSV color"));
-
-	for (auto& elem : rgb_input)
-		elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
-
-	ass_input = new wxTextCtrl(this, -1, "", wxDefaultPosition, colorinput_size);
-	html_input = new wxTextCtrl(this, -1, "", wxDefaultPosition, colorinput_size);
-	alpha_input = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
-
-	for (auto& elem : hsl_input)
-		elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
-
-	for (auto& elem : hsv_input)
-		elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
-
-	preview_box = new wxStaticBitmap(this, -1, wxBitmap(40, 40, 24), wxDefaultPosition, wxSize(40, 40), STATIC_BORDER_FLAG);
-	recent_box = new ColorPickerRecent(this, 8, 4, 16);
-
-	eyedropper_bitmap = GETIMAGE(eyedropper_tool_24);
-	eyedropper_bitmap.SetMask(new wxMask(eyedropper_bitmap, wxColour(255, 0, 255)));
-	screen_dropper_icon = new wxStaticBitmap(this, -1, eyedropper_bitmap, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER);
-	screen_dropper = new ColorPickerScreenDropper(this, 7, 7, 8);
-
-	// Arrange the controls in a nice way
-	wxSizer *spectop_sizer = new wxBoxSizer(wxHORIZONTAL);
-	spectop_sizer->Add(new wxStaticText(this, -1, _("Spectrum mode:")), 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT|wxRIGHT, 5);
-	spectop_sizer->Add(colorspace_choice, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_LEFT);
-	spectop_sizer->Add(5, 5, 1, wxEXPAND);
-	spectop_sizer->Add(preview_box, 0, wxALIGN_CENTER_VERTICAL);
-
-	wxSizer *spectrum_sizer = new wxFlexGridSizer(3, 5, 5);
-	spectrum_sizer->Add(spectop_sizer, wxEXPAND);
-	spectrum_sizer->AddStretchSpacer(1);
-	spectrum_sizer->AddStretchSpacer(1);
-	spectrum_sizer->Add(spectrum);
-	spectrum_sizer->Add(slider);
-	spectrum_sizer->Add(alpha_slider);
-	if (!alpha)
-		spectrum_sizer->Hide(alpha_slider);
-
-	spectrum_box->Add(spectrum_sizer, 0, wxALL, 3);
-
-	wxString rgb_labels[] = { _("Red:"), _("Green:"), _("Blue:") };
-	rgb_box->Add(MakeColorInputSizer(rgb_labels, rgb_input), 1, wxALL|wxEXPAND, 3);
-
-	wxString ass_labels[] = { "ASS:", "HTML:", _("Alpha:") };
-	wxControl *ass_ctrls[] = { ass_input, html_input, alpha_input };
-	auto ass_colors_sizer = MakeColorInputSizer(ass_labels, ass_ctrls);
-	if (!alpha)
-		ass_colors_sizer->Hide(alpha_input);
-	rgb_box->Add(ass_colors_sizer, 0, wxALL|wxCENTER|wxEXPAND, 3);
-
-	wxString hsl_labels[] = { _("Hue:"), _("Sat.:"), _("Lum.:") };
-	hsl_box->Add(MakeColorInputSizer(hsl_labels, hsl_input), 0, wxALL|wxEXPAND, 3);
-
-	wxString hsv_labels[] = { _("Hue:"), _("Sat.:"), _("Value:") };
-	hsv_box->Add(MakeColorInputSizer(hsv_labels, hsv_input), 0, wxALL|wxEXPAND, 3);
-
-	wxSizer *hsx_sizer = new wxBoxSizer(wxHORIZONTAL);
-	hsx_sizer->Add(hsl_box);
-	hsx_sizer->AddSpacer(5);
-	hsx_sizer->Add(hsv_box);
-
-	wxSizer *picker_sizer = new wxBoxSizer(wxHORIZONTAL);
-	picker_sizer->AddStretchSpacer();
-	picker_sizer->Add(screen_dropper_icon, 0, wxALIGN_CENTER|wxRIGHT, 5);
-	picker_sizer->Add(screen_dropper, 0, wxALIGN_CENTER);
-	picker_sizer->AddStretchSpacer();
-	picker_sizer->Add(recent_box, 0, wxALIGN_CENTER);
-	picker_sizer->AddStretchSpacer();
-
-	wxStdDialogButtonSizer *button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-
-	wxSizer *input_sizer = new wxBoxSizer(wxVERTICAL);
-	input_sizer->Add(rgb_box, 0, wxEXPAND);
-	input_sizer->AddSpacer(5);
-	input_sizer->Add(hsx_sizer, 0, wxEXPAND);
-	input_sizer->AddStretchSpacer(1);
-	input_sizer->Add(picker_sizer, 0, wxEXPAND);
-	input_sizer->AddStretchSpacer(2);
-	input_sizer->Add(button_sizer, 0, wxALIGN_RIGHT);
-
-	wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
-	main_sizer->Add(spectrum_box, 1, wxALL | wxEXPAND, 5);
-	main_sizer->Add(input_sizer, 0, (wxALL&~wxLEFT)|wxEXPAND, 5);
-
-	SetSizerAndFit(main_sizer);
-
-	persist = agi::make_unique<PersistLocation>(this, "Tool/Colour Picker");
-
-	// Fill the controls
-	int mode = OPT_GET("Tool/Colour Picker/Mode")->GetInt();
-	if (mode < 0 || mode > 4) mode = 3; // HSL default
-	colorspace_choice->SetSelection(mode);
-	SetColor(initial_color);
-	recent_box->Load(OPT_GET("Tool/Colour Picker/Recent Colours")->GetListColor());
-
-	using std::bind;
-	for (int i = 0; i < 3; ++i) {
-		rgb_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromRGB, this, true));
-		rgb_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromRGB, this, true));
-		hsl_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromHSL, this, true));
-		hsl_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHSL, this, true));
-		hsv_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromHSV, this, true));
-		hsv_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHSV, this, true));
-	}
-	ass_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromAss, this));
-	html_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHTML, this));
-	alpha_input->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromAlpha, this));
-	alpha_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromAlpha, this));
-
-	screen_dropper_icon->Bind(wxEVT_MOTION, &DialogColorPicker::OnDropperMouse, this);
-	screen_dropper_icon->Bind(wxEVT_LEFT_DOWN, &DialogColorPicker::OnDropperMouse, this);
-	screen_dropper_icon->Bind(wxEVT_LEFT_UP, &DialogColorPicker::OnDropperMouse, this);
-	screen_dropper_icon->Bind(wxEVT_MOUSE_CAPTURE_LOST, &DialogColorPicker::OnCaptureLost, this);
-	Bind(wxEVT_MOTION, &DialogColorPicker::OnMouse, this);
-	Bind(wxEVT_LEFT_DOWN, &DialogColorPicker::OnMouse, this);
-	Bind(wxEVT_LEFT_UP, &DialogColorPicker::OnMouse, this);
-
-	spectrum->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnSpectrumChange, this);
-	slider->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnSliderChange, this);
-	alpha_slider->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnAlphaSliderChange, this);
-	recent_box->Bind(EVT_RECENT_SELECT, &DialogColorPicker::OnRecentSelect, this);
-	screen_dropper->Bind(EVT_DROPPER_SELECT, &DialogColorPicker::OnRecentSelect, this);
-
-	colorspace_choice->Bind(wxEVT_CHOICE, &DialogColorPicker::OnChangeMode, this);
-
-	button_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Colour Picker"));
+    // generate spectrum slider bar images
+    for (int i = 0; i < 3; ++i) {
+        rgb_slider[i] = make_slider([ = ](int y, unsigned char *rgb) {
+            memset(rgb, 0, 3);
+            rgb[i] = y;
+        });
+    }
+    hsl_slider = make_slider([](int y, unsigned char *rgb) { memset(rgb, y, 3); });
+    hsv_slider = make_slider([](int y, unsigned char *rgb) { hsv_to_rgb(y, 255, 255, rgb, rgb + 1, rgb + 2); });
+
+    // Create the controls for the dialog
+    wxSizer *spectrum_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Color spectrum"));
+    spectrum = new ColorPickerSpectrum(this, PickerDirection::HorzVert, wxSize(256, 256));
+    slider = new ColorPickerSpectrum(this, PickerDirection::Vert, wxSize(slider_width, 256));
+    alpha_slider = new ColorPickerSpectrum(this, PickerDirection::Vert, wxSize(slider_width, 256));
+    wxString modes[] = { _("RGB/R"), _("RGB/G"), _("RGB/B"), _("HSL/L"), _("HSV/H") };
+    colorspace_choice = new wxChoice(this, -1, wxDefaultPosition, wxDefaultSize, 5, modes);
+
+    wxSize colorinput_size = GetTextExtent(" &H10117B& ");
+    colorinput_size.SetHeight(-1);
+    wxSize colorinput_labelsize(40, -1);
+
+    wxSizer *rgb_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("RGB color"));
+    wxSizer *hsl_box = new wxStaticBoxSizer(wxVERTICAL, this, _("HSL color"));
+    wxSizer *hsv_box = new wxStaticBoxSizer(wxVERTICAL, this, _("HSV color"));
+
+    for (auto &elem : rgb_input)
+        elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
+
+    ass_input = new wxTextCtrl(this, -1, "", wxDefaultPosition, colorinput_size);
+    html_input = new wxTextCtrl(this, -1, "", wxDefaultPosition, colorinput_size);
+    alpha_input = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
+
+    for (auto &elem : hsl_input)
+        elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
+
+    for (auto &elem : hsv_input)
+        elem = new wxSpinCtrl(this, -1, "", wxDefaultPosition, colorinput_size, wxSP_ARROW_KEYS, 0, 255);
+
+    preview_box = new wxStaticBitmap(this, -1, wxBitmap(40, 40, 24), wxDefaultPosition, wxSize(40, 40), STATIC_BORDER_FLAG);
+    recent_box = new ColorPickerRecent(this, 8, 4, 16);
+
+    eyedropper_bitmap = GETIMAGE(eyedropper_tool_24);
+    eyedropper_bitmap.SetMask(new wxMask(eyedropper_bitmap, wxColour(255, 0, 255)));
+    screen_dropper_icon = new wxStaticBitmap(this, -1, eyedropper_bitmap, wxDefaultPosition, wxDefaultSize, wxRAISED_BORDER);
+    screen_dropper = new ColorPickerScreenDropper(this, 7, 7, 8);
+
+    // Arrange the controls in a nice way
+    wxSizer *spectop_sizer = new wxBoxSizer(wxHORIZONTAL);
+    spectop_sizer->Add(new wxStaticText(this, -1, _("Spectrum mode:")), 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxRIGHT, 5);
+    spectop_sizer->Add(colorspace_choice, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
+    spectop_sizer->Add(5, 5, 1, wxEXPAND);
+    spectop_sizer->Add(preview_box, 0, wxALIGN_CENTER_VERTICAL);
+
+    wxSizer *spectrum_sizer = new wxFlexGridSizer(3, 5, 5);
+    spectrum_sizer->Add(spectop_sizer, wxEXPAND);
+    spectrum_sizer->AddStretchSpacer(1);
+    spectrum_sizer->AddStretchSpacer(1);
+    spectrum_sizer->Add(spectrum);
+    spectrum_sizer->Add(slider);
+    spectrum_sizer->Add(alpha_slider);
+    if (!alpha)
+        spectrum_sizer->Hide(alpha_slider);
+
+    spectrum_box->Add(spectrum_sizer, 0, wxALL, 3);
+
+    wxString rgb_labels[] = { _("Red:"), _("Green:"), _("Blue:") };
+    rgb_box->Add(MakeColorInputSizer(rgb_labels, rgb_input), 1, wxALL | wxEXPAND, 3);
+
+    wxString ass_labels[] = { "ASS:", "HTML:", _("Alpha:") };
+    wxControl *ass_ctrls[] = { ass_input, html_input, alpha_input };
+    auto ass_colors_sizer = MakeColorInputSizer(ass_labels, ass_ctrls);
+    if (!alpha)
+        ass_colors_sizer->Hide(alpha_input);
+    rgb_box->Add(ass_colors_sizer, 0, wxALL | wxCENTER | wxEXPAND, 3);
+
+    wxString hsl_labels[] = { _("Hue:"), _("Sat.:"), _("Lum.:") };
+    hsl_box->Add(MakeColorInputSizer(hsl_labels, hsl_input), 0, wxALL | wxEXPAND, 3);
+
+    wxString hsv_labels[] = { _("Hue:"), _("Sat.:"), _("Value:") };
+    hsv_box->Add(MakeColorInputSizer(hsv_labels, hsv_input), 0, wxALL | wxEXPAND, 3);
+
+    wxSizer *hsx_sizer = new wxBoxSizer(wxHORIZONTAL);
+    hsx_sizer->Add(hsl_box);
+    hsx_sizer->AddSpacer(5);
+    hsx_sizer->Add(hsv_box);
+
+    wxSizer *picker_sizer = new wxBoxSizer(wxHORIZONTAL);
+    picker_sizer->AddStretchSpacer();
+    picker_sizer->Add(screen_dropper_icon, 0, wxALIGN_CENTER | wxRIGHT, 5);
+    picker_sizer->Add(screen_dropper, 0, wxALIGN_CENTER);
+    picker_sizer->AddStretchSpacer();
+    picker_sizer->Add(recent_box, 0, wxALIGN_CENTER);
+    picker_sizer->AddStretchSpacer();
+
+    wxStdDialogButtonSizer *button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+
+    wxSizer *input_sizer = new wxBoxSizer(wxVERTICAL);
+    input_sizer->Add(rgb_box, 0, wxEXPAND);
+    input_sizer->AddSpacer(5);
+    input_sizer->Add(hsx_sizer, 0, wxEXPAND);
+    input_sizer->AddStretchSpacer(1);
+    input_sizer->Add(picker_sizer, 0, wxEXPAND);
+    input_sizer->AddStretchSpacer(2);
+    input_sizer->Add(button_sizer, 0, wxALIGN_RIGHT);
+
+    wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
+    main_sizer->Add(spectrum_box, 1, wxALL | wxEXPAND, 5);
+    main_sizer->Add(input_sizer, 0, (wxALL & ~wxLEFT) | wxEXPAND, 5);
+
+    SetSizerAndFit(main_sizer);
+
+    persist = agi::make_unique<PersistLocation>(this, "Tool/Colour Picker");
+
+    // Fill the controls
+    int mode = OPT_GET("Tool/Colour Picker/Mode")->GetInt();
+    if (mode < 0 || mode > 4) mode = 3; // HSL default
+    colorspace_choice->SetSelection(mode);
+    SetColor(initial_color);
+    recent_box->Load(OPT_GET("Tool/Colour Picker/Recent Colours")->GetListColor());
+
+    using std::bind;
+    for (int i = 0; i < 3; ++i) {
+        rgb_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromRGB, this, true));
+        rgb_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromRGB, this, true));
+        hsl_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromHSL, this, true));
+        hsl_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHSL, this, true));
+        hsv_input[i]->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromHSV, this, true));
+        hsv_input[i]->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHSV, this, true));
+    }
+    ass_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromAss, this));
+    html_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromHTML, this));
+    alpha_input->Bind(wxEVT_SPINCTRL, bind(&DialogColorPicker::UpdateFromAlpha, this));
+    alpha_input->Bind(wxEVT_TEXT, bind(&DialogColorPicker::UpdateFromAlpha, this));
+
+    screen_dropper_icon->Bind(wxEVT_MOTION, &DialogColorPicker::OnDropperMouse, this);
+    screen_dropper_icon->Bind(wxEVT_LEFT_DOWN, &DialogColorPicker::OnDropperMouse, this);
+    screen_dropper_icon->Bind(wxEVT_LEFT_UP, &DialogColorPicker::OnDropperMouse, this);
+    screen_dropper_icon->Bind(wxEVT_MOUSE_CAPTURE_LOST, &DialogColorPicker::OnCaptureLost, this);
+    Bind(wxEVT_MOTION, &DialogColorPicker::OnMouse, this);
+    Bind(wxEVT_LEFT_DOWN, &DialogColorPicker::OnMouse, this);
+    Bind(wxEVT_LEFT_UP, &DialogColorPicker::OnMouse, this);
+
+    spectrum->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnSpectrumChange, this);
+    slider->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnSliderChange, this);
+    alpha_slider->Bind(EVT_SPECTRUM_CHANGE, &DialogColorPicker::OnAlphaSliderChange, this);
+    recent_box->Bind(EVT_RECENT_SELECT, &DialogColorPicker::OnRecentSelect, this);
+    screen_dropper->Bind(EVT_DROPPER_SELECT, &DialogColorPicker::OnRecentSelect, this);
+
+    colorspace_choice->Bind(wxEVT_CHOICE, &DialogColorPicker::OnChangeMode, this);
+
+    button_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Colour Picker"));
 }
 
 template<int N, class Control>
-wxSizer *DialogColorPicker::MakeColorInputSizer(wxString (&labels)[N], Control *(&inputs)[N]) {
-	auto sizer = new wxFlexGridSizer(2, 5, 5);
-	for (int i = 0; i < N; ++i) {
-		sizer->Add(new wxStaticText(this, -1, labels[i]), wxSizerFlags(1).Center().Left());
-		sizer->Add(inputs[i]);
-	}
-	sizer->AddGrowableCol(0,1);
-	return sizer;
+wxSizer *DialogColorPicker::MakeColorInputSizer(wxString (&labels)[N], Control * (&inputs)[N])
+{
+    auto sizer = new wxFlexGridSizer(2, 5, 5);
+    for (int i = 0; i < N; ++i) {
+        sizer->Add(new wxStaticText(this, -1, labels[i]), wxSizerFlags(1).Center().Left());
+        sizer->Add(inputs[i]);
+    }
+    sizer->AddGrowableCol(0, 1);
+    return sizer;
 }
 
-DialogColorPicker::~DialogColorPicker() {
-	if (screen_dropper_icon->HasCapture()) screen_dropper_icon->ReleaseMouse();
+DialogColorPicker::~DialogColorPicker()
+{
+    if (screen_dropper_icon->HasCapture()) screen_dropper_icon->ReleaseMouse();
 }
 
-static void change_value(wxSpinCtrl *ctrl, int value) {
-	wxEventBlocker blocker(ctrl);
-	ctrl->SetValue(value);
+static void change_value(wxSpinCtrl *ctrl, int value)
+{
+    wxEventBlocker blocker(ctrl);
+    ctrl->SetValue(value);
 }
 
-void DialogColorPicker::SetColor(agi::Color new_color) {
-	change_value(alpha_input, new_color.a);
-	alpha_slider->SetXY(0, new_color.a);
-	cur_color.a = new_color.a;
+void DialogColorPicker::SetColor(agi::Color new_color)
+{
+    change_value(alpha_input, new_color.a);
+    alpha_slider->SetXY(0, new_color.a);
+    cur_color.a = new_color.a;
 
-	SetRGB(new_color);
-	spectrum_dirty = true;
-	UpdateFromRGB();
+    SetRGB(new_color);
+    spectrum_dirty = true;
+    UpdateFromRGB();
 }
 
-void DialogColorPicker::AddColorToRecent() {
-	recent_box->AddColor(cur_color);
-	OPT_SET("Tool/Colour Picker/Recent Colours")->SetListColor(recent_box->Save());
+void DialogColorPicker::AddColorToRecent()
+{
+    recent_box->AddColor(cur_color);
+    OPT_SET("Tool/Colour Picker/Recent Colours")->SetListColor(recent_box->Save());
 }
 
-void DialogColorPicker::SetRGB(agi::Color new_color) {
-	change_value(rgb_input[0], new_color.r);
-	change_value(rgb_input[1], new_color.g);
-	change_value(rgb_input[2], new_color.b);
-	new_color.a = cur_color.a;
-	cur_color = new_color;
+void DialogColorPicker::SetRGB(agi::Color new_color)
+{
+    change_value(rgb_input[0], new_color.r);
+    change_value(rgb_input[1], new_color.g);
+    change_value(rgb_input[2], new_color.b);
+    new_color.a = cur_color.a;
+    cur_color = new_color;
 }
 
-void DialogColorPicker::SetHSL(unsigned char r, unsigned char g, unsigned char b) {
-	unsigned char h, s, l;
-	rgb_to_hsl(r, g, b, &h, &s, &l);
-	change_value(hsl_input[0], h);
-	change_value(hsl_input[1], s);
-	change_value(hsl_input[2], l);
+void DialogColorPicker::SetHSL(unsigned char r, unsigned char g, unsigned char b)
+{
+    unsigned char h, s, l;
+    rgb_to_hsl(r, g, b, &h, &s, &l);
+    change_value(hsl_input[0], h);
+    change_value(hsl_input[1], s);
+    change_value(hsl_input[2], l);
 }
 
-void DialogColorPicker::SetHSV(unsigned char r, unsigned char g, unsigned char b) {
-	unsigned char h, s, v;
-	rgb_to_hsv(r, g, b, &h, &s, &v);
-	change_value(hsv_input[0], h);
-	change_value(hsv_input[1], s);
-	change_value(hsv_input[2], v);
+void DialogColorPicker::SetHSV(unsigned char r, unsigned char g, unsigned char b)
+{
+    unsigned char h, s, v;
+    rgb_to_hsv(r, g, b, &h, &s, &v);
+    change_value(hsv_input[0], h);
+    change_value(hsv_input[1], s);
+    change_value(hsv_input[2], v);
 }
 
-void DialogColorPicker::UpdateFromRGB(bool dirty) {
-	unsigned char r = rgb_input[0]->GetValue();
-	unsigned char g = rgb_input[1]->GetValue();
-	unsigned char b = rgb_input[2]->GetValue();
-	SetHSL(r, g, b);
-	SetHSV(r, g, b);
-	cur_color = agi::Color(r, g, b, cur_color.a);
-	ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
-	html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
-
-	if (dirty)
-		spectrum_dirty = true;
-	UpdateSpectrumDisplay();
+void DialogColorPicker::UpdateFromRGB(bool dirty)
+{
+    unsigned char r = rgb_input[0]->GetValue();
+    unsigned char g = rgb_input[1]->GetValue();
+    unsigned char b = rgb_input[2]->GetValue();
+    SetHSL(r, g, b);
+    SetHSV(r, g, b);
+    cur_color = agi::Color(r, g, b, cur_color.a);
+    ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
+    html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
+
+    if (dirty)
+        spectrum_dirty = true;
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::UpdateFromHSL(bool dirty) {
-	unsigned char r, g, b, h, s, l;
-	h = hsl_input[0]->GetValue();
-	s = hsl_input[1]->GetValue();
-	l = hsl_input[2]->GetValue();
-	hsl_to_rgb(h, s, l, &r, &g, &b);
-	SetRGB(agi::Color(r, g, b));
-	SetHSV(r, g, b);
-
-	ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
-	html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
-
-	if (dirty)
-		spectrum_dirty = true;
-	UpdateSpectrumDisplay();
+void DialogColorPicker::UpdateFromHSL(bool dirty)
+{
+    unsigned char r, g, b, h, s, l;
+    h = hsl_input[0]->GetValue();
+    s = hsl_input[1]->GetValue();
+    l = hsl_input[2]->GetValue();
+    hsl_to_rgb(h, s, l, &r, &g, &b);
+    SetRGB(agi::Color(r, g, b));
+    SetHSV(r, g, b);
+
+    ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
+    html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
+
+    if (dirty)
+        spectrum_dirty = true;
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::UpdateFromHSV(bool dirty) {
-	unsigned char r, g, b, h, s, v;
-	h = hsv_input[0]->GetValue();
-	s = hsv_input[1]->GetValue();
-	v = hsv_input[2]->GetValue();
-	hsv_to_rgb(h, s, v, &r, &g, &b);
-	SetRGB(agi::Color(r, g, b));
-	SetHSL(r, g, b);
-	ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
-	html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
-
-	if (dirty)
-		spectrum_dirty = true;
-	UpdateSpectrumDisplay();
+void DialogColorPicker::UpdateFromHSV(bool dirty)
+{
+    unsigned char r, g, b, h, s, v;
+    h = hsv_input[0]->GetValue();
+    s = hsv_input[1]->GetValue();
+    v = hsv_input[2]->GetValue();
+    hsv_to_rgb(h, s, v, &r, &g, &b);
+    SetRGB(agi::Color(r, g, b));
+    SetHSL(r, g, b);
+    ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
+    html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
+
+    if (dirty)
+        spectrum_dirty = true;
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::UpdateFromAss() {
-	agi::Color color(from_wx(ass_input->GetValue()));
-	SetRGB(color);
-	SetHSL(color.r, color.g, color.b);
-	SetHSV(color.r, color.g, color.b);
-	html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
-
-	spectrum_dirty = true;
-	UpdateSpectrumDisplay();
+void DialogColorPicker::UpdateFromAss()
+{
+    agi::Color color(from_wx(ass_input->GetValue()));
+    SetRGB(color);
+    SetHSL(color.r, color.g, color.b);
+    SetHSV(color.r, color.g, color.b);
+    html_input->ChangeValue(to_wx(cur_color.GetHexFormatted()));
+
+    spectrum_dirty = true;
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::UpdateFromHTML() {
-	agi::Color color(from_wx(html_input->GetValue()));
-	SetRGB(color);
-	SetHSL(color.r, color.g, color.b);
-	SetHSV(color.r, color.g, color.b);
-	ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
-
-	spectrum_dirty = true;
-	UpdateSpectrumDisplay();
+void DialogColorPicker::UpdateFromHTML()
+{
+    agi::Color color(from_wx(html_input->GetValue()));
+    SetRGB(color);
+    SetHSL(color.r, color.g, color.b);
+    SetHSV(color.r, color.g, color.b);
+    ass_input->ChangeValue(to_wx(cur_color.GetAssOverrideFormatted()));
+
+    spectrum_dirty = true;
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::UpdateFromAlpha() {
-	cur_color.a = alpha_input->GetValue();
-	alpha_slider->SetXY(0, cur_color.a);
-	callback(cur_color);
+void DialogColorPicker::UpdateFromAlpha()
+{
+    cur_color.a = alpha_input->GetValue();
+    alpha_slider->SetXY(0, cur_color.a);
+    callback(cur_color);
 }
 
-void DialogColorPicker::UpdateSpectrumDisplay() {
-	int i = colorspace_choice->GetSelection();
-	if (spectrum_dirty) {
-		switch (i) {
-			case 0: spectrum->SetBackground(MakeGBSpectrum(), true); break;
-			case 1: spectrum->SetBackground(MakeRBSpectrum(), true); break;
-			case 2: spectrum->SetBackground(MakeRGSpectrum(), true); break;
-			case 3: spectrum->SetBackground(MakeHSSpectrum(), true); break;
-			case 4: spectrum->SetBackground(MakeSVSpectrum(), true); break;
-		}
-	}
-
-	switch (i) {
-		case 0: case 1: case 2:
-			slider->SetBackground(&rgb_slider[i]);
-			slider->SetXY(0, rgb_input[i]->GetValue());
-			spectrum->SetXY(rgb_input[2 - (i == 2)]->GetValue(), rgb_input[i == 0]->GetValue());
-			break;
-		case 3:
-			slider->SetBackground(&hsl_slider);
-			slider->SetXY(0, hsl_input[2]->GetValue());
-			spectrum->SetXY(hsl_input[1]->GetValue(), hsl_input[0]->GetValue());
-			break;
-		case 4:
-			slider->SetBackground(&hsv_slider);
-			slider->SetXY(0, hsv_input[0]->GetValue());
-			spectrum->SetXY(hsv_input[1]->GetValue(), hsv_input[2]->GetValue());
-			break;
-	}
-	spectrum_dirty = false;
-
-	wxBitmap tempBmp = preview_box->GetBitmap();
-	{
-		wxMemoryDC previewdc;
-		previewdc.SelectObject(tempBmp);
-		previewdc.SetPen(*wxTRANSPARENT_PEN);
-		previewdc.SetBrush(wxBrush(to_wx(cur_color)));
-		previewdc.DrawRectangle(0, 0, 40, 40);
-	}
-	preview_box->SetBitmap(tempBmp);
-
-	alpha_slider_img = make_slider_img([=](unsigned char *slid) {
-		static_assert(slider_width % alpha_box_size == 0, "Slider width must be a multiple of alpha box width");
-
-		for (int y = 0; y < 256; ++y) {
-			unsigned char inv_y = 0xFF - y;
-
-			unsigned char box_colors[] = {
-				static_cast<unsigned char>(0x66 - inv_y * 0x66 / 0xFF),
-				static_cast<unsigned char>(0x99 - inv_y * 0x99 / 0xFF)
-			};
-
-			if ((y / alpha_box_size) & 1)
-				std::swap(box_colors[0], box_colors[1]);
-
-			unsigned char colors[2][3];
-			for (int i = 0; i < 2; ++i) {
-				colors[i][0] = cur_color.r * inv_y / 0xFF + box_colors[i];
-				colors[i][1] = cur_color.g * inv_y / 0xFF + box_colors[i];
-				colors[i][2] = cur_color.b * inv_y / 0xFF + box_colors[i];
-			}
-
-			for (int x = 0; x < slider_width; ++x) {
-				*slid++ = colors[x / alpha_box_size][0];
-				*slid++ = colors[x / alpha_box_size][1];
-				*slid++ = colors[x / alpha_box_size][2];
-			}
-		}
-	});
-	alpha_slider->SetBackground(&alpha_slider_img, true);
-
-	callback(cur_color);
+void DialogColorPicker::UpdateSpectrumDisplay()
+{
+    int i = colorspace_choice->GetSelection();
+    if (spectrum_dirty) {
+        switch (i) {
+        case 0: spectrum->SetBackground(MakeGBSpectrum(), true); break;
+        case 1: spectrum->SetBackground(MakeRBSpectrum(), true); break;
+        case 2: spectrum->SetBackground(MakeRGSpectrum(), true); break;
+        case 3: spectrum->SetBackground(MakeHSSpectrum(), true); break;
+        case 4: spectrum->SetBackground(MakeSVSpectrum(), true); break;
+        }
+    }
+
+    switch (i) {
+    case 0: case 1: case 2:
+        slider->SetBackground(&rgb_slider[i]);
+        slider->SetXY(0, rgb_input[i]->GetValue());
+        spectrum->SetXY(rgb_input[2 - (i == 2)]->GetValue(), rgb_input[i == 0]->GetValue());
+        break;
+    case 3:
+        slider->SetBackground(&hsl_slider);
+        slider->SetXY(0, hsl_input[2]->GetValue());
+        spectrum->SetXY(hsl_input[1]->GetValue(), hsl_input[0]->GetValue());
+        break;
+    case 4:
+        slider->SetBackground(&hsv_slider);
+        slider->SetXY(0, hsv_input[0]->GetValue());
+        spectrum->SetXY(hsv_input[1]->GetValue(), hsv_input[2]->GetValue());
+        break;
+    }
+    spectrum_dirty = false;
+
+    wxBitmap tempBmp = preview_box->GetBitmap();
+    {
+        wxMemoryDC previewdc;
+        previewdc.SelectObject(tempBmp);
+        previewdc.SetPen(*wxTRANSPARENT_PEN);
+        previewdc.SetBrush(wxBrush(to_wx(cur_color)));
+        previewdc.DrawRectangle(0, 0, 40, 40);
+    }
+    preview_box->SetBitmap(tempBmp);
+
+    alpha_slider_img = make_slider_img([ = ](unsigned char *slid) {
+        static_assert(slider_width % alpha_box_size == 0, "Slider width must be a multiple of alpha box width");
+
+        for (int y = 0; y < 256; ++y) {
+            unsigned char inv_y = 0xFF - y;
+
+            unsigned char box_colors[] = {
+                static_cast<unsigned char>(0x66 - inv_y * 0x66 / 0xFF),
+                static_cast<unsigned char>(0x99 - inv_y * 0x99 / 0xFF)
+            };
+
+            if ((y / alpha_box_size) & 1)
+                std::swap(box_colors[0], box_colors[1]);
+
+            unsigned char colors[2][3];
+            for (int i = 0; i < 2; ++i) {
+                colors[i][0] = cur_color.r * inv_y / 0xFF + box_colors[i];
+                colors[i][1] = cur_color.g * inv_y / 0xFF + box_colors[i];
+                colors[i][2] = cur_color.b * inv_y / 0xFF + box_colors[i];
+            }
+
+            for (int x = 0; x < slider_width; ++x) {
+                *slid++ = colors[x / alpha_box_size][0];
+                *slid++ = colors[x / alpha_box_size][1];
+                *slid++ = colors[x / alpha_box_size][2];
+            }
+        }
+    });
+    alpha_slider->SetBackground(&alpha_slider_img, true);
+
+    callback(cur_color);
 }
 
 template<typename Func>
-static wxBitmap *make_spectrum(wxBitmap *bitmap, Func func) {
-	wxImage spectrum_image(256, 256);
-	func(spectrum_image.GetData());
-	*bitmap = wxBitmap(spectrum_image);
-	return bitmap;
+static wxBitmap *make_spectrum(wxBitmap *bitmap, Func func)
+{
+    wxImage spectrum_image(256, 256);
+    func(spectrum_image.GetData());
+    *bitmap = wxBitmap(spectrum_image);
+    return bitmap;
 }
 
-wxBitmap *DialogColorPicker::MakeGBSpectrum() {
-	return make_spectrum(&rgb_spectrum[0], [=](unsigned char *spec) {
-		for (int g = 0; g < 256; g++) {
-			for (int b = 0; b < 256; b++) {
-				*spec++ = cur_color.r;
-				*spec++ = g;
-				*spec++ = b;
-			}
-		}
-	});
+wxBitmap *DialogColorPicker::MakeGBSpectrum()
+{
+    return make_spectrum(&rgb_spectrum[0], [ = ](unsigned char *spec) {
+        for (int g = 0; g < 256; g++) {
+            for (int b = 0; b < 256; b++) {
+                *spec++ = cur_color.r;
+                *spec++ = g;
+                *spec++ = b;
+            }
+        }
+    });
 }
 
-wxBitmap *DialogColorPicker::MakeRBSpectrum() {
-	return make_spectrum(&rgb_spectrum[1], [=](unsigned char *spec) {
-		for (int r = 0; r < 256; r++) {
-			for (int b = 0; b < 256; b++) {
-				*spec++ = r;
-				*spec++ = cur_color.g;
-				*spec++ = b;
-			}
-		}
-	});
+wxBitmap *DialogColorPicker::MakeRBSpectrum()
+{
+    return make_spectrum(&rgb_spectrum[1], [ = ](unsigned char *spec) {
+        for (int r = 0; r < 256; r++) {
+            for (int b = 0; b < 256; b++) {
+                *spec++ = r;
+                *spec++ = cur_color.g;
+                *spec++ = b;
+            }
+        }
+    });
 }
 
-wxBitmap *DialogColorPicker::MakeRGSpectrum() {
-	return make_spectrum(&rgb_spectrum[2], [=](unsigned char *spec) {
-		for (int r = 0; r < 256; r++) {
-			for (int g = 0; g < 256; g++) {
-				*spec++ = r;
-				*spec++ = g;
-				*spec++ = cur_color.b;
-			}
-		}
-	});
+wxBitmap *DialogColorPicker::MakeRGSpectrum()
+{
+    return make_spectrum(&rgb_spectrum[2], [ = ](unsigned char *spec) {
+        for (int r = 0; r < 256; r++) {
+            for (int g = 0; g < 256; g++) {
+                *spec++ = r;
+                *spec++ = g;
+                *spec++ = cur_color.b;
+            }
+        }
+    });
 }
 
-wxBitmap *DialogColorPicker::MakeHSSpectrum() {
-	int l = hsl_input[2]->GetValue();
-	return make_spectrum(&hsl_spectrum, [=](unsigned char *spec) {
-		for (int h = 0; h < 256; h++) {
-			unsigned char maxr, maxg, maxb;
-			hsl_to_rgb(h, 255, l, &maxr, &maxg, &maxb);
-
-			for (int s = 0; s < 256; s++) {
-				*spec++ = maxr * s / 256 + (255-s) * l / 256;
-				*spec++ = maxg * s / 256 + (255-s) * l / 256;
-				*spec++ = maxb * s / 256 + (255-s) * l / 256;
-			}
-		}
-	});
+wxBitmap *DialogColorPicker::MakeHSSpectrum()
+{
+    int l = hsl_input[2]->GetValue();
+    return make_spectrum(&hsl_spectrum, [ = ](unsigned char *spec) {
+        for (int h = 0; h < 256; h++) {
+            unsigned char maxr, maxg, maxb;
+            hsl_to_rgb(h, 255, l, &maxr, &maxg, &maxb);
+
+            for (int s = 0; s < 256; s++) {
+                *spec++ = maxr * s / 256 + (255 - s) * l / 256;
+                *spec++ = maxg * s / 256 + (255 - s) * l / 256;
+                *spec++ = maxb * s / 256 + (255 - s) * l / 256;
+            }
+        }
+    });
 }
 
-wxBitmap *DialogColorPicker::MakeSVSpectrum() {
-	int h = hsv_input[0]->GetValue();
-	unsigned char maxr, maxg, maxb;
-	hsv_to_rgb(h, 255, 255, &maxr, &maxg, &maxb);
-
-	return make_spectrum(&hsv_spectrum, [=](unsigned char *spec) {
-		for (int v = 0; v < 256; v++) {
-			int rr = (255-maxr) * v / 256;
-			int rg = (255-maxg) * v / 256;
-			int rb = (255-maxb) * v / 256;
-			for (int s = 0; s < 256; s++) {
-				*spec++ = 255 - rr * s / 256 - (255-v);
-				*spec++ = 255 - rg * s / 256 - (255-v);
-				*spec++ = 255 - rb * s / 256 - (255-v);
-			}
-		}
-	});
+wxBitmap *DialogColorPicker::MakeSVSpectrum()
+{
+    int h = hsv_input[0]->GetValue();
+    unsigned char maxr, maxg, maxb;
+    hsv_to_rgb(h, 255, 255, &maxr, &maxg, &maxb);
+
+    return make_spectrum(&hsv_spectrum, [ = ](unsigned char *spec) {
+        for (int v = 0; v < 256; v++) {
+            int rr = (255 - maxr) * v / 256;
+            int rg = (255 - maxg) * v / 256;
+            int rb = (255 - maxb) * v / 256;
+            for (int s = 0; s < 256; s++) {
+                *spec++ = 255 - rr * s / 256 - (255 - v);
+                *spec++ = 255 - rg * s / 256 - (255 - v);
+                *spec++ = 255 - rb * s / 256 - (255 - v);
+            }
+        }
+    });
 }
 
-void DialogColorPicker::OnChangeMode(wxCommandEvent &) {
-	spectrum_dirty = true;
-	OPT_SET("Tool/Colour Picker/Mode")->SetInt(colorspace_choice->GetSelection());
-	UpdateSpectrumDisplay();
+void DialogColorPicker::OnChangeMode(wxCommandEvent &)
+{
+    spectrum_dirty = true;
+    OPT_SET("Tool/Colour Picker/Mode")->SetInt(colorspace_choice->GetSelection());
+    UpdateSpectrumDisplay();
 }
 
-void DialogColorPicker::OnSpectrumChange(wxCommandEvent &) {
-	int i = colorspace_choice->GetSelection();
-	switch (i) {
-		case 0: case 1: case 2:
-			change_value(rgb_input[2 - (i == 2)], spectrum->GetX());
-			change_value(rgb_input[i == 0], spectrum->GetY());
-			break;
-		case 3:
-			change_value(hsl_input[1], spectrum->GetX());
-			change_value(hsl_input[0], spectrum->GetY());
-			break;
-		case 4:
-			change_value(hsv_input[1], spectrum->GetX());
-			change_value(hsv_input[2], spectrum->GetY());
-			break;
-	}
-
-	switch (i) {
-		case 0: case 1: case 2:
-			UpdateFromRGB(false);
-			break;
-		case 3:
-			UpdateFromHSL(false);
-			break;
-		case 4:
-			UpdateFromHSV(false);
-			break;
-	}
+void DialogColorPicker::OnSpectrumChange(wxCommandEvent &)
+{
+    int i = colorspace_choice->GetSelection();
+    switch (i) {
+    case 0: case 1: case 2:
+        change_value(rgb_input[2 - (i == 2)], spectrum->GetX());
+        change_value(rgb_input[i == 0], spectrum->GetY());
+        break;
+    case 3:
+        change_value(hsl_input[1], spectrum->GetX());
+        change_value(hsl_input[0], spectrum->GetY());
+        break;
+    case 4:
+        change_value(hsv_input[1], spectrum->GetX());
+        change_value(hsv_input[2], spectrum->GetY());
+        break;
+    }
+
+    switch (i) {
+    case 0: case 1: case 2:
+        UpdateFromRGB(false);
+        break;
+    case 3:
+        UpdateFromHSL(false);
+        break;
+    case 4:
+        UpdateFromHSV(false);
+        break;
+    }
 }
 
-void DialogColorPicker::OnSliderChange(wxCommandEvent &) {
-	spectrum_dirty = true;
-	int i = colorspace_choice->GetSelection();
-	switch (i) {
-		case 0: case 1: case 2:
-			change_value(rgb_input[i], slider->GetY());
-			UpdateFromRGB(false);
-			break;
-		case 3:
-			change_value(hsl_input[2], slider->GetY());
-			UpdateFromHSL(false);
-			break;
-		case 4:
-			change_value(hsv_input[0], slider->GetY());
-			UpdateFromHSV(false);
-			break;
-	}
+void DialogColorPicker::OnSliderChange(wxCommandEvent &)
+{
+    spectrum_dirty = true;
+    int i = colorspace_choice->GetSelection();
+    switch (i) {
+    case 0: case 1: case 2:
+        change_value(rgb_input[i], slider->GetY());
+        UpdateFromRGB(false);
+        break;
+    case 3:
+        change_value(hsl_input[2], slider->GetY());
+        UpdateFromHSL(false);
+        break;
+    case 4:
+        change_value(hsv_input[0], slider->GetY());
+        UpdateFromHSV(false);
+        break;
+    }
 }
 
-void DialogColorPicker::OnAlphaSliderChange(wxCommandEvent &) {
-	change_value(alpha_input, alpha_slider->GetY());
-	cur_color.a = alpha_slider->GetY();
-	callback(cur_color);
+void DialogColorPicker::OnAlphaSliderChange(wxCommandEvent &)
+{
+    change_value(alpha_input, alpha_slider->GetY());
+    cur_color.a = alpha_slider->GetY();
+    callback(cur_color);
 }
 
-void DialogColorPicker::OnRecentSelect(ValueEvent<agi::Color> &evt) {
-	agi::Color new_color = evt.Get();
-	new_color.a = cur_color.a;
-	SetColor(new_color);
+void DialogColorPicker::OnRecentSelect(ValueEvent<agi::Color> &evt)
+{
+    agi::Color new_color = evt.Get();
+    new_color.a = cur_color.a;
+    SetColor(new_color);
 }
 
-void DialogColorPicker::OnDropperMouse(wxMouseEvent &evt) {
-	if (evt.LeftDown() && !screen_dropper_icon->HasCapture()) {
+void DialogColorPicker::OnDropperMouse(wxMouseEvent &evt)
+{
+    if (evt.LeftDown() && !screen_dropper_icon->HasCapture()) {
 #ifdef WIN32
-		screen_dropper_icon->SetCursor(wxCursor("eyedropper_cursor"));
+        screen_dropper_icon->SetCursor(wxCursor("eyedropper_cursor"));
 #else
-		screen_dropper_icon->SetCursor(*wxCROSS_CURSOR);
+        screen_dropper_icon->SetCursor(*wxCROSS_CURSOR);
 #endif
-		screen_dropper_icon->SetBitmap(wxNullBitmap);
-		screen_dropper_icon->CaptureMouse();
-		eyedropper_grab_point = evt.GetPosition();
-		eyedropper_is_grabbed = false;
-	}
-
-	if (evt.LeftUp()) {
-		wxPoint ptdiff = evt.GetPosition() - eyedropper_grab_point;
-		bool release_now = eyedropper_is_grabbed || abs(ptdiff.x) + abs(ptdiff.y) > 7;
-		if (release_now) {
-			screen_dropper_icon->ReleaseMouse();
-			eyedropper_is_grabbed = false;
-			screen_dropper_icon->SetCursor(wxNullCursor);
-			screen_dropper_icon->SetBitmap(eyedropper_bitmap);
-		}
-		else
-			eyedropper_is_grabbed = true;
-	}
-
-	if (screen_dropper_icon->HasCapture()) {
-		wxPoint scrpos = screen_dropper_icon->ClientToScreen(evt.GetPosition());
-		screen_dropper->DropFromScreenXY(scrpos.x, scrpos.y);
-	}
+        screen_dropper_icon->SetBitmap(wxNullBitmap);
+        screen_dropper_icon->CaptureMouse();
+        eyedropper_grab_point = evt.GetPosition();
+        eyedropper_is_grabbed = false;
+    }
+
+    if (evt.LeftUp()) {
+        wxPoint ptdiff = evt.GetPosition() - eyedropper_grab_point;
+        bool release_now = eyedropper_is_grabbed || abs(ptdiff.x) + abs(ptdiff.y) > 7;
+        if (release_now) {
+            screen_dropper_icon->ReleaseMouse();
+            eyedropper_is_grabbed = false;
+            screen_dropper_icon->SetCursor(wxNullCursor);
+            screen_dropper_icon->SetBitmap(eyedropper_bitmap);
+        }
+        else
+            eyedropper_is_grabbed = true;
+    }
+
+    if (screen_dropper_icon->HasCapture()) {
+        wxPoint scrpos = screen_dropper_icon->ClientToScreen(evt.GetPosition());
+        screen_dropper->DropFromScreenXY(scrpos.x, scrpos.y);
+    }
 }
 
 /// @brief Hack to redirect events to the screen dropper icon
-void DialogColorPicker::OnMouse(wxMouseEvent &evt) {
-	if (!screen_dropper_icon->HasCapture()) {
-		evt.Skip();
-		return;
-	}
-
-	wxPoint dropper_pos = screen_dropper_icon->ScreenToClient(ClientToScreen(evt.GetPosition()));
-	evt.m_x = dropper_pos.x;
-	evt.m_y = dropper_pos.y;
-	screen_dropper_icon->GetEventHandler()->ProcessEvent(evt);
+void DialogColorPicker::OnMouse(wxMouseEvent &evt)
+{
+    if (!screen_dropper_icon->HasCapture()) {
+        evt.Skip();
+        return;
+    }
+
+    wxPoint dropper_pos = screen_dropper_icon->ScreenToClient(ClientToScreen(evt.GetPosition()));
+    evt.m_x = dropper_pos.x;
+    evt.m_y = dropper_pos.y;
+    screen_dropper_icon->GetEventHandler()->ProcessEvent(evt);
 }
 
-void DialogColorPicker::OnCaptureLost(wxMouseCaptureLostEvent&) {
-	eyedropper_is_grabbed = false;
-	screen_dropper_icon->SetCursor(wxNullCursor);
-	screen_dropper_icon->SetBitmap(eyedropper_bitmap);
+void DialogColorPicker::OnCaptureLost(wxMouseCaptureLostEvent &)
+{
+    eyedropper_is_grabbed = false;
+    screen_dropper_icon->SetCursor(wxNullCursor);
+    screen_dropper_icon->SetBitmap(eyedropper_bitmap);
 }
 
 }
 
-bool GetColorFromUser(wxWindow* parent, agi::Color original, bool alpha, std::function<void (agi::Color)> callback) {
-	DialogColorPicker dialog(parent, original, callback, alpha);
-	bool ok = dialog.ShowModal() == wxID_OK;
-	if (!ok)
-		callback(original);
-	else
-		dialog.AddColorToRecent();
-	return ok;
+bool GetColorFromUser(wxWindow *parent, agi::Color original, bool alpha, std::function<void (agi::Color)> callback)
+{
+    DialogColorPicker dialog(parent, original, callback, alpha);
+    bool ok = dialog.ShowModal() == wxID_OK;
+    if (!ok)
+        callback(original);
+    else
+        dialog.AddColorToRecent();
+    return ok;
 }
diff --git a/src/dialog_detached_video.cpp b/src/dialog_detached_video.cpp
index 173f9e8d93a8083e78f4ec844390b249e5a0da94..ae01eafbd7980553ecba2ea2601b5da64889f253 100644
--- a/src/dialog_detached_video.cpp
+++ b/src/dialog_detached_video.cpp
@@ -54,86 +54,90 @@
 #include <wx/display.h> /// Must be included last.
 
 DialogDetachedVideo::DialogDetachedVideo(agi::Context *context)
-: wxDialog(context->parent, -1, "Detached Video", wxDefaultPosition, wxSize(400,300), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxWANTS_CHARS)
-, context(context)
-, old_display(context->videoDisplay)
-, old_slider(context->videoSlider)
-, video_open(context->project->AddVideoProviderListener(&DialogDetachedVideo::OnVideoOpen, this))
+    : wxDialog(context->parent, -1, "Detached Video", wxDefaultPosition, wxSize(400, 300), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX | wxMINIMIZE_BOX | wxWANTS_CHARS)
+    , context(context)
+    , old_display(context->videoDisplay)
+    , old_slider(context->videoSlider)
+    , video_open(context->project->AddVideoProviderListener(&DialogDetachedVideo::OnVideoOpen, this))
 {
-	// Set obscure stuff
-	SetExtraStyle((GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS) | wxWS_EX_PROCESS_UI_UPDATES);
+    // Set obscure stuff
+    SetExtraStyle((GetExtraStyle() & ~wxWS_EX_BLOCK_EVENTS) | wxWS_EX_PROCESS_UI_UPDATES);
 
-	SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
+    SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
 
-	old_display->Unload();
+    old_display->Unload();
 
-	// Video area;
-	auto videoBox = new VideoBox(this, true, context);
-	context->videoDisplay->SetMinClientSize(old_display->GetClientSize());
-	videoBox->Layout();
+    // Video area;
+    auto videoBox = new VideoBox(this, true, context);
+    context->videoDisplay->SetMinClientSize(old_display->GetClientSize());
+    videoBox->Layout();
 
-	// Set sizer
-	wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
-	mainSizer->Add(videoBox,1,wxEXPAND);
-	SetSizerAndFit(mainSizer);
+    // Set sizer
+    wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
+    mainSizer->Add(videoBox, 1, wxEXPAND);
+    SetSizerAndFit(mainSizer);
 
-	// Ensure we can grow smaller, without these the window is locked to at least the initial size
-	context->videoDisplay->SetMinSize(wxSize(1,1));
-	videoBox->SetMinSize(wxSize(1,1));
-	SetMinSize(wxSize(1,1));
+    // Ensure we can grow smaller, without these the window is locked to at least the initial size
+    context->videoDisplay->SetMinSize(wxSize(1, 1));
+    videoBox->SetMinSize(wxSize(1, 1));
+    SetMinSize(wxSize(1, 1));
 
-	persist = agi::make_unique<PersistLocation>(this, "Video/Detached");
+    persist = agi::make_unique<PersistLocation>(this, "Video/Detached");
 
-	int display_index = wxDisplay::GetFromWindow(this);
-	// Ensure that the dialog is no larger than the screen
-	if (display_index != wxNOT_FOUND) {
-		wxRect bounds_rect = GetRect();
-		wxRect disp_rect = wxDisplay(display_index).GetClientArea();
-		SetSize(std::min(bounds_rect.width, disp_rect.width), std::min(bounds_rect.height, disp_rect.height));
-	}
+    int display_index = wxDisplay::GetFromWindow(this);
+    // Ensure that the dialog is no larger than the screen
+    if (display_index != wxNOT_FOUND) {
+        wxRect bounds_rect = GetRect();
+        wxRect disp_rect = wxDisplay(display_index).GetClientArea();
+        SetSize(std::min(bounds_rect.width, disp_rect.width), std::min(bounds_rect.height, disp_rect.height));
+    }
 
-	OPT_SET("Video/Detached/Enabled")->SetBool(true);
+    OPT_SET("Video/Detached/Enabled")->SetBool(true);
 
-	Bind(wxEVT_CLOSE_WINDOW, &DialogDetachedVideo::OnClose, this);
-	Bind(wxEVT_ICONIZE, &DialogDetachedVideo::OnMinimize, this);
-	Bind(wxEVT_CHAR_HOOK, &DialogDetachedVideo::OnKeyDown, this);
+    Bind(wxEVT_CLOSE_WINDOW, &DialogDetachedVideo::OnClose, this);
+    Bind(wxEVT_ICONIZE, &DialogDetachedVideo::OnMinimize, this);
+    Bind(wxEVT_CHAR_HOOK, &DialogDetachedVideo::OnKeyDown, this);
 
-	AddFullScreenButton(this);
+    AddFullScreenButton(this);
 }
 
 DialogDetachedVideo::~DialogDetachedVideo() { }
 
-void DialogDetachedVideo::OnClose(wxCloseEvent &evt) {
-	context->videoDisplay->Destroy();
+void DialogDetachedVideo::OnClose(wxCloseEvent &evt)
+{
+    context->videoDisplay->Destroy();
 
-	context->videoDisplay = old_display;
-	context->videoSlider = old_slider;
+    context->videoDisplay = old_display;
+    context->videoSlider = old_slider;
 
-	OPT_SET("Video/Detached/Enabled")->SetBool(false);
+    OPT_SET("Video/Detached/Enabled")->SetBool(false);
 
-	context->videoController->JumpToFrame(context->videoController->GetFrameN());
+    context->videoController->JumpToFrame(context->videoController->GetFrameN());
 
-	evt.Skip();
+    evt.Skip();
 }
 
-void DialogDetachedVideo::OnMinimize(wxIconizeEvent &event) {
-	if (event.IsIconized()) {
-		// Force the video display to repaint as otherwise the last displayed
-		// frame stays visible even though the dialog is minimized
-		Hide();
-		Show();
-	}
+void DialogDetachedVideo::OnMinimize(wxIconizeEvent &event)
+{
+    if (event.IsIconized()) {
+        // Force the video display to repaint as otherwise the last displayed
+        // frame stays visible even though the dialog is minimized
+        Hide();
+        Show();
+    }
 }
 
-void DialogDetachedVideo::OnKeyDown(wxKeyEvent &evt) {
-	hotkey::check("Video Display", context, evt);
+void DialogDetachedVideo::OnKeyDown(wxKeyEvent &evt)
+{
+    hotkey::check("Video Display", context, evt);
 }
 
-void DialogDetachedVideo::OnVideoOpen(AsyncVideoProvider *new_provider) {
-	if (new_provider)
-		SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
-	else {
-		Close();
-		OPT_SET("Video/Detached/Enabled")->SetBool(true);
-	}
+void DialogDetachedVideo::OnVideoOpen(AsyncVideoProvider *new_provider)
+{
+    if (new_provider)
+        SetTitle(fmt_tl("Video: %s", context->project->VideoName().filename()));
+    else {
+        Close();
+        OPT_SET("Video/Detached/Enabled")->SetBool(true);
+    }
 }
diff --git a/src/dialog_detached_video.h b/src/dialog_detached_video.h
index 97be3fb59c10174c67153ba4cdb2707d0e11f416..e18a09a1ed66085c0d64957bd7260fd359f96da6 100644
--- a/src/dialog_detached_video.h
+++ b/src/dialog_detached_video.h
@@ -44,22 +44,22 @@ class VideoBox;
 class VideoDisplay;
 
 class DialogDetachedVideo final : public wxDialog {
-	agi::Context *context;
-	VideoDisplay *old_display;
-	wxWindow *old_slider;
-	agi::signal::Connection video_open;
-	std::unique_ptr<PersistLocation> persist;
+    agi::Context *context;
+    VideoDisplay *old_display;
+    wxWindow *old_slider;
+    agi::signal::Connection video_open;
+    std::unique_ptr<PersistLocation> persist;
 
-	void OnClose(wxCloseEvent &);
-	/// Minimize event handler to hack around a wx bug
-	void OnMinimize(wxIconizeEvent &evt);
-	void OnKeyDown(wxKeyEvent &evt);
-	void OnVideoOpen(AsyncVideoProvider *new_provider);
+    void OnClose(wxCloseEvent &);
+    /// Minimize event handler to hack around a wx bug
+    void OnMinimize(wxIconizeEvent &evt);
+    void OnKeyDown(wxKeyEvent &evt);
+    void OnVideoOpen(AsyncVideoProvider *new_provider);
 
 public:
-	/// @brief Constructor
-	/// @param context Project context
-	DialogDetachedVideo(agi::Context *context);
-	/// Destructor
-	~DialogDetachedVideo();
+    /// @brief Constructor
+    /// @param context Project context
+    DialogDetachedVideo(agi::Context *context);
+    /// Destructor
+    ~DialogDetachedVideo();
 };
diff --git a/src/dialog_dummy_video.cpp b/src/dialog_dummy_video.cpp
index 23b32ce173fff68c7bf1e63bd2c5c8b1acaab428..a1f9887d036e25a2b2623fe048d6e42af172d660 100644
--- a/src/dialog_dummy_video.cpp
+++ b/src/dialog_dummy_video.cpp
@@ -38,148 +38,156 @@
 
 namespace {
 struct DialogDummyVideo {
-	wxDialog d;
+    wxDialog d;
 
-	double fps       = OPT_GET("Video/Dummy/FPS")->GetDouble();
-	int width        = OPT_GET("Video/Dummy/Last/Width")->GetInt();
-	int height       = OPT_GET("Video/Dummy/Last/Height")->GetInt();
-	int length       = OPT_GET("Video/Dummy/Last/Length")->GetInt();
-	agi::Color color = OPT_GET("Colour/Video Dummy/Last Colour")->GetColor();
-	bool pattern     = OPT_GET("Video/Dummy/Pattern")->GetBool();
+    double fps       = OPT_GET("Video/Dummy/FPS")->GetDouble();
+    int width        = OPT_GET("Video/Dummy/Last/Width")->GetInt();
+    int height       = OPT_GET("Video/Dummy/Last/Height")->GetInt();
+    int length       = OPT_GET("Video/Dummy/Last/Length")->GetInt();
+    agi::Color color = OPT_GET("Colour/Video Dummy/Last Colour")->GetColor();
+    bool pattern     = OPT_GET("Video/Dummy/Pattern")->GetBool();
 
-	wxStaticText *length_display;
-	wxFlexGridSizer *sizer;
+    wxStaticText *length_display;
+    wxFlexGridSizer *sizer;
 
-	template<typename T>
-	void AddCtrl(wxString const& label, T *ctrl);
+    template<typename T>
+    void AddCtrl(wxString const &label, T *ctrl);
 
-	void OnResolutionShortcut(wxCommandEvent &evt);
-	void UpdateLengthDisplay();
+    void OnResolutionShortcut(wxCommandEvent &evt);
+    void UpdateLengthDisplay();
 
-	DialogDummyVideo(wxWindow *parent);
+    DialogDummyVideo(wxWindow *parent);
 };
 
 struct ResolutionShortcut {
-	const char *name;
-	int width;
-	int height;
+    const char *name;
+    int width;
+    int height;
 };
 
 static ResolutionShortcut resolutions[] = {
-	{"640x480 (SD fullscreen)", 640, 480},
-	{"704x480 (SD anamorphic)", 704, 480},
-	{"640x360 (SD widescreen)", 640, 360},
-	{"704x396 (SD widescreen)", 704, 396},
-	{"640x352 (SD widescreen MOD16)", 640, 352},
-	{"704x400 (SD widescreen MOD16)", 704, 400},
-	{"1280x720 (HD 720p)", 1280, 720},
-	{"1920x1080 (HD 1080p)", 1920, 1080},
-	{"1024x576 (SuperPAL widescreen)", 1024, 576}
+    {"640x480 (SD fullscreen)", 640, 480},
+    {"704x480 (SD anamorphic)", 704, 480},
+    {"640x360 (SD widescreen)", 640, 360},
+    {"704x396 (SD widescreen)", 704, 396},
+    {"640x352 (SD widescreen MOD16)", 640, 352},
+    {"704x400 (SD widescreen MOD16)", 704, 400},
+    {"1280x720 (HD 720p)", 1280, 720},
+    {"1920x1080 (HD 1080p)", 1920, 1080},
+    {"1024x576 (SuperPAL widescreen)", 1024, 576}
 };
 
-wxSpinCtrl *spin_ctrl(wxWindow *parent, int min, int max, int *value) {
-	auto ctrl = new wxSpinCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, min, max, *value);
-	ctrl->SetValidator(wxGenericValidator(value));
-	return ctrl;
+wxSpinCtrl *spin_ctrl(wxWindow *parent, int min, int max, int *value)
+{
+    auto ctrl = new wxSpinCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, min, max, *value);
+    ctrl->SetValidator(wxGenericValidator(value));
+    return ctrl;
 }
 
-wxControl *spin_ctrl(wxWindow *parent, double min, double max, double *value) {
-	return new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), 0, DoubleValidator(value, min, max));
+wxControl *spin_ctrl(wxWindow *parent, double min, double max, double *value)
+{
+    return new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(50, -1), 0, DoubleValidator(value, min, max));
 }
 
-wxComboBox *resolution_shortcuts(wxWindow *parent, int width, int height) {
-	wxComboBox *ctrl = new wxComboBox(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
+wxComboBox *resolution_shortcuts(wxWindow *parent, int width, int height)
+{
+    wxComboBox *ctrl = new wxComboBox(parent, -1, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY);
 
-	for (auto const& res : resolutions) {
-		ctrl->Append(res.name);
-		if (res.width == width && res.height == height)
-			ctrl->SetSelection(ctrl->GetCount() - 1);
-	}
+    for (auto const &res : resolutions) {
+        ctrl->Append(res.name);
+        if (res.width == width && res.height == height)
+            ctrl->SetSelection(ctrl->GetCount() - 1);
+    }
 
-	return ctrl;
+    return ctrl;
 }
 
 DialogDummyVideo::DialogDummyVideo(wxWindow *parent)
-: d(parent, -1, _("Dummy video options"))
+    : d(parent, -1, _("Dummy video options"))
 {
-	d.SetIcon(GETICON(use_dummy_video_menu_16));
-
-	auto res_sizer = new wxBoxSizer(wxHORIZONTAL);
-	res_sizer->Add(spin_ctrl(&d, 1, 10000, &width), wxSizerFlags(1).Expand());
-	res_sizer->Add(new wxStaticText(&d, -1, " x "), wxSizerFlags().Center());
-	res_sizer->Add(spin_ctrl(&d, 1, 10000, &height), wxSizerFlags(1).Expand());
-
-	auto color_sizer = new wxBoxSizer(wxHORIZONTAL);
-	auto color_btn = new ColourButton(&d, wxSize(30, 17), false, color);
-	color_sizer->Add(color_btn, wxSizerFlags().DoubleBorder(wxRIGHT));
-	color_sizer->Add(new wxCheckBox(&d, -1, _("Checkerboard &pattern"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&pattern)), wxSizerFlags(1).Center());
-
-	sizer = new wxFlexGridSizer(2, 5, 5);
-	AddCtrl(_("Video resolution:"), resolution_shortcuts(&d, width, height));
-	AddCtrl("", res_sizer);
-	AddCtrl(_("Color:"), color_sizer);
-	AddCtrl(_("Frame rate (fps):"), spin_ctrl(&d, .1, 1000.0, &fps));
-	AddCtrl(_("Duration (frames):"), spin_ctrl(&d, 2, 36000000, &length)); // Ten hours of 1k FPS
-	AddCtrl("", length_display = new wxStaticText(&d, -1, ""));
-
-	auto btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	btn_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Dummy Video"));
-
-	auto main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(sizer, wxSizerFlags(1).Border().Expand());
-	main_sizer->Add(new wxStaticLine(&d, wxHORIZONTAL), wxSizerFlags().HorzBorder().Expand());
-	main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border());
-
-	UpdateLengthDisplay();
-
-	d.SetSizerAndFit(main_sizer);
-	d.CenterOnParent();
-
-	d.Bind(wxEVT_COMBOBOX, &DialogDummyVideo::OnResolutionShortcut, this);
-	color_btn->Bind(EVT_COLOR, [=](ValueEvent<agi::Color>& e) { color = e.Get(); });
-	d.Bind(wxEVT_SPINCTRL, [&](wxCommandEvent&) {
-		d.TransferDataFromWindow();
-		UpdateLengthDisplay();
-	});
+    d.SetIcon(GETICON(use_dummy_video_menu_16));
+
+    auto res_sizer = new wxBoxSizer(wxHORIZONTAL);
+    res_sizer->Add(spin_ctrl(&d, 1, 10000, &width), wxSizerFlags(1).Expand());
+    res_sizer->Add(new wxStaticText(&d, -1, " x "), wxSizerFlags().Center());
+    res_sizer->Add(spin_ctrl(&d, 1, 10000, &height), wxSizerFlags(1).Expand());
+
+    auto color_sizer = new wxBoxSizer(wxHORIZONTAL);
+    auto color_btn = new ColourButton(&d, wxSize(30, 17), false, color);
+    color_sizer->Add(color_btn, wxSizerFlags().DoubleBorder(wxRIGHT));
+    color_sizer->Add(new wxCheckBox(&d, -1, _("Checkerboard &pattern"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&pattern)), wxSizerFlags(1).Center());
+
+    sizer = new wxFlexGridSizer(2, 5, 5);
+    AddCtrl(_("Video resolution:"), resolution_shortcuts(&d, width, height));
+    AddCtrl("", res_sizer);
+    AddCtrl(_("Color:"), color_sizer);
+    AddCtrl(_("Frame rate (fps):"), spin_ctrl(&d, .1, 1000.0, &fps));
+    AddCtrl(_("Duration (frames):"), spin_ctrl(&d, 2, 36000000, &length)); // Ten hours of 1k FPS
+    AddCtrl("", length_display = new wxStaticText(&d, -1, ""));
+
+    auto btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    btn_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Dummy Video"));
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(sizer, wxSizerFlags(1).Border().Expand());
+    main_sizer->Add(new wxStaticLine(&d, wxHORIZONTAL), wxSizerFlags().HorzBorder().Expand());
+    main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border());
+
+    UpdateLengthDisplay();
+
+    d.SetSizerAndFit(main_sizer);
+    d.CenterOnParent();
+
+    d.Bind(wxEVT_COMBOBOX, &DialogDummyVideo::OnResolutionShortcut, this);
+    color_btn->Bind(EVT_COLOR, [ = ](ValueEvent<agi::Color> &e) { color = e.Get(); });
+    d.Bind(wxEVT_SPINCTRL, [&](wxCommandEvent &) {
+        d.TransferDataFromWindow();
+        UpdateLengthDisplay();
+    });
 }
 
-static void add_label(wxWindow *parent, wxSizer *sizer, wxString const& label) {
-	if (!label)
-		sizer->AddStretchSpacer();
-	else
-		sizer->Add(new wxStaticText(parent, -1, label), wxSizerFlags().Center().Left());
+static void add_label(wxWindow *parent, wxSizer *sizer, wxString const &label)
+{
+    if (!label)
+        sizer->AddStretchSpacer();
+    else
+        sizer->Add(new wxStaticText(parent, -1, label), wxSizerFlags().Center().Left());
 }
 
 template<typename T>
-void DialogDummyVideo::AddCtrl(wxString const& label, T *ctrl) {
-	add_label(&d, sizer, label);
-	sizer->Add(ctrl, wxSizerFlags().Expand().Center().Left());
+void DialogDummyVideo::AddCtrl(wxString const &label, T *ctrl)
+{
+    add_label(&d, sizer, label);
+    sizer->Add(ctrl, wxSizerFlags().Expand().Center().Left());
 }
 
-void DialogDummyVideo::OnResolutionShortcut(wxCommandEvent &e) {
-	d.TransferDataFromWindow();
-	int rs = e.GetSelection();
-	width = resolutions[rs].width;
-	height = resolutions[rs].height;
-	d.TransferDataToWindow();
+void DialogDummyVideo::OnResolutionShortcut(wxCommandEvent &e)
+{
+    d.TransferDataFromWindow();
+    int rs = e.GetSelection();
+    width = resolutions[rs].width;
+    height = resolutions[rs].height;
+    d.TransferDataToWindow();
 }
 
-void DialogDummyVideo::UpdateLengthDisplay() {
-	length_display->SetLabel(fmt_tl("Resulting duration: %s", agi::Time(length / fps * 1000).GetAssFormatted(true)));
+void DialogDummyVideo::UpdateLengthDisplay()
+{
+    length_display->SetLabel(fmt_tl("Resulting duration: %s", agi::Time(length / fps * 1000).GetAssFormatted(true)));
 }
 }
 
-std::string CreateDummyVideo(wxWindow *parent) {
-	DialogDummyVideo dlg(parent);
-	if (dlg.d.ShowModal() != wxID_OK)
-		return "";
-
-	OPT_SET("Video/Dummy/FPS")->SetDouble(dlg.fps);
-	OPT_SET("Video/Dummy/Last/Width")->SetInt(dlg.width);
-	OPT_SET("Video/Dummy/Last/Height")->SetInt(dlg.height);
-	OPT_SET("Video/Dummy/Last/Length")->SetInt(dlg.length);
-	OPT_SET("Colour/Video Dummy/Last Colour")->SetColor(dlg.color);
-	OPT_SET("Video/Dummy/Pattern")->SetBool(dlg.pattern);
-
-	return DummyVideoProvider::MakeFilename(dlg.fps, dlg.length, dlg.width, dlg.height, dlg.color, dlg.pattern);
+std::string CreateDummyVideo(wxWindow *parent)
+{
+    DialogDummyVideo dlg(parent);
+    if (dlg.d.ShowModal() != wxID_OK)
+        return "";
+
+    OPT_SET("Video/Dummy/FPS")->SetDouble(dlg.fps);
+    OPT_SET("Video/Dummy/Last/Width")->SetInt(dlg.width);
+    OPT_SET("Video/Dummy/Last/Height")->SetInt(dlg.height);
+    OPT_SET("Video/Dummy/Last/Length")->SetInt(dlg.length);
+    OPT_SET("Colour/Video Dummy/Last Colour")->SetColor(dlg.color);
+    OPT_SET("Video/Dummy/Pattern")->SetBool(dlg.pattern);
+
+    return DummyVideoProvider::MakeFilename(dlg.fps, dlg.length, dlg.width, dlg.height, dlg.color, dlg.pattern);
 }
diff --git a/src/dialog_export.cpp b/src/dialog_export.cpp
index d0bf99389604d5114cf5ab48f59c85c52942f61d..c056b5b68fcd3ac5824e45aa898930ed1e1e8d17 100644
--- a/src/dialog_export.cpp
+++ b/src/dialog_export.cpp
@@ -52,191 +52,198 @@
 
 namespace {
 class DialogExport {
-	wxDialog d;
-	agi::Context *c;
+    wxDialog d;
+    agi::Context *c;
 
-	/// The export transform engine
-	AssExporter exporter;
+    /// The export transform engine
+    AssExporter exporter;
 
-	/// The description of the currently selected export filter
-	wxTextCtrl *filter_description;
+    /// The description of the currently selected export filter
+    wxTextCtrl *filter_description;
 
-	/// A list of all registered export filters
-	wxCheckListBox *filter_list;
+    /// A list of all registered export filters
+    wxCheckListBox *filter_list;
 
-	/// A list of available target charsets
-	wxChoice *charset_list;
+    /// A list of available target charsets
+    wxChoice *charset_list;
 
-	wxSizer *opt_sizer;
+    wxSizer *opt_sizer;
 
-	void OnProcess(wxCommandEvent &);
-	void OnChange(wxCommandEvent &);
+    void OnProcess(wxCommandEvent &);
+    void OnChange(wxCommandEvent &);
 
-	/// Set all the checkboxes
-	void SetAll(bool new_value);
-	/// Update which options sizers are shown
-	void RefreshOptions();
+    /// Set all the checkboxes
+    void SetAll(bool new_value);
+    /// Update which options sizers are shown
+    void RefreshOptions();
 
 public:
-	DialogExport(agi::Context *c);
-	~DialogExport();
-	void ShowModal() { d.ShowModal(); }
+    DialogExport(agi::Context *c);
+    ~DialogExport();
+    void ShowModal() { d.ShowModal(); }
 };
 
 // Swap the items at idx and idx + 1
-void swap(wxCheckListBox *list, int idx, int sel_dir) {
-	if (idx < 0 || idx + 1 == (int)list->GetCount()) return;
-
-	list->Freeze();
-	wxString tempname = list->GetString(idx);
-	bool tempval = list->IsChecked(idx);
-	list->SetString(idx, list->GetString(idx + 1));
-	list->Check(idx, list->IsChecked(idx + 1));
-	list->SetString(idx + 1, tempname);
-	list->Check(idx + 1, tempval);
-	list->SetSelection(idx + sel_dir);
-	list->Thaw();
+void swap(wxCheckListBox *list, int idx, int sel_dir)
+{
+    if (idx < 0 || idx + 1 == (int)list->GetCount()) return;
+
+    list->Freeze();
+    wxString tempname = list->GetString(idx);
+    bool tempval = list->IsChecked(idx);
+    list->SetString(idx, list->GetString(idx + 1));
+    list->Check(idx, list->IsChecked(idx + 1));
+    list->SetString(idx + 1, tempname);
+    list->Check(idx + 1, tempval);
+    list->SetSelection(idx + sel_dir);
+    list->Thaw();
 }
 
 DialogExport::DialogExport(agi::Context *c)
-: d(c->parent, -1, _("Export"), wxDefaultPosition, wxSize(200, 100), wxCAPTION | wxCLOSE_BOX)
-, c(c)
-, exporter(c)
+    : d(c->parent, -1, _("Export"), wxDefaultPosition, wxSize(200, 100), wxCAPTION | wxCLOSE_BOX)
+    , c(c)
+    , exporter(c)
 {
-	d.SetIcon(GETICON(export_menu_16));
-	d.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
-
-	std::vector<std::string> filters = exporter.GetAllFilterNames();
-	filter_list = new wxCheckListBox(&d, -1, wxDefaultPosition, wxSize(200, 100), to_wx(filters));
-	filter_list->Bind(wxEVT_CHECKLISTBOX, [=](wxCommandEvent&) { RefreshOptions(); });
-	filter_list->Bind(wxEVT_LISTBOX, &DialogExport::OnChange, this);
-
-	// Get selected filters
-	std::string const& selected = c->ass->Properties.export_filters;
-	for (auto token : agi::Split(selected, '|')) {
-		auto it = find(begin(filters), end(filters), token);
-		if (it != end(filters))
-			filter_list->Check(distance(begin(filters), it));
-	}
-
-	wxButton *btn_up = new wxButton(&d, -1, _("Move &Up"), wxDefaultPosition, wxSize(90, -1));
-	wxButton *btn_down = new wxButton(&d, -1, _("Move &Down"), wxDefaultPosition, wxSize(90, -1));
-	wxButton *btn_all = new wxButton(&d, -1, _("Select &All"), wxDefaultPosition, wxSize(80, -1));
-	wxButton *btn_none = new wxButton(&d, -1, _("Select &None"), wxDefaultPosition, wxSize(80, -1));
-
-	btn_up->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { swap(filter_list, filter_list->GetSelection() - 1, 0); });
-	btn_down->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { swap(filter_list, filter_list->GetSelection(), 1); });
-	btn_all->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { SetAll(true); });
-	btn_none->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { SetAll(false); });
-
-	wxSizer *top_buttons = new wxBoxSizer(wxHORIZONTAL);
-	top_buttons->Add(btn_up, wxSizerFlags(1).Expand());
-	top_buttons->Add(btn_down, wxSizerFlags(1).Expand().Border(wxRIGHT));
-	top_buttons->Add(btn_all, wxSizerFlags(1).Expand());
-	top_buttons->Add(btn_none, wxSizerFlags(1).Expand());
-
-	filter_description = new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxSize(200, 60), wxTE_MULTILINE | wxTE_READONLY);
-
-	// Charset dropdown list
-	wxStaticText *charset_list_label = new wxStaticText(&d, -1, _("Text encoding:"));
-	charset_list = new wxChoice(&d, -1, wxDefaultPosition, wxDefaultSize, agi::charset::GetEncodingsList<wxArrayString>());
-	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(to_wx(c->ass->Properties.export_encoding)))
-		charset_list->SetStringSelection("Unicode (UTF-8)");
-
-	wxSizer *top_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Filters"));
-	top_sizer->Add(filter_list, wxSizerFlags(1).Expand());
-	top_sizer->Add(top_buttons, wxSizerFlags(0).Expand());
-	top_sizer->Add(filter_description, wxSizerFlags(0).Expand().Border(wxTOP));
-	top_sizer->Add(charset_list_sizer, wxSizerFlags(0).Expand().Border(wxTOP));
-
-	auto btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	btn_sizer->GetAffirmativeButton()->SetLabelText(_("Export..."));
-	d.Bind(wxEVT_BUTTON, &DialogExport::OnProcess, this, wxID_OK);
-	d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Export"), wxID_HELP);
-
-	wxSizer *horz_sizer = new wxBoxSizer(wxHORIZONTAL);
-	opt_sizer = new wxBoxSizer(wxVERTICAL);
-	exporter.DrawSettings(&d, opt_sizer);
-	horz_sizer->Add(top_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxRIGHT));
-	horz_sizer->Add(opt_sizer, wxSizerFlags(1).Expand().Border(wxALL & ~wxLEFT));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(horz_sizer, wxSizerFlags(1).Expand());
-	main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	d.SetSizerAndFit(main_sizer);
-	RefreshOptions();
-	d.CenterOnParent();
+    d.SetIcon(GETICON(export_menu_16));
+    d.SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
+
+    std::vector<std::string> filters = exporter.GetAllFilterNames();
+    filter_list = new wxCheckListBox(&d, -1, wxDefaultPosition, wxSize(200, 100), to_wx(filters));
+    filter_list->Bind(wxEVT_CHECKLISTBOX, [ = ](wxCommandEvent &) { RefreshOptions(); });
+    filter_list->Bind(wxEVT_LISTBOX, &DialogExport::OnChange, this);
+
+    // Get selected filters
+    std::string const &selected = c->ass->Properties.export_filters;
+    for (auto token : agi::Split(selected, '|')) {
+        auto it = find(begin(filters), end(filters), token);
+        if (it != end(filters))
+            filter_list->Check(distance(begin(filters), it));
+    }
+
+    wxButton *btn_up = new wxButton(&d, -1, _("Move &Up"), wxDefaultPosition, wxSize(90, -1));
+    wxButton *btn_down = new wxButton(&d, -1, _("Move &Down"), wxDefaultPosition, wxSize(90, -1));
+    wxButton *btn_all = new wxButton(&d, -1, _("Select &All"), wxDefaultPosition, wxSize(80, -1));
+    wxButton *btn_none = new wxButton(&d, -1, _("Select &None"), wxDefaultPosition, wxSize(80, -1));
+
+    btn_up->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { swap(filter_list, filter_list->GetSelection() - 1, 0); });
+    btn_down->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { swap(filter_list, filter_list->GetSelection(), 1); });
+    btn_all->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { SetAll(true); });
+    btn_none->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { SetAll(false); });
+
+    wxSizer *top_buttons = new wxBoxSizer(wxHORIZONTAL);
+    top_buttons->Add(btn_up, wxSizerFlags(1).Expand());
+    top_buttons->Add(btn_down, wxSizerFlags(1).Expand().Border(wxRIGHT));
+    top_buttons->Add(btn_all, wxSizerFlags(1).Expand());
+    top_buttons->Add(btn_none, wxSizerFlags(1).Expand());
+
+    filter_description = new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxSize(200, 60), wxTE_MULTILINE | wxTE_READONLY);
+
+    // Charset dropdown list
+    wxStaticText *charset_list_label = new wxStaticText(&d, -1, _("Text encoding:"));
+    charset_list = new wxChoice(&d, -1, wxDefaultPosition, wxDefaultSize, agi::charset::GetEncodingsList<wxArrayString>());
+    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(to_wx(c->ass->Properties.export_encoding)))
+        charset_list->SetStringSelection("Unicode (UTF-8)");
+
+    wxSizer *top_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Filters"));
+    top_sizer->Add(filter_list, wxSizerFlags(1).Expand());
+    top_sizer->Add(top_buttons, wxSizerFlags(0).Expand());
+    top_sizer->Add(filter_description, wxSizerFlags(0).Expand().Border(wxTOP));
+    top_sizer->Add(charset_list_sizer, wxSizerFlags(0).Expand().Border(wxTOP));
+
+    auto btn_sizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    btn_sizer->GetAffirmativeButton()->SetLabelText(_("Export..."));
+    d.Bind(wxEVT_BUTTON, &DialogExport::OnProcess, this, wxID_OK);
+    d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Export"), wxID_HELP);
+
+    wxSizer *horz_sizer = new wxBoxSizer(wxHORIZONTAL);
+    opt_sizer = new wxBoxSizer(wxVERTICAL);
+    exporter.DrawSettings(&d, opt_sizer);
+    horz_sizer->Add(top_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxRIGHT));
+    horz_sizer->Add(opt_sizer, wxSizerFlags(1).Expand().Border(wxALL & ~wxLEFT));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(horz_sizer, wxSizerFlags(1).Expand());
+    main_sizer->Add(btn_sizer, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    d.SetSizerAndFit(main_sizer);
+    RefreshOptions();
+    d.CenterOnParent();
 }
 
-DialogExport::~DialogExport() {
-	c->ass->Properties.export_filters.clear();
-	for (size_t i = 0; i < filter_list->GetCount(); ++i) {
-		if (filter_list->IsChecked(i)) {
-			if (!c->ass->Properties.export_filters.empty())
-				c->ass->Properties.export_filters += "|";
-			c->ass->Properties.export_filters += from_wx(filter_list->GetString(i));
-		}
-	}
+DialogExport::~DialogExport()
+{
+    c->ass->Properties.export_filters.clear();
+    for (size_t i = 0; i < filter_list->GetCount(); ++i) {
+        if (filter_list->IsChecked(i)) {
+            if (!c->ass->Properties.export_filters.empty())
+                c->ass->Properties.export_filters += "|";
+            c->ass->Properties.export_filters += from_wx(filter_list->GetString(i));
+        }
+    }
 }
 
-void DialogExport::OnProcess(wxCommandEvent &) {
-	if (!d.TransferDataFromWindow()) return;
-
-	auto filename = SaveFileSelector(_("Export subtitles file"), "", "", "", SubtitleFormat::GetWildcards(1), &d);
-	if (filename.empty()) return;
-
-	for (size_t i = 0; i < filter_list->GetCount(); ++i) {
-		if (filter_list->IsChecked(i))
-			exporter.AddFilter(from_wx(filter_list->GetString(i)));
-	}
-
-	try {
-		wxBusyCursor busy;
-		c->ass->Properties.export_encoding = from_wx(charset_list->GetStringSelection());
-		exporter.Export(filename, from_wx(charset_list->GetStringSelection()), &d);
-	}
-	catch (agi::UserCancelException const&) { }
-	catch (agi::Exception const& err) {
-		wxMessageBox(to_wx(err.GetMessage()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
-	}
-	catch (std::exception const& err) {
-		wxMessageBox(to_wx(err.what()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
-	}
-	catch (...) {
-		wxMessageBox("Unknown error", "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
-	}
-
-	d.EndModal(0);
+void DialogExport::OnProcess(wxCommandEvent &)
+{
+    if (!d.TransferDataFromWindow()) return;
+
+    auto filename = SaveFileSelector(_("Export subtitles file"), "", "", "", SubtitleFormat::GetWildcards(1), &d);
+    if (filename.empty()) return;
+
+    for (size_t i = 0; i < filter_list->GetCount(); ++i) {
+        if (filter_list->IsChecked(i))
+            exporter.AddFilter(from_wx(filter_list->GetString(i)));
+    }
+
+    try {
+        wxBusyCursor busy;
+        c->ass->Properties.export_encoding = from_wx(charset_list->GetStringSelection());
+        exporter.Export(filename, from_wx(charset_list->GetStringSelection()), &d);
+    }
+    catch (agi::UserCancelException const &) { }
+    catch (agi::Exception const &err) {
+        wxMessageBox(to_wx(err.GetMessage()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
+    }
+    catch (std::exception const &err) {
+        wxMessageBox(to_wx(err.what()), "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
+    }
+    catch (...) {
+        wxMessageBox("Unknown error", "Error exporting subtitles", wxOK | wxICON_ERROR | wxCENTER, &d);
+    }
+
+    d.EndModal(0);
 }
 
-void DialogExport::OnChange(wxCommandEvent &) {
-	wxString sel = filter_list->GetStringSelection();
-	if (!sel.empty())
-		filter_description->SetValue(to_wx(exporter.GetDescription(from_wx(sel))));
+void DialogExport::OnChange(wxCommandEvent &)
+{
+    wxString sel = filter_list->GetStringSelection();
+    if (!sel.empty())
+        filter_description->SetValue(to_wx(exporter.GetDescription(from_wx(sel))));
 }
 
-void DialogExport::SetAll(bool new_value) {
-	filter_list->Freeze();
-	for (size_t i = 0; i < filter_list->GetCount(); ++i)
-		filter_list->Check(i, new_value);
-	filter_list->Thaw();
+void DialogExport::SetAll(bool new_value)
+{
+    filter_list->Freeze();
+    for (size_t i = 0; i < filter_list->GetCount(); ++i)
+        filter_list->Check(i, new_value);
+    filter_list->Thaw();
 
-	RefreshOptions();
+    RefreshOptions();
 }
 
-void DialogExport::RefreshOptions() {
-	for (size_t i = 0; i < filter_list->GetCount(); ++i) {
-		if (wxSizer *sizer = exporter.GetSettingsSizer(from_wx(filter_list->GetString(i))))
-			opt_sizer->Show(sizer, filter_list->IsChecked(i), true);
-	}
-	d.Layout();
-	d.GetSizer()->Fit(&d);
+void DialogExport::RefreshOptions()
+{
+    for (size_t i = 0; i < filter_list->GetCount(); ++i) {
+        if (wxSizer *sizer = exporter.GetSettingsSizer(from_wx(filter_list->GetString(i))))
+            opt_sizer->Show(sizer, filter_list->IsChecked(i), true);
+    }
+    d.Layout();
+    d.GetSizer()->Fit(&d);
 }
 }
 
-void ShowExportDialog(agi::Context *c) {
-	DialogExport(c).ShowModal();
+void ShowExportDialog(agi::Context *c)
+{
+    DialogExport(c).ShowModal();
 }
diff --git a/src/dialog_export_ebu3264.cpp b/src/dialog_export_ebu3264.cpp
index 77fb10fd3f3684032cc834c43c833036431831a6..02ec66340d3b6bf256c37e0ad89352e1c3f70e72 100644
--- a/src/dialog_export_ebu3264.cpp
+++ b/src/dialog_export_ebu3264.cpp
@@ -44,216 +44,220 @@
 #include <wx/valgen.h>
 
 namespace {
-	const boost::regex 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 final : public wxValidator {
-		EbuTimecode *value;
+/// Validator for SMPTE timecodes
+class TimecodeValidator final : public wxValidator {
+    EbuTimecode *value;
 
-		wxTextCtrl *GetCtrl() const { return dynamic_cast<wxTextCtrl*>(GetWindow()); }
+    wxTextCtrl *GetCtrl() const { return dynamic_cast<wxTextCtrl *>(GetWindow()); }
 
-		bool TransferToWindow() override {
-			wxTextCtrl *ctrl = GetCtrl();
-			if (!ctrl) return false;
-			ctrl->SetValue(fmt_wx("%02d:%02d:%02d:%02d", value->h, value->m, value->s, value->f));
-			return true;
-		}
+    bool TransferToWindow() override {
+        wxTextCtrl *ctrl = GetCtrl();
+        if (!ctrl) return false;
+        ctrl->SetValue(fmt_wx("%02d:%02d:%02d:%02d", value->h, value->m, value->s, value->f));
+        return true;
+    }
 
-		bool TransferFromWindow() override {
-			wxTextCtrl *ctrl = GetCtrl();
-			if (!ctrl) return false;
+    bool TransferFromWindow() override {
+        wxTextCtrl *ctrl = GetCtrl();
+        if (!ctrl) return false;
 
-			std::string str = from_wx(ctrl->GetValue());
-			boost::smatch result;
-			if (!regex_match(str, result, timecode_regex))
-				return false;
+        std::string str = from_wx(ctrl->GetValue());
+        boost::smatch result;
+        if (!regex_match(str, result, timecode_regex))
+            return false;
 
-			value->h = boost::lexical_cast<int>(result.str(1));
-			value->m = boost::lexical_cast<int>(result.str(2));
-			value->s = boost::lexical_cast<int>(result.str(3));
-			value->f = boost::lexical_cast<int>(result.str(4));
+        value->h = boost::lexical_cast<int>(result.str(1));
+        value->m = boost::lexical_cast<int>(result.str(2));
+        value->s = boost::lexical_cast<int>(result.str(3));
+        value->f = boost::lexical_cast<int>(result.str(4));
 
-			return true;
-		}
+        return true;
+    }
 
-		bool Validate(wxWindow *parent) override {
-			wxTextCtrl *ctrl = GetCtrl();
-			if (!ctrl) return false;
+    bool Validate(wxWindow *parent) override {
+        wxTextCtrl *ctrl = GetCtrl();
+        if (!ctrl) return false;
 
-			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;
-			}
-			return true;
-		}
+        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;
+        }
+        return true;
+    }
 
-		wxObject *Clone() const override { return new TimecodeValidator(*this); }
+    wxObject *Clone() const override { return new TimecodeValidator(*this); }
 
-	public:
-		TimecodeValidator(EbuTimecode *target) : value(target) { assert(target); }
-		TimecodeValidator(TimecodeValidator const& other) : wxValidator(other), value(other.value) { }
-	};
+public:
+    TimecodeValidator(EbuTimecode *target) : value(target) { assert(target); }
+    TimecodeValidator(TimecodeValidator const &other) : wxValidator(other), value(other.value) { }
+};
 
 } // namespace {
 
-int ShowEbuExportConfigurationDialog(wxWindow *owner, EbuExportSettings &s) {
-	wxDialog d(owner, -1, _("Export to EBU STL format"));
-
-	wxString tv_standards[] = {
-		_("23.976 fps (non-standard, STL24.01)"),
-		_("24 fps (non-standard, STL24.01)"),
-		_("25 fps (STL25.01)"),
-		_("29.97 fps (non-dropframe, STL30.01)"),
-		_("29.97 fps (dropframe, STL30.01)"),
-		_("30 fps (STL30.01)")
-	};
-	wxRadioBox *tv_standard_box = new wxRadioBox(&d, -1, _("TV standard"), wxDefaultPosition, wxDefaultSize, 6, tv_standards, 0, wxRA_SPECIFY_ROWS);
-
-	wxTextCtrl *timecode_offset_entry = new wxTextCtrl(&d, -1, "00:00:00:00");
-	wxCheckBox *inclusive_end_times_check = new wxCheckBox(&d, -1, _("Out-times are inclusive"));
-
-	wxString text_encodings[] = {
-		_("ISO 6937-2 (Latin/Western Europe)"),
-		_("ISO 8859-5 (Cyrillic)"),
-		_("ISO 8859-6 (Arabic)"),
-		_("ISO 8859-7 (Greek)"),
-		_("ISO 8859-8 (Hebrew)"),
-		_("UTF-8 Unicode (non-standard)")
-	};
-	wxRadioBox *text_encoding_box = new wxRadioBox(&d, -1, _("Text encoding"), wxDefaultPosition, wxDefaultSize, 6, text_encodings, 0, wxRA_SPECIFY_ROWS);
-
-	wxString wrap_modes[] = {
-		_("Automatically wrap long lines (ASS)"),
-		_("Automatically wrap long lines (Balanced)"),
-		_("Abort if any lines are too long"),
-		_("Skip lines that are too long")
-	};
-
-	wxSpinCtrl *max_line_length_ctrl = new wxSpinCtrl(&d, -1, wxString(), wxDefaultPosition, wxSize(65, -1));
-	wxComboBox *wrap_mode_ctrl = new wxComboBox(&d, -1, wrap_modes[0], wxDefaultPosition, wxDefaultSize, 4, wrap_modes, wxCB_DROPDOWN | wxCB_READONLY);
-	wxCheckBox *translate_alignments_check = new wxCheckBox(&d, -1, _("Translate alignments"));
-
-	max_line_length_ctrl->SetRange(10, 99);
-
-	wxString display_standards[] = {
-		_("Open subtitles"),
-		_("Level-1 teletext"),
-		_("Level-2 teletext")
-	};
-
-	wxComboBox *display_standard_ctrl = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 2, display_standards, wxCB_DROPDOWN | wxCB_READONLY);
-
-	wxSizer *max_line_length_labelled = new wxBoxSizer(wxHORIZONTAL);
-	max_line_length_labelled->Add(new wxStaticText(&d, -1, _("Max. line length:")), 1, wxALIGN_CENTRE|wxRIGHT, 12);
-	max_line_length_labelled->Add(max_line_length_ctrl, 0, 0, 0);
-
-	wxSizer *timecode_offset_labelled = new wxBoxSizer(wxHORIZONTAL);
-	timecode_offset_labelled->Add(new wxStaticText(&d, -1, _("Time code offset:")), 1, wxALIGN_CENTRE|wxRIGHT, 12);
-	timecode_offset_labelled->Add(timecode_offset_entry, 0, 0, 0);
-
-	wxSizer *text_formatting_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Text formatting"));
-	text_formatting_sizer->Add(max_line_length_labelled, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-	text_formatting_sizer->Add(wrap_mode_ctrl, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-	text_formatting_sizer->Add(translate_alignments_check, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-
-	wxSizer *timecode_control_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Time codes"));
-	timecode_control_sizer->Add(timecode_offset_labelled, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-	timecode_control_sizer->Add(inclusive_end_times_check, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-
-	wxSizer *display_standard_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Display standard"));
-	display_standard_sizer->Add(display_standard_ctrl, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
-
-	wxSizer *left_column = new wxBoxSizer(wxVERTICAL);
-	left_column->Add(tv_standard_box, 0, wxEXPAND | wxBOTTOM, 6);
-	left_column->Add(timecode_control_sizer, 0, wxEXPAND | wxBOTTOM, 6);
-	left_column->Add(display_standard_sizer, 0, wxEXPAND, 0);
-
-	wxSizer *right_column = new wxBoxSizer(wxVERTICAL);
-	right_column->Add(text_encoding_box, 0, wxEXPAND|wxBOTTOM, 6);
-	right_column->Add(text_formatting_sizer, 0, wxEXPAND, 0);
-
-	wxSizer *vertical_split_sizer = new wxBoxSizer(wxHORIZONTAL);
-	vertical_split_sizer->Add(left_column, 0, wxRIGHT, 6);
-	vertical_split_sizer->Add(right_column, 0, 0, 0);
-
-	wxSizer *buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
-	// Developers are requested to leave &d message in! Intentionally not translatable.
-	wxStaticText *sponsor_label = new wxStaticText(&d, -1, "EBU STL format writing sponsored by Bandai");
-	sponsor_label->Enable(false);
-	buttons_sizer->Add(sponsor_label, 1, wxALIGN_BOTTOM, 0);
-	buttons_sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxLEFT, 6);
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(vertical_split_sizer, 0, wxEXPAND|wxALL, 12);
-	main_sizer->Add(buttons_sizer, 0, wxEXPAND | (wxALL & ~wxTOP), 12);
-
-	d.SetSizerAndFit(main_sizer);
-	d.CenterOnParent();
-
-	// set up validators to move data in and out
-	tv_standard_box->SetValidator(wxGenericValidator((int*)&s.tv_standard));
-	text_encoding_box->SetValidator(wxGenericValidator((int*)&s.text_encoding));
-	translate_alignments_check->SetValidator(wxGenericValidator(&s.translate_alignments));
-	max_line_length_ctrl->SetValidator(wxGenericValidator(&s.max_line_length));
-	wrap_mode_ctrl->SetValidator(wxGenericValidator((int*)&s.line_wrapping_mode));
-	inclusive_end_times_check->SetValidator(wxGenericValidator(&s.inclusive_end_times));
-	timecode_offset_entry->SetValidator(TimecodeValidator(&s.timecode_offset));
-	display_standard_ctrl->SetValidator(wxGenericValidator((int*)&s.display_standard));
-
-	return d.ShowModal();
+int ShowEbuExportConfigurationDialog(wxWindow *owner, EbuExportSettings &s)
+{
+    wxDialog d(owner, -1, _("Export to EBU STL format"));
+
+    wxString tv_standards[] = {
+        _("23.976 fps (non-standard, STL24.01)"),
+        _("24 fps (non-standard, STL24.01)"),
+        _("25 fps (STL25.01)"),
+        _("29.97 fps (non-dropframe, STL30.01)"),
+        _("29.97 fps (dropframe, STL30.01)"),
+        _("30 fps (STL30.01)")
+    };
+    wxRadioBox *tv_standard_box = new wxRadioBox(&d, -1, _("TV standard"), wxDefaultPosition, wxDefaultSize, 6, tv_standards, 0, wxRA_SPECIFY_ROWS);
+
+    wxTextCtrl *timecode_offset_entry = new wxTextCtrl(&d, -1, "00:00:00:00");
+    wxCheckBox *inclusive_end_times_check = new wxCheckBox(&d, -1, _("Out-times are inclusive"));
+
+    wxString text_encodings[] = {
+        _("ISO 6937-2 (Latin/Western Europe)"),
+        _("ISO 8859-5 (Cyrillic)"),
+        _("ISO 8859-6 (Arabic)"),
+        _("ISO 8859-7 (Greek)"),
+        _("ISO 8859-8 (Hebrew)"),
+        _("UTF-8 Unicode (non-standard)")
+    };
+    wxRadioBox *text_encoding_box = new wxRadioBox(&d, -1, _("Text encoding"), wxDefaultPosition, wxDefaultSize, 6, text_encodings, 0, wxRA_SPECIFY_ROWS);
+
+    wxString wrap_modes[] = {
+        _("Automatically wrap long lines (ASS)"),
+        _("Automatically wrap long lines (Balanced)"),
+        _("Abort if any lines are too long"),
+        _("Skip lines that are too long")
+    };
+
+    wxSpinCtrl *max_line_length_ctrl = new wxSpinCtrl(&d, -1, wxString(), wxDefaultPosition, wxSize(65, -1));
+    wxComboBox *wrap_mode_ctrl = new wxComboBox(&d, -1, wrap_modes[0], wxDefaultPosition, wxDefaultSize, 4, wrap_modes, wxCB_DROPDOWN | wxCB_READONLY);
+    wxCheckBox *translate_alignments_check = new wxCheckBox(&d, -1, _("Translate alignments"));
+
+    max_line_length_ctrl->SetRange(10, 99);
+
+    wxString display_standards[] = {
+        _("Open subtitles"),
+        _("Level-1 teletext"),
+        _("Level-2 teletext")
+    };
+
+    wxComboBox *display_standard_ctrl = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 2, display_standards, wxCB_DROPDOWN | wxCB_READONLY);
+
+    wxSizer *max_line_length_labelled = new wxBoxSizer(wxHORIZONTAL);
+    max_line_length_labelled->Add(new wxStaticText(&d, -1, _("Max. line length:")), 1, wxALIGN_CENTRE | wxRIGHT, 12);
+    max_line_length_labelled->Add(max_line_length_ctrl, 0, 0, 0);
+
+    wxSizer *timecode_offset_labelled = new wxBoxSizer(wxHORIZONTAL);
+    timecode_offset_labelled->Add(new wxStaticText(&d, -1, _("Time code offset:")), 1, wxALIGN_CENTRE | wxRIGHT, 12);
+    timecode_offset_labelled->Add(timecode_offset_entry, 0, 0, 0);
+
+    wxSizer *text_formatting_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Text formatting"));
+    text_formatting_sizer->Add(max_line_length_labelled, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    text_formatting_sizer->Add(wrap_mode_ctrl, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    text_formatting_sizer->Add(translate_alignments_check, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+
+    wxSizer *timecode_control_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Time codes"));
+    timecode_control_sizer->Add(timecode_offset_labelled, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    timecode_control_sizer->Add(inclusive_end_times_check, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+
+    wxSizer *display_standard_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Display standard"));
+    display_standard_sizer->Add(display_standard_ctrl, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+
+    wxSizer *left_column = new wxBoxSizer(wxVERTICAL);
+    left_column->Add(tv_standard_box, 0, wxEXPAND | wxBOTTOM, 6);
+    left_column->Add(timecode_control_sizer, 0, wxEXPAND | wxBOTTOM, 6);
+    left_column->Add(display_standard_sizer, 0, wxEXPAND, 0);
+
+    wxSizer *right_column = new wxBoxSizer(wxVERTICAL);
+    right_column->Add(text_encoding_box, 0, wxEXPAND | wxBOTTOM, 6);
+    right_column->Add(text_formatting_sizer, 0, wxEXPAND, 0);
+
+    wxSizer *vertical_split_sizer = new wxBoxSizer(wxHORIZONTAL);
+    vertical_split_sizer->Add(left_column, 0, wxRIGHT, 6);
+    vertical_split_sizer->Add(right_column, 0, 0, 0);
+
+    wxSizer *buttons_sizer = new wxBoxSizer(wxHORIZONTAL);
+    // Developers are requested to leave &d message in! Intentionally not translatable.
+    wxStaticText *sponsor_label = new wxStaticText(&d, -1, "EBU STL format writing sponsored by Bandai");
+    sponsor_label->Enable(false);
+    buttons_sizer->Add(sponsor_label, 1, wxALIGN_BOTTOM, 0);
+    buttons_sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL), 0, wxLEFT, 6);
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(vertical_split_sizer, 0, wxEXPAND | wxALL, 12);
+    main_sizer->Add(buttons_sizer, 0, wxEXPAND | (wxALL & ~wxTOP), 12);
+
+    d.SetSizerAndFit(main_sizer);
+    d.CenterOnParent();
+
+    // set up validators to move data in and out
+    tv_standard_box->SetValidator(wxGenericValidator((int *)&s.tv_standard));
+    text_encoding_box->SetValidator(wxGenericValidator((int *)&s.text_encoding));
+    translate_alignments_check->SetValidator(wxGenericValidator(&s.translate_alignments));
+    max_line_length_ctrl->SetValidator(wxGenericValidator(&s.max_line_length));
+    wrap_mode_ctrl->SetValidator(wxGenericValidator((int *)&s.line_wrapping_mode));
+    inclusive_end_times_check->SetValidator(wxGenericValidator(&s.inclusive_end_times));
+    timecode_offset_entry->SetValidator(TimecodeValidator(&s.timecode_offset));
+    display_standard_ctrl->SetValidator(wxGenericValidator((int *)&s.display_standard));
+
+    return d.ShowModal();
 }
 
-agi::vfr::Framerate EbuExportSettings::GetFramerate() const {
-	switch (tv_standard) {
-		case STL24:     return agi::vfr::Framerate(24, 1);
-		case STL25:     return agi::vfr::Framerate(25, 1);
-		case STL30:     return agi::vfr::Framerate(30, 1);
-		case STL23:     return agi::vfr::Framerate(24000, 1001, false);
-		case STL29:     return agi::vfr::Framerate(30000, 1001, false);
-		case STL29drop: return agi::vfr::Framerate(30000, 1001);
-		default:        return agi::vfr::Framerate(25, 1);
-	}
+agi::vfr::Framerate EbuExportSettings::GetFramerate() const
+{
+    switch (tv_standard) {
+    case STL24:     return agi::vfr::Framerate(24, 1);
+    case STL25:     return agi::vfr::Framerate(25, 1);
+    case STL30:     return agi::vfr::Framerate(30, 1);
+    case STL23:     return agi::vfr::Framerate(24000, 1001, false);
+    case STL29:     return agi::vfr::Framerate(30000, 1001, false);
+    case STL29drop: return agi::vfr::Framerate(30000, 1001);
+    default:        return agi::vfr::Framerate(25, 1);
+    }
 }
 
-std::unique_ptr<agi::charset::IconvWrapper> EbuExportSettings::GetTextEncoder() const {
-	using namespace agi;
-	switch (text_encoding) {
-		case iso6937_2: return make_unique<charset::IconvWrapper>("utf-8", "ISO-6937-2");
-		case iso8859_5: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-5");
-		case iso8859_6: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-6");
-		case iso8859_7: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-7");
-		case iso8859_8: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-8");
-		case utf8:      return make_unique<charset::IconvWrapper>("utf-8", "utf-8");
-		default:        return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-1");
-	}
+std::unique_ptr<agi::charset::IconvWrapper> EbuExportSettings::GetTextEncoder() const
+{
+    using namespace agi;
+    switch (text_encoding) {
+    case iso6937_2: return make_unique<charset::IconvWrapper>("utf-8", "ISO-6937-2");
+    case iso8859_5: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-5");
+    case iso8859_6: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-6");
+    case iso8859_7: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-7");
+    case iso8859_8: return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-8");
+    case utf8:      return make_unique<charset::IconvWrapper>("utf-8", "utf-8");
+    default:        return make_unique<charset::IconvWrapper>("utf-8", "ISO-8859-1");
+    }
 }
 
-EbuExportSettings::EbuExportSettings(std::string const& prefix)
-: prefix(prefix)
-, tv_standard((TvStandard)OPT_GET(prefix + "/TV Standard")->GetInt())
-, text_encoding((TextEncoding)OPT_GET(prefix + "/Text Encoding")->GetInt())
-, max_line_length(OPT_GET(prefix + "/Max Line Length")->GetInt())
-, line_wrapping_mode((LineWrappingMode)OPT_GET(prefix + "/Line Wrapping Mode")->GetInt())
-, translate_alignments(OPT_GET(prefix + "/Translate Alignments")->GetBool())
-, inclusive_end_times(OPT_GET(prefix + "/Inclusive End Times")->GetBool())
-, display_standard((DisplayStandard)OPT_GET(prefix + "/Display Standard")->GetInt())
+EbuExportSettings::EbuExportSettings(std::string const &prefix)
+    : prefix(prefix)
+    , tv_standard((TvStandard)OPT_GET(prefix + "/TV Standard")->GetInt())
+    , text_encoding((TextEncoding)OPT_GET(prefix + "/Text Encoding")->GetInt())
+    , max_line_length(OPT_GET(prefix + "/Max Line Length")->GetInt())
+    , line_wrapping_mode((LineWrappingMode)OPT_GET(prefix + "/Line Wrapping Mode")->GetInt())
+    , translate_alignments(OPT_GET(prefix + "/Translate Alignments")->GetBool())
+    , inclusive_end_times(OPT_GET(prefix + "/Inclusive End Times")->GetBool())
+    , display_standard((DisplayStandard)OPT_GET(prefix + "/Display Standard")->GetInt())
 {
-	timecode_offset.h = OPT_GET(prefix + "/Timecode Offset/H")->GetInt();
-	timecode_offset.m = OPT_GET(prefix + "/Timecode Offset/M")->GetInt();
-	timecode_offset.s = OPT_GET(prefix + "/Timecode Offset/S")->GetInt();
-	timecode_offset.f = OPT_GET(prefix + "/Timecode Offset/F")->GetInt();
+    timecode_offset.h = OPT_GET(prefix + "/Timecode Offset/H")->GetInt();
+    timecode_offset.m = OPT_GET(prefix + "/Timecode Offset/M")->GetInt();
+    timecode_offset.s = OPT_GET(prefix + "/Timecode Offset/S")->GetInt();
+    timecode_offset.f = OPT_GET(prefix + "/Timecode Offset/F")->GetInt();
 }
 
-void EbuExportSettings::Save() const {
-	OPT_SET(prefix + "/TV Standard")->SetInt(tv_standard);
-	OPT_SET(prefix + "/Text Encoding")->SetInt(text_encoding);
-	OPT_SET(prefix + "/Max Line Length")->SetInt(max_line_length);
-	OPT_SET(prefix + "/Line Wrapping Mode")->SetInt(line_wrapping_mode);
-	OPT_SET(prefix + "/Translate Alignments")->SetBool(translate_alignments);
-	OPT_SET(prefix + "/Inclusive End Times")->SetBool(inclusive_end_times);
-	OPT_SET(prefix + "/Display Standard")->SetInt(display_standard);
-	OPT_SET(prefix + "/Timecode Offset/H")->SetInt(timecode_offset.h);
-	OPT_SET(prefix + "/Timecode Offset/M")->SetInt(timecode_offset.m);
-	OPT_SET(prefix + "/Timecode Offset/S")->SetInt(timecode_offset.s);
-	OPT_SET(prefix + "/Timecode Offset/F")->SetInt(timecode_offset.f);
+void EbuExportSettings::Save() const
+{
+    OPT_SET(prefix + "/TV Standard")->SetInt(tv_standard);
+    OPT_SET(prefix + "/Text Encoding")->SetInt(text_encoding);
+    OPT_SET(prefix + "/Max Line Length")->SetInt(max_line_length);
+    OPT_SET(prefix + "/Line Wrapping Mode")->SetInt(line_wrapping_mode);
+    OPT_SET(prefix + "/Translate Alignments")->SetBool(translate_alignments);
+    OPT_SET(prefix + "/Inclusive End Times")->SetBool(inclusive_end_times);
+    OPT_SET(prefix + "/Display Standard")->SetInt(display_standard);
+    OPT_SET(prefix + "/Timecode Offset/H")->SetInt(timecode_offset.h);
+    OPT_SET(prefix + "/Timecode Offset/M")->SetInt(timecode_offset.m);
+    OPT_SET(prefix + "/Timecode Offset/S")->SetInt(timecode_offset.s);
+    OPT_SET(prefix + "/Timecode Offset/F")->SetInt(timecode_offset.f);
 }
diff --git a/src/dialog_export_ebu3264.h b/src/dialog_export_ebu3264.h
index ec31b26c6e0619b90c83917f9d150072da2dd9e7..642cdc85f2ba609f460e902e3b3e526e67c8e6da 100644
--- a/src/dialog_export_ebu3264.h
+++ b/src/dialog_export_ebu3264.h
@@ -29,86 +29,86 @@ namespace agi { namespace charset { class IconvWrapper; } }
 #pragma pack(push, 1)
 /// A binary timecode representation, packed
 struct EbuTimecode {
-	uint8_t h, m, s, f;
+    uint8_t h, m, s, f;
 };
 #pragma pack(pop)
 
 /// User configuration for EBU Tech 3264-1991 export
 class EbuExportSettings {
-	/// Prefix used for saving to/loading from options
-	std::string prefix;
+    /// Prefix used for saving to/loading from options
+    std::string prefix;
 public:
-	/// Frame rate + timecode format
-	enum TvStandard {
-		STL23     = 0, ///< 23.976 fps (non-dropframe) (marked as 24)
-		STL24     = 1, ///< 24 fps (film)
-		STL25     = 2, ///< 25 fps (PAL)
-		STL29     = 3, ///< 29.97 fps (non-dropframe) (marked as 30)
-		STL29drop = 4, ///< 29.97 fps (dropframe) (marked as 30)
-		STL30     = 5, ///< 30 fps (NTSC monochrome)
-	};
-
-	/// Character sets for subtitle data
-	enum TextEncoding {
-		iso6937_2 = 0, ///< latin multibyte
-		iso8859_5 = 1, ///< cyrillic
-		iso8859_6 = 2, ///< arabic
-		iso8859_7 = 3, ///< greek
-		iso8859_8 = 4, ///< hebrew
-		utf8      = 5, ///< nonstandard
-	};
-
-	/// Modes for handling lines over the maximum width
-	enum LineWrappingMode {
-		AutoWrap         = 0, ///< Wrap overly-long lines ASS-style
-		AutoWrapBalance  = 1, ///< Wrap overly-long lines with balanced lines
-		AbortOverLength  = 2, ///< Fail if there are overly-long lines
-		IgnoreOverLength = 3  ///< Skip overly-long lines
-	};
-
-	/// Types of subtitles/captions that can be stored in STL files
-	enum DisplayStandard {
-		DSC_Open      = 0, ///< Open subtitles
-		DSC_Level1    = 1, ///< Level-1 teletext closed captions
-		DSC_Level2    = 2  ///< Level-2 teletext closed captions
-	};
-
-	/// Which TV standard (frame rate + timecode encoding) to use
-	TvStandard tv_standard;
-
-	/// How to encode subtitle text
-	TextEncoding text_encoding;
-
-	/// Maximum length of rows in subtitles (in characters)
-	int max_line_length;
-
-	/// How to deal with over-length rows
-	LineWrappingMode line_wrapping_mode;
-
-	/// Translate SSA alignments?
-	bool translate_alignments;
-
-	/// Timecode which time 0 in Aegisub corresponds to
-	EbuTimecode timecode_offset;
-
-	/// Are end timecodes inclusive or exclusive?
-	bool inclusive_end_times;
-
-	/// Save as subtitles, or as closed captions?
-	DisplayStandard display_standard;
-
-	/// Get the frame rate for the current TV Standard
-	agi::vfr::Framerate GetFramerate() const;
-
-	/// Get a charset encoder for the current text encoding
-	std::unique_ptr<agi::charset::IconvWrapper> GetTextEncoder() const;
-
-	/// Load saved export settings from options
-	/// @param prefix Option name prefix
-	EbuExportSettings(std::string const& prefix);
-
-	/// Save export settings to options
-	void Save() const;
+    /// Frame rate + timecode format
+    enum TvStandard {
+        STL23     = 0, ///< 23.976 fps (non-dropframe) (marked as 24)
+        STL24     = 1, ///< 24 fps (film)
+        STL25     = 2, ///< 25 fps (PAL)
+        STL29     = 3, ///< 29.97 fps (non-dropframe) (marked as 30)
+        STL29drop = 4, ///< 29.97 fps (dropframe) (marked as 30)
+        STL30     = 5, ///< 30 fps (NTSC monochrome)
+    };
+
+    /// Character sets for subtitle data
+    enum TextEncoding {
+        iso6937_2 = 0, ///< latin multibyte
+        iso8859_5 = 1, ///< cyrillic
+        iso8859_6 = 2, ///< arabic
+        iso8859_7 = 3, ///< greek
+        iso8859_8 = 4, ///< hebrew
+        utf8      = 5, ///< nonstandard
+    };
+
+    /// Modes for handling lines over the maximum width
+    enum LineWrappingMode {
+        AutoWrap         = 0, ///< Wrap overly-long lines ASS-style
+        AutoWrapBalance  = 1, ///< Wrap overly-long lines with balanced lines
+        AbortOverLength  = 2, ///< Fail if there are overly-long lines
+        IgnoreOverLength = 3  ///< Skip overly-long lines
+    };
+
+    /// Types of subtitles/captions that can be stored in STL files
+    enum DisplayStandard {
+        DSC_Open      = 0, ///< Open subtitles
+        DSC_Level1    = 1, ///< Level-1 teletext closed captions
+        DSC_Level2    = 2  ///< Level-2 teletext closed captions
+    };
+
+    /// Which TV standard (frame rate + timecode encoding) to use
+    TvStandard tv_standard;
+
+    /// How to encode subtitle text
+    TextEncoding text_encoding;
+
+    /// Maximum length of rows in subtitles (in characters)
+    int max_line_length;
+
+    /// How to deal with over-length rows
+    LineWrappingMode line_wrapping_mode;
+
+    /// Translate SSA alignments?
+    bool translate_alignments;
+
+    /// Timecode which time 0 in Aegisub corresponds to
+    EbuTimecode timecode_offset;
+
+    /// Are end timecodes inclusive or exclusive?
+    bool inclusive_end_times;
+
+    /// Save as subtitles, or as closed captions?
+    DisplayStandard display_standard;
+
+    /// Get the frame rate for the current TV Standard
+    agi::vfr::Framerate GetFramerate() const;
+
+    /// Get a charset encoder for the current text encoding
+    std::unique_ptr<agi::charset::IconvWrapper> GetTextEncoder() const;
+
+    /// Load saved export settings from options
+    /// @param prefix Option name prefix
+    EbuExportSettings(std::string const &prefix);
+
+    /// Save export settings to options
+    void Save() const;
 };
 
 /// Show a dialog box for getting an export configuration for EBU Tech 3264-1991
diff --git a/src/dialog_fonts_collector.cpp b/src/dialog_fonts_collector.cpp
index 78ac20ef5cfb5b8d8d87ff69968bb9760b1469b3..3b95878b8f458ee15e7cafe0d4f11be8ba9c5d03 100644
--- a/src/dialog_fonts_collector.cpp
+++ b/src/dialog_fonts_collector.cpp
@@ -49,377 +49,390 @@
 
 namespace {
 enum class FcMode {
-	CheckFontsOnly = 0,
-	CopyToFolder = 1,
-	CopyToScriptFolder = 2,
-	CopyToZip = 3,
-	SymlinkToFolder = 4
+    CheckFontsOnly = 0,
+    CopyToFolder = 1,
+    CopyToScriptFolder = 2,
+    CopyToZip = 3,
+    SymlinkToFolder = 4
 };
 
 class DialogFontsCollector final : public wxDialog {
-	AssFile *subs;
-	agi::Path &path;
-	FcMode mode = FcMode::CheckFontsOnly;
+    AssFile *subs;
+    agi::Path &path;
+    FcMode mode = FcMode::CheckFontsOnly;
 
-	wxStyledTextCtrl *collection_log;
-	wxButton *close_btn;
-	wxButton *dest_browse_button;
-	wxButton *start_btn;
-	wxRadioBox *collection_mode;
-	wxStaticText *dest_label;
-	wxTextCtrl *dest_ctrl;
+    wxStyledTextCtrl *collection_log;
+    wxButton *close_btn;
+    wxButton *dest_browse_button;
+    wxButton *start_btn;
+    wxRadioBox *collection_mode;
+    wxStaticText *dest_label;
+    wxTextCtrl *dest_ctrl;
 
-	void OnStart(wxCommandEvent &);
-	void OnBrowse(wxCommandEvent &);
-	void OnRadio(wxCommandEvent &e);
+    void OnStart(wxCommandEvent &);
+    void OnBrowse(wxCommandEvent &);
+    void OnRadio(wxCommandEvent &e);
 
-	/// Append text to log message from worker thread
-	void OnAddText(ValueEvent<std::pair<int, wxString>>& event);
-	/// Collection complete notification from the worker thread to reenable buttons
-	void OnCollectionComplete(wxThreadEvent &);
+    /// Append text to log message from worker thread
+    void OnAddText(ValueEvent<std::pair<int, wxString>> &event);
+    /// Collection complete notification from the worker thread to reenable buttons
+    void OnCollectionComplete(wxThreadEvent &);
 
-	void UpdateControls();
+    void UpdateControls();
 
 public:
-	DialogFontsCollector(agi::Context *c);
+    DialogFontsCollector(agi::Context *c);
 };
 
 using color_str_pair = std::pair<int, wxString>;
 wxDEFINE_EVENT(EVT_ADD_TEXT, ValueEvent<color_str_pair>);
 wxDEFINE_EVENT(EVT_COLLECTION_DONE, wxThreadEvent);
 
-void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMode oper, wxEvtHandler *collector) {
-	agi::dispatch::Background().Async([=]{
-		auto AppendText = [&](wxString text, int colour) {
-			collector->AddPendingEvent(ValueEvent<color_str_pair>(EVT_ADD_TEXT, -1, {colour, text.Clone()}));
-		};
-
-		auto paths = FontCollector(AppendText).GetFontPaths(subs);
-		if (paths.empty()) {
-			collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
-			return;
-		}
-
-		// Copy fonts
-		switch (oper) {
-			case FcMode::CheckFontsOnly:
-				collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
-				return;
-			case FcMode::SymlinkToFolder:
-				AppendText(_("Symlinking fonts to folder...\n"), 0);
-				break;
-			case FcMode::CopyToScriptFolder:
-			case FcMode::CopyToFolder:
-				AppendText(_("Copying fonts to folder...\n"), 0);
-				break;
-			case FcMode::CopyToZip:
-				AppendText(_("Copying fonts to archive...\n"), 0);
-				break;
-		}
-
-		// Open zip stream if saving to compressed archive
-		std::unique_ptr<wxFFileOutputStream> out;
-		std::unique_ptr<wxZipOutputStream> zip;
-		if (oper == FcMode::CopyToZip) {
-			try {
-				agi::fs::CreateDirectory(destination.parent_path());
-			}
-			catch (agi::fs::FileSystemError const& e) {
-				AppendText(fmt_tl("* Failed to create directory '%s': %s.\n",
-					destination.parent_path().wstring(), to_wx(e.GetMessage())), 2);
-				collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
-				return;
-			}
-
-			out = agi::make_unique<wxFFileOutputStream>(destination.wstring());
-			if (out->IsOk())
-				zip = agi::make_unique<wxZipOutputStream>(*out);
-
-			if (!out->IsOk() || !zip || !zip->IsOk()) {
-				AppendText(fmt_tl("* Failed to open %s.\n", destination), 2);
-				collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
-				return;
-			}
-		}
-
-		int64_t total_size = 0;
-		bool allOk = true;
-		for (auto path : paths) {
-			path.make_preferred();
-
-			int ret = 0;
-			total_size += agi::fs::Size(path);
-
-			switch (oper) {
-				case FcMode::SymlinkToFolder:
-				case FcMode::CopyToScriptFolder:
-				case FcMode::CopyToFolder: {
-					auto dest = destination/path.filename();
-					if (agi::fs::FileExists(dest))
-						ret = 2;
+void FontsCollectorThread(AssFile *subs, agi::fs::path const &destination, FcMode oper, wxEvtHandler *collector)
+{
+    agi::dispatch::Background().Async([ = ] {
+        auto AppendText = [&](wxString text, int colour)
+        {
+            collector->AddPendingEvent(ValueEvent<color_str_pair>(EVT_ADD_TEXT, -1, {colour, text.Clone()}));
+        };
+
+        auto paths = FontCollector(AppendText).GetFontPaths(subs);
+        if (paths.empty())
+        {
+            collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
+            return;
+        }
+
+        // Copy fonts
+        switch (oper)
+        {
+        case FcMode::CheckFontsOnly:
+            collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
+            return;
+        case FcMode::SymlinkToFolder:
+            AppendText(_("Symlinking fonts to folder...\n"), 0);
+            break;
+        case FcMode::CopyToScriptFolder:
+        case FcMode::CopyToFolder:
+            AppendText(_("Copying fonts to folder...\n"), 0);
+            break;
+        case FcMode::CopyToZip:
+            AppendText(_("Copying fonts to archive...\n"), 0);
+            break;
+        }
+
+        // Open zip stream if saving to compressed archive
+        std::unique_ptr<wxFFileOutputStream> out;
+        std::unique_ptr<wxZipOutputStream> zip;
+        if (oper == FcMode::CopyToZip)
+        {
+            try {
+                agi::fs::CreateDirectory(destination.parent_path());
+            }
+            catch (agi::fs::FileSystemError const &e) {
+                AppendText(fmt_tl("* Failed to create directory '%s': %s.\n",
+                                  destination.parent_path().wstring(), to_wx(e.GetMessage())), 2);
+                collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
+                return;
+            }
+
+            out = agi::make_unique<wxFFileOutputStream>(destination.wstring());
+            if (out->IsOk())
+                zip = agi::make_unique<wxZipOutputStream>(*out);
+
+            if (!out->IsOk() || !zip || !zip->IsOk()) {
+                AppendText(fmt_tl("* Failed to open %s.\n", destination), 2);
+                collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
+                return;
+            }
+        }
+
+        int64_t total_size = 0;
+        bool allOk = true;
+        for (auto path : paths)
+        {
+            path.make_preferred();
+
+            int ret = 0;
+            total_size += agi::fs::Size(path);
+
+            switch (oper) {
+            case FcMode::SymlinkToFolder:
+            case FcMode::CopyToScriptFolder:
+            case FcMode::CopyToFolder: {
+                auto dest = destination / path.filename();
+                if (agi::fs::FileExists(dest))
+                    ret = 2;
 #ifndef _WIN32
-					else if (oper == FcMode::SymlinkToFolder) {
-						// returns 0 on success, -1 on error...
-						if (symlink(path.c_str(), dest.c_str()))
-							ret = 0;
-						else
-							ret = 3;
-					}
+                else if (oper == FcMode::SymlinkToFolder) {
+                    // returns 0 on success, -1 on error...
+                    if (symlink(path.c_str(), dest.c_str()))
+                        ret = 0;
+                    else
+                        ret = 3;
+                }
 #endif
-					else {
-						try {
-							agi::fs::Copy(path, dest);
-							ret = true;
-						}
-						catch (...) {
-							ret = false;
-						}
-					}
-				}
-				break;
-
-				case FcMode::CopyToZip: {
-					wxFFileInputStream in(path.wstring());
-					if (!in.IsOk())
-						ret = false;
-					else {
-						ret = zip->PutNextEntry(path.filename().wstring());
-						zip->Write(in);
-					}
-				}
-				default: break;
-			}
-
-			if (ret == 1)
-				AppendText(fmt_tl("* Copied %s.\n", path), 1);
-			else if (ret == 2)
-				AppendText(fmt_tl("* %s already exists on destination.\n", path.filename()), 3);
-			else if (ret == 3)
-				AppendText(fmt_tl("* Symlinked %s.\n", path), 1);
-			else {
-				AppendText(fmt_tl("* Failed to copy %s.\n", path), 2);
-				allOk = false;
-			}
-		}
-
-		if (allOk)
-			AppendText(_("Done. All fonts copied."), 1);
-		else
-			AppendText(_("Done. Some fonts could not be copied."), 2);
-
-		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", 0);
-
-		collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
-	});
+                else {
+                    try {
+                        agi::fs::Copy(path, dest);
+                        ret = true;
+                    }
+                    catch (...) {
+                        ret = false;
+                    }
+                }
+            }
+            break;
+
+            case FcMode::CopyToZip: {
+                wxFFileInputStream in(path.wstring());
+                if (!in.IsOk())
+                    ret = false;
+                else {
+                    ret = zip->PutNextEntry(path.filename().wstring());
+                    zip->Write(in);
+                }
+            }
+            default: break;
+            }
+
+            if (ret == 1)
+                AppendText(fmt_tl("* Copied %s.\n", path), 1);
+            else if (ret == 2)
+                AppendText(fmt_tl("* %s already exists on destination.\n", path.filename()), 3);
+            else if (ret == 3)
+                AppendText(fmt_tl("* Symlinked %s.\n", path), 1);
+            else {
+                AppendText(fmt_tl("* Failed to copy %s.\n", path), 2);
+                allOk = false;
+            }
+        }
+
+        if (allOk)
+            AppendText(_("Done. All fonts copied."), 1);
+        else
+            AppendText(_("Done. Some fonts could not be copied."), 2);
+
+        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", 0);
+
+        collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
+    });
 }
 
 DialogFontsCollector::DialogFontsCollector(agi::Context *c)
-: wxDialog(c->parent, -1, _("Fonts Collector"))
-, subs(c->ass.get())
-, path(*c->path)
+    : wxDialog(c->parent, -1, _("Fonts Collector"))
+    , subs(c->ass.get())
+    , path(*c->path)
 {
-	SetIcon(GETICON(font_collector_button_16));
+    SetIcon(GETICON(font_collector_button_16));
 
-	wxString modes[] = {
-		 _("Check fonts for availability")
-		,_("Copy fonts to folder")
-		,_("Copy fonts to subtitle file's folder")
-		,_("Copy fonts to zipped archive")
+    wxString modes[] = {
+        _("Check fonts for availability")
+        , _("Copy fonts to folder")
+        , _("Copy fonts to subtitle file's folder")
+        , _("Copy fonts to zipped archive")
 #ifndef _WIN32
-		,_("Symlink fonts to folder")
+        , _("Symlink fonts to folder")
 #endif
-	};
-
-	mode = static_cast<FcMode>(mid<int>(0, OPT_GET("Tool/Fonts Collector/Action")->GetInt(), countof(modes)));
-	collection_mode = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, countof(modes), modes, 1);
-	collection_mode->SetSelection(static_cast<int>(mode));
-
-	if (c->path->Decode("?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, c->path->Decode(OPT_GET("Path/Fonts Collector Destination")->GetString()).wstring());
-	dest_browse_button = new wxButton(this, -1, _("&Browse..."));
-
-	wxSizer *dest_browse_sizer = new wxBoxSizer(wxHORIZONTAL);
-	dest_browse_sizer->Add(dest_ctrl, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
-	dest_browse_sizer->Add(dest_browse_button, wxSizerFlags());
-
-	destination_box->Add(dest_label, wxSizerFlags().Border(wxBOTTOM));
-	destination_box->Add(dest_browse_sizer, wxSizerFlags().Expand());
-
-	wxStaticBoxSizer *log_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Log"));
-	collection_log = new wxStyledTextCtrl(this, -1, wxDefaultPosition, wxSize(600, 300));
-	collection_log->SetWrapMode(wxSTC_WRAP_WORD);
-	collection_log->SetMarginWidth(1, 0);
-	collection_log->SetReadOnly(true);
-	collection_log->StyleSetForeground(1, wxColour(0, 200, 0));
-	collection_log->StyleSetForeground(2, wxColour(200, 0, 0));
-	collection_log->StyleSetForeground(3, wxColour(200, 100, 0));
-	log_box->Add(collection_log, wxSizerFlags().Border());
-
-	wxStdDialogButtonSizer *button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	start_btn = button_sizer->GetAffirmativeButton();
-	close_btn = button_sizer->GetCancelButton();
-	start_btn->SetLabel(_("&Start!"));
-	start_btn->SetDefault();
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(collection_mode, wxSizerFlags().Expand().Border());
-	main_sizer->Add(destination_box, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	main_sizer->Add(log_box, wxSizerFlags().Border(wxALL & ~wxTOP));
-	main_sizer->Add(button_sizer, wxSizerFlags().Right().Border(wxALL & ~wxTOP));
-
-	SetSizerAndFit(main_sizer);
-	CenterOnParent();
-
-	// Update the browse button and label
-	UpdateControls();
-
-	start_btn->Bind(wxEVT_BUTTON, &DialogFontsCollector::OnStart, this);
-	dest_browse_button->Bind(wxEVT_BUTTON, &DialogFontsCollector::OnBrowse, this);
-	collection_mode->Bind(wxEVT_RADIOBOX, &DialogFontsCollector::OnRadio, this);
-	button_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Fonts Collector"));
-	Bind(EVT_ADD_TEXT, &DialogFontsCollector::OnAddText, this);
-	Bind(EVT_COLLECTION_DONE, &DialogFontsCollector::OnCollectionComplete, this);
+    };
+
+    mode = static_cast<FcMode>(mid<int>(0, OPT_GET("Tool/Fonts Collector/Action")->GetInt(), countof(modes)));
+    collection_mode = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, countof(modes), modes, 1);
+    collection_mode->SetSelection(static_cast<int>(mode));
+
+    if (c->path->Decode("?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, c->path->Decode(OPT_GET("Path/Fonts Collector Destination")->GetString()).wstring());
+    dest_browse_button = new wxButton(this, -1, _("&Browse..."));
+
+    wxSizer *dest_browse_sizer = new wxBoxSizer(wxHORIZONTAL);
+    dest_browse_sizer->Add(dest_ctrl, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
+    dest_browse_sizer->Add(dest_browse_button, wxSizerFlags());
+
+    destination_box->Add(dest_label, wxSizerFlags().Border(wxBOTTOM));
+    destination_box->Add(dest_browse_sizer, wxSizerFlags().Expand());
+
+    wxStaticBoxSizer *log_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Log"));
+    collection_log = new wxStyledTextCtrl(this, -1, wxDefaultPosition, wxSize(600, 300));
+    collection_log->SetWrapMode(wxSTC_WRAP_WORD);
+    collection_log->SetMarginWidth(1, 0);
+    collection_log->SetReadOnly(true);
+    collection_log->StyleSetForeground(1, wxColour(0, 200, 0));
+    collection_log->StyleSetForeground(2, wxColour(200, 0, 0));
+    collection_log->StyleSetForeground(3, wxColour(200, 100, 0));
+    log_box->Add(collection_log, wxSizerFlags().Border());
+
+    wxStdDialogButtonSizer *button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    start_btn = button_sizer->GetAffirmativeButton();
+    close_btn = button_sizer->GetCancelButton();
+    start_btn->SetLabel(_("&Start!"));
+    start_btn->SetDefault();
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(collection_mode, wxSizerFlags().Expand().Border());
+    main_sizer->Add(destination_box, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    main_sizer->Add(log_box, wxSizerFlags().Border(wxALL & ~wxTOP));
+    main_sizer->Add(button_sizer, wxSizerFlags().Right().Border(wxALL & ~wxTOP));
+
+    SetSizerAndFit(main_sizer);
+    CenterOnParent();
+
+    // Update the browse button and label
+    UpdateControls();
+
+    start_btn->Bind(wxEVT_BUTTON, &DialogFontsCollector::OnStart, this);
+    dest_browse_button->Bind(wxEVT_BUTTON, &DialogFontsCollector::OnBrowse, this);
+    collection_mode->Bind(wxEVT_RADIOBOX, &DialogFontsCollector::OnRadio, this);
+    button_sizer->GetHelpButton()->Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Fonts Collector"));
+    Bind(EVT_ADD_TEXT, &DialogFontsCollector::OnAddText, this);
+    Bind(EVT_COLLECTION_DONE, &DialogFontsCollector::OnCollectionComplete, this);
 }
 
-void DialogFontsCollector::OnStart(wxCommandEvent &) {
-	collection_log->SetReadOnly(false);
-	collection_log->ClearAll();
-	collection_log->SetReadOnly(true);
-
-	agi::fs::path dest;
-	if (mode != FcMode::CheckFontsOnly) {
-		dest = path.Decode(mode == FcMode::CopyToScriptFolder ? "?script/" : from_wx(dest_ctrl->GetValue()));
-
-		if (mode != FcMode::CopyToZip) {
-			if (agi::fs::FileExists(dest))
-				wxMessageBox(_("Invalid destination."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
-			try {
-				agi::fs::CreateDirectory(dest);
-			}
-			catch (agi::Exception const&) {
-				wxMessageBox(_("Could not create destination folder."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
-				return;
-			}
-		}
-		else if (agi::fs::DirectoryExists(dest) || dest.filename().empty()) {
-			wxMessageBox(_("Invalid path for .zip file."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
-			return;
-		}
-	}
-
-	if (mode != FcMode::CheckFontsOnly)
-		OPT_SET("Path/Fonts Collector Destination")->SetString(dest.string());
-
-	// Disable the UI while it runs as we don't support canceling
-	EnableCloseButton(false);
-	start_btn->Enable(false);
-	dest_browse_button->Enable(false);
-	dest_ctrl->Enable(false);
-	close_btn->Enable(false);
-	collection_mode->Enable(false);
-	dest_label->Enable(false);
-
-	FontsCollectorThread(subs, dest, mode, GetEventHandler());
+void DialogFontsCollector::OnStart(wxCommandEvent &)
+{
+    collection_log->SetReadOnly(false);
+    collection_log->ClearAll();
+    collection_log->SetReadOnly(true);
+
+    agi::fs::path dest;
+    if (mode != FcMode::CheckFontsOnly) {
+        dest = path.Decode(mode == FcMode::CopyToScriptFolder ? "?script/" : from_wx(dest_ctrl->GetValue()));
+
+        if (mode != FcMode::CopyToZip) {
+            if (agi::fs::FileExists(dest))
+                wxMessageBox(_("Invalid destination."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
+            try {
+                agi::fs::CreateDirectory(dest);
+            }
+            catch (agi::Exception const &) {
+                wxMessageBox(_("Could not create destination folder."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
+                return;
+            }
+        }
+        else if (agi::fs::DirectoryExists(dest) || dest.filename().empty()) {
+            wxMessageBox(_("Invalid path for .zip file."), _("Error"), wxOK | wxICON_ERROR | wxCENTER, this);
+            return;
+        }
+    }
+
+    if (mode != FcMode::CheckFontsOnly)
+        OPT_SET("Path/Fonts Collector Destination")->SetString(dest.string());
+
+    // Disable the UI while it runs as we don't support canceling
+    EnableCloseButton(false);
+    start_btn->Enable(false);
+    dest_browse_button->Enable(false);
+    dest_ctrl->Enable(false);
+    close_btn->Enable(false);
+    collection_mode->Enable(false);
+    dest_label->Enable(false);
+
+    FontsCollectorThread(subs, dest, mode, GetEventHandler());
 }
 
-void DialogFontsCollector::OnBrowse(wxCommandEvent &) {
-	wxString dest;
-	if (mode == FcMode::CopyToZip) {
-		dest = wxFileSelector(
-			_("Select archive file name"),
-			dest_ctrl->GetValue(),
-			wxFileName(dest_ctrl->GetValue()).GetFullName(),
-			".zip", "Zip Archives (*.zip)|*.zip",
-			wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
-	}
-	else
-		dest = wxDirSelector(_("Select folder to save fonts on"), dest_ctrl->GetValue(), 0);
-
-	if (!dest.empty())
-		dest_ctrl->SetValue(dest);
+void DialogFontsCollector::OnBrowse(wxCommandEvent &)
+{
+    wxString dest;
+    if (mode == FcMode::CopyToZip) {
+        dest = wxFileSelector(
+                   _("Select archive file name"),
+                   dest_ctrl->GetValue(),
+                   wxFileName(dest_ctrl->GetValue()).GetFullName(),
+                   ".zip", "Zip Archives (*.zip)|*.zip",
+                   wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+    }
+    else
+        dest = wxDirSelector(_("Select folder to save fonts on"), dest_ctrl->GetValue(), 0);
+
+    if (!dest.empty())
+        dest_ctrl->SetValue(dest);
 }
 
-void DialogFontsCollector::OnRadio(wxCommandEvent &evt) {
-	OPT_SET("Tool/Fonts Collector/Action")->SetInt(evt.GetInt());
-	mode = static_cast<FcMode>(evt.GetInt());
-	UpdateControls();
+void DialogFontsCollector::OnRadio(wxCommandEvent &evt)
+{
+    OPT_SET("Tool/Fonts Collector/Action")->SetInt(evt.GetInt());
+    mode = static_cast<FcMode>(evt.GetInt());
+    UpdateControls();
 }
 
-void DialogFontsCollector::UpdateControls() {
-	wxString dst = dest_ctrl->GetValue();
-
-	if (mode == FcMode::CheckFontsOnly || mode == FcMode::CopyToScriptFolder) {
-		dest_ctrl->Enable(false);
-		dest_browse_button->Enable(false);
-		dest_label->Enable(false);
-		dest_label->SetLabel(_("N/A"));
-	}
-	else {
-		dest_ctrl->Enable(true);
-		dest_browse_button->Enable(true);
-		dest_label->Enable(true);
-
-		if (mode == FcMode::CopyToFolder || mode == FcMode::SymlinkToFolder) {
-			dest_label->SetLabel(_("Choose the folder where the fonts will be collected to. It will be created if it doesn't exist."));
-
-			// Remove filename from browse box
-			if (dst.Right(4) == ".zip")
-				dest_ctrl->SetValue(wxFileName(dst).GetPath());
-		}
-		else {
-			dest_label->SetLabel(_("Enter the name of the destination zip file to collect the fonts to. If a folder is entered, a default name will be used."));
-
-			// Add filename to browse box
-			if (!dst.EndsWith(".zip")) {
-				wxFileName fn(dst + "//");
-				fn.SetFullName("fonts.zip");
-				dest_ctrl->SetValue(fn.GetFullPath());
-			}
-		}
-	}
+void DialogFontsCollector::UpdateControls()
+{
+    wxString dst = dest_ctrl->GetValue();
+
+    if (mode == FcMode::CheckFontsOnly || mode == FcMode::CopyToScriptFolder) {
+        dest_ctrl->Enable(false);
+        dest_browse_button->Enable(false);
+        dest_label->Enable(false);
+        dest_label->SetLabel(_("N/A"));
+    }
+    else {
+        dest_ctrl->Enable(true);
+        dest_browse_button->Enable(true);
+        dest_label->Enable(true);
+
+        if (mode == FcMode::CopyToFolder || mode == FcMode::SymlinkToFolder) {
+            dest_label->SetLabel(_("Choose the folder where the fonts will be collected to. It will be created if it doesn't exist."));
+
+            // Remove filename from browse box
+            if (dst.Right(4) == ".zip")
+                dest_ctrl->SetValue(wxFileName(dst).GetPath());
+        }
+        else {
+            dest_label->SetLabel(_("Enter the name of the destination zip file to collect the fonts to. If a folder is entered, a default name will be used."));
+
+            // Add filename to browse box
+            if (!dst.EndsWith(".zip")) {
+                wxFileName fn(dst + "//");
+                fn.SetFullName("fonts.zip");
+                dest_ctrl->SetValue(fn.GetFullPath());
+            }
+        }
+    }
 
 #ifdef __APPLE__
-	// wxStaticText auto-wraps everywhere but OS X
-	dest_label->Wrap(dest_label->GetParent()->GetSize().GetWidth() - 20);
-	Layout();
+    // wxStaticText auto-wraps everywhere but OS X
+    dest_label->Wrap(dest_label->GetParent()->GetSize().GetWidth() - 20);
+    Layout();
 #endif
 }
 
-void DialogFontsCollector::OnAddText(ValueEvent<color_str_pair> &event) {
-	auto const& str = event.Get();
-	collection_log->SetReadOnly(false);
-	int pos = collection_log->GetLength();
-	auto const& utf8 = str.second.utf8_str();
-	collection_log->AppendTextRaw(utf8.data(), utf8.length());
-	if (str.first) {
-		collection_log->StartStyling(pos, 31);
-		collection_log->SetStyling(utf8.length(), str.first);
-	}
-	collection_log->GotoPos(pos + utf8.length());
-	collection_log->SetReadOnly(true);
+void DialogFontsCollector::OnAddText(ValueEvent<color_str_pair> &event)
+{
+    auto const &str = event.Get();
+    collection_log->SetReadOnly(false);
+    int pos = collection_log->GetLength();
+    auto const &utf8 = str.second.utf8_str();
+    collection_log->AppendTextRaw(utf8.data(), utf8.length());
+    if (str.first) {
+        collection_log->StartStyling(pos, 31);
+        collection_log->SetStyling(utf8.length(), str.first);
+    }
+    collection_log->GotoPos(pos + utf8.length());
+    collection_log->SetReadOnly(true);
 }
 
-void DialogFontsCollector::OnCollectionComplete(wxThreadEvent &) {
-	EnableCloseButton(true);
-	start_btn->Enable();
-	close_btn->Enable();
-	collection_mode->Enable();
-	if (path.Decode("?script") == "?script")
-		collection_mode->Enable(2, false);
-
-	wxCommandEvent evt;
-	OnRadio(evt);
+void DialogFontsCollector::OnCollectionComplete(wxThreadEvent &)
+{
+    EnableCloseButton(true);
+    start_btn->Enable();
+    close_btn->Enable();
+    collection_mode->Enable();
+    if (path.Decode("?script") == "?script")
+        collection_mode->Enable(2, false);
+
+    wxCommandEvent evt;
+    OnRadio(evt);
 }
 }
 
-void ShowFontsCollectorDialog(agi::Context *c) {
-	c->dialog->Show<DialogFontsCollector>(c);
+void ShowFontsCollectorDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogFontsCollector>(c);
 }
diff --git a/src/dialog_jumpto.cpp b/src/dialog_jumpto.cpp
index 8503240f8b8d34770c3b7efb63535bedc7b3e317..a6c8b9ec00da03d9f88abb00b06300c65cede2bc 100644
--- a/src/dialog_jumpto.cpp
+++ b/src/dialog_jumpto.cpp
@@ -45,90 +45,95 @@
 
 namespace {
 struct DialogJumpTo {
-	wxDialog d;
-	agi::Context *c;       ///< Project context
-	long jumpframe;        ///< Target frame to jump to
-	TimeEdit *JumpTime;    ///< Target time edit control
-	wxTextCtrl *JumpFrame; ///< Target frame edit control
-
-	/// Enter/OK button handler
-	void OnOK(wxCommandEvent &event);
-	/// Update target frame on target time changed
-	void OnEditTime(wxCommandEvent &event);
-	/// Update target time on target frame changed
-	void OnEditFrame(wxCommandEvent &event);
-	/// Dialog initializer to set default focus and selection
-	void OnInitDialog(wxInitDialogEvent&);
-
-	DialogJumpTo(agi::Context *c);
+    wxDialog d;
+    agi::Context *c;       ///< Project context
+    long jumpframe;        ///< Target frame to jump to
+    TimeEdit *JumpTime;    ///< Target time edit control
+    wxTextCtrl *JumpFrame; ///< Target frame edit control
+
+    /// Enter/OK button handler
+    void OnOK(wxCommandEvent &event);
+    /// Update target frame on target time changed
+    void OnEditTime(wxCommandEvent &event);
+    /// Update target time on target frame changed
+    void OnEditFrame(wxCommandEvent &event);
+    /// Dialog initializer to set default focus and selection
+    void OnInitDialog(wxInitDialogEvent &);
+
+    DialogJumpTo(agi::Context *c);
 };
 
 DialogJumpTo::DialogJumpTo(agi::Context *c)
-: d(c->parent, -1, _("Jump to"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxWANTS_CHARS)
-, c(c)
-, jumpframe(c->videoController->GetFrameN())
+    : d(c->parent, -1, _("Jump to"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxWANTS_CHARS)
+    , c(c)
+    , jumpframe(c->videoController->GetFrameN())
 {
-	d.SetIcon(GETICON(jumpto_button_16));
+    d.SetIcon(GETICON(jumpto_button_16));
 
-	auto LabelFrame = new wxStaticText(&d, -1, _("Frame: "));
-	auto LabelTime = new wxStaticText(&d, -1, _("Time: "));
+    auto LabelFrame = new wxStaticText(&d, -1, _("Frame: "));
+    auto LabelTime = new wxStaticText(&d, -1, _("Time: "));
 
-	JumpFrame = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxSize(-1,-1),wxTE_PROCESS_ENTER, IntValidator((int)jumpframe));
-	JumpFrame->SetMaxLength(std::to_string(c->project->VideoProvider()->GetFrameCount() - 1).size());
-	JumpTime = new TimeEdit(&d, -1, c, agi::Time(c->videoController->TimeAtFrame(jumpframe)).GetAssFormatted(), wxSize(-1,-1));
+    JumpFrame = new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxSize(-1, -1), wxTE_PROCESS_ENTER, IntValidator((int)jumpframe));
+    JumpFrame->SetMaxLength(std::to_string(c->project->VideoProvider()->GetFrameCount() - 1).size());
+    JumpTime = new TimeEdit(&d, -1, c, agi::Time(c->videoController->TimeAtFrame(jumpframe)).GetAssFormatted(), wxSize(-1, -1));
 
-	auto TimesSizer = new wxGridSizer(2, 5, 5);
+    auto TimesSizer = new wxGridSizer(2, 5, 5);
 
-	TimesSizer->Add(LabelFrame, 1, wxALIGN_CENTER_VERTICAL);
-	TimesSizer->Add(JumpFrame, wxEXPAND);
+    TimesSizer->Add(LabelFrame, 1, wxALIGN_CENTER_VERTICAL);
+    TimesSizer->Add(JumpFrame, wxEXPAND);
 
-	TimesSizer->Add(LabelTime, 1, wxALIGN_CENTER_VERTICAL);
-	TimesSizer->Add(JumpTime, wxEXPAND);
+    TimesSizer->Add(LabelTime, 1, wxALIGN_CENTER_VERTICAL);
+    TimesSizer->Add(JumpTime, wxEXPAND);
 
-	auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL);
+    auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL);
 
-	// General layout
-	auto MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(TimesSizer, 0, wxALL | wxALIGN_CENTER, 5);
-	MainSizer->Add(ButtonSizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 5);
-	d.SetSizerAndFit(MainSizer);
-	d.CenterOnParent();
+    // General layout
+    auto MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(TimesSizer, 0, wxALL | wxALIGN_CENTER, 5);
+    MainSizer->Add(ButtonSizer, 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 5);
+    d.SetSizerAndFit(MainSizer);
+    d.CenterOnParent();
 
-	d.Bind(wxEVT_INIT_DIALOG, &DialogJumpTo::OnInitDialog, this);
-	d.Bind(wxEVT_TEXT_ENTER, &DialogJumpTo::OnOK, this);
-	d.Bind(wxEVT_BUTTON, &DialogJumpTo::OnOK, this, wxID_OK);
-	JumpTime->Bind(wxEVT_TEXT, &DialogJumpTo::OnEditTime, this);
-	JumpFrame->Bind(wxEVT_TEXT, &DialogJumpTo::OnEditFrame, this);
+    d.Bind(wxEVT_INIT_DIALOG, &DialogJumpTo::OnInitDialog, this);
+    d.Bind(wxEVT_TEXT_ENTER, &DialogJumpTo::OnOK, this);
+    d.Bind(wxEVT_BUTTON, &DialogJumpTo::OnOK, this, wxID_OK);
+    JumpTime->Bind(wxEVT_TEXT, &DialogJumpTo::OnEditTime, this);
+    JumpFrame->Bind(wxEVT_TEXT, &DialogJumpTo::OnEditFrame, this);
 }
 
-void DialogJumpTo::OnInitDialog(wxInitDialogEvent&) {
-	d.TransferDataToWindow();
-	d.UpdateWindowUI(wxUPDATE_UI_RECURSE);
+void DialogJumpTo::OnInitDialog(wxInitDialogEvent &)
+{
+    d.TransferDataToWindow();
+    d.UpdateWindowUI(wxUPDATE_UI_RECURSE);
 
-	// This can't simply be done in the constructor as the value hasn't been set yet
-	JumpFrame->SetFocus();
-	JumpFrame->SelectAll();
+    // This can't simply be done in the constructor as the value hasn't been set yet
+    JumpFrame->SetFocus();
+    JumpFrame->SelectAll();
 }
 
-void DialogJumpTo::OnOK(wxCommandEvent &) {
-	d.EndModal(0);
-	c->videoController->JumpToFrame(jumpframe);
+void DialogJumpTo::OnOK(wxCommandEvent &)
+{
+    d.EndModal(0);
+    c->videoController->JumpToFrame(jumpframe);
 }
 
-void DialogJumpTo::OnEditTime (wxCommandEvent &) {
-	long newframe = c->videoController->FrameAtTime(JumpTime->GetTime());
-	if (jumpframe != newframe) {
-		jumpframe = newframe;
-		JumpFrame->ChangeValue(fmt_wx("%d", jumpframe));
-	}
+void DialogJumpTo::OnEditTime (wxCommandEvent &)
+{
+    long newframe = c->videoController->FrameAtTime(JumpTime->GetTime());
+    if (jumpframe != newframe) {
+        jumpframe = newframe;
+        JumpFrame->ChangeValue(fmt_wx("%d", jumpframe));
+    }
 }
 
-void DialogJumpTo::OnEditFrame (wxCommandEvent &event) {
-	JumpFrame->GetValue().ToLong(&jumpframe);
-	JumpTime->SetTime(c->videoController->TimeAtFrame(jumpframe));
+void DialogJumpTo::OnEditFrame (wxCommandEvent &event)
+{
+    JumpFrame->GetValue().ToLong(&jumpframe);
+    JumpTime->SetTime(c->videoController->TimeAtFrame(jumpframe));
 }
 }
 
-void ShowJumpToDialog(agi::Context *c) {
-	DialogJumpTo(c).d.ShowModal();
+void ShowJumpToDialog(agi::Context *c)
+{
+    DialogJumpTo(c).d.ShowModal();
 }
diff --git a/src/dialog_kara_timing_copy.cpp b/src/dialog_kara_timing_copy.cpp
index 0c1891da40fe3f76a2622581de3d23b31f32b842..97bc42eff03a7c9798e7523aba28e05fb7991260 100644
--- a/src/dialog_kara_timing_copy.cpp
+++ b/src/dialog_kara_timing_copy.cpp
@@ -58,658 +58,656 @@ namespace {
 #define TEXT_LABEL_DEST _("Dest: ")
 
 class KaraokeLineMatchDisplay final : public wxControl {
-	typedef AssKaraoke::Syllable MatchSyllable;
+    typedef AssKaraoke::Syllable MatchSyllable;
 
-	struct MatchGroup {
-		std::vector<MatchSyllable> src;
-		std::string dst;
-		int last_render_width = 0;
-	};
+    struct MatchGroup {
+        std::vector<MatchSyllable> src;
+        std::string dst;
+        int last_render_width = 0;
+    };
 
-	std::vector<MatchGroup> matched_groups;
-	std::deque<MatchSyllable> unmatched_source;
-	std::string destination_str;
-	boost::locale::boundary::ssegment_index destination;
-	boost::locale::boundary::ssegment_index::iterator match_begin, match_end;
+    std::vector<MatchGroup> matched_groups;
+    std::deque<MatchSyllable> unmatched_source;
+    std::string destination_str;
+    boost::locale::boundary::ssegment_index destination;
+    boost::locale::boundary::ssegment_index::iterator match_begin, match_end;
 
-	int last_total_matchgroup_render_width;
+    int last_total_matchgroup_render_width;
 
-	size_t source_sel_length;
+    size_t source_sel_length;
 
-	void OnPaint(wxPaintEvent &event);
+    void OnPaint(wxPaintEvent &event);
 
-	wxString const& label_source = TEXT_LABEL_SOURCE;
-	wxString const& label_destination = TEXT_LABEL_DEST;
+    wxString const &label_source = TEXT_LABEL_SOURCE;
+    wxString const &label_destination = TEXT_LABEL_DEST;
 
 public:
-	/// Start processing a new line pair
-	void SetInputData(AssDialogue *src, AssDialogue *dst);
-	/// Build and return the output line from the matched syllables
-	std::string GetOutputLine() const;
-
-	/// Number of syllables not yet matched from source
-	size_t GetRemainingSource() const { return unmatched_source.size(); }
-	/// Number of characters not yet matched from destination
-	size_t GetRemainingDestination() const { return distance(match_end, destination.end()); }
-
-	// Adjust source and destination match lengths
-	void IncreaseSourceMatch();
-	void DecreaseSourceMatch();
-	void IncreseDestinationMatch();
-	void DecreaseDestinationMatch();
-	/// Attempt to treat source as Japanese romaji, destination as Japanese kana+kanji, and make an automatic match
-	void AutoMatchJapanese();
-	/// Accept current selection and save match
-	bool AcceptMatch();
-	/// Undo last match, adding it back to the unmatched input
-	bool UndoMatch();
-
-	KaraokeLineMatchDisplay(wxWindow *parent);
-
-	wxSize GetBestSize() const;
+    /// Start processing a new line pair
+    void SetInputData(AssDialogue *src, AssDialogue *dst);
+    /// Build and return the output line from the matched syllables
+    std::string GetOutputLine() const;
+
+    /// Number of syllables not yet matched from source
+    size_t GetRemainingSource() const { return unmatched_source.size(); }
+    /// Number of characters not yet matched from destination
+    size_t GetRemainingDestination() const { return distance(match_end, destination.end()); }
+
+    // Adjust source and destination match lengths
+    void IncreaseSourceMatch();
+    void DecreaseSourceMatch();
+    void IncreseDestinationMatch();
+    void DecreaseDestinationMatch();
+    /// Attempt to treat source as Japanese romaji, destination as Japanese kana+kanji, and make an automatic match
+    void AutoMatchJapanese();
+    /// Accept current selection and save match
+    bool AcceptMatch();
+    /// Undo last match, adding it back to the unmatched input
+    bool UndoMatch();
+
+    KaraokeLineMatchDisplay(wxWindow *parent);
+
+    wxSize GetBestSize() const;
 };
 
 KaraokeLineMatchDisplay::KaraokeLineMatchDisplay(wxWindow *parent)
-: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE|wxWANTS_CHARS)
+    : wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | wxWANTS_CHARS)
 {
-	InheritAttributes();
-	SetInputData(nullptr, nullptr);
+    InheritAttributes();
+    SetInputData(nullptr, nullptr);
 
-	wxSize best_size = GetBestSize();
-	SetMaxSize(wxSize(-1, best_size.GetHeight()));
-	SetMinSize(best_size);
+    wxSize best_size = GetBestSize();
+    SetMaxSize(wxSize(-1, best_size.GetHeight()));
+    SetMinSize(best_size);
 
-	Bind(wxEVT_SET_FOCUS, std::bind(&wxControl::Refresh, this, true, nullptr));
-	Bind(wxEVT_KILL_FOCUS, std::bind(&wxControl::Refresh, this, true, nullptr));
-	Bind(wxEVT_PAINT, &KaraokeLineMatchDisplay::OnPaint, this);
+    Bind(wxEVT_SET_FOCUS, std::bind(&wxControl::Refresh, this, true, nullptr));
+    Bind(wxEVT_KILL_FOCUS, std::bind(&wxControl::Refresh, this, true, nullptr));
+    Bind(wxEVT_PAINT, &KaraokeLineMatchDisplay::OnPaint, this);
 }
 
 wxSize KaraokeLineMatchDisplay::GetBestSize() const
 {
-	int w_src, h_src, w_dst, h_dst;
-	GetTextExtent(label_source, &w_src, &h_src);
-	GetTextExtent(label_destination, &w_dst, &h_dst);
+    int w_src, h_src, w_dst, h_dst;
+    GetTextExtent(label_source, &w_src, &h_src);
+    GetTextExtent(label_destination, &w_dst, &h_dst);
 
-	int min_width = std::max(w_dst, w_src);
+    int min_width = std::max(w_dst, w_src);
 
-	// Magic number 7:
-	// 1 pixel for top black border, 1 pixel white top border in first row, 1 pixel white bottom padding in first row
-	// 1 pixel middle border, 1 pixel top and 1 pixel bottom padding in second row, 1 pixel bottom border
-	return wxSize(min_width * 2, h_src + h_dst + 7);
+    // Magic number 7:
+    // 1 pixel for top black border, 1 pixel white top border in first row, 1 pixel white bottom padding in first row
+    // 1 pixel middle border, 1 pixel top and 1 pixel bottom padding in second row, 1 pixel bottom border
+    return wxSize(min_width * 2, h_src + h_dst + 7);
 }
 
-int DrawBoxedText(wxDC &dc, wxString const& txt, int x, int y)
+int DrawBoxedText(wxDC &dc, wxString const &txt, int x, int y)
 {
-	int tw, th;
-	// Assume the pen, brush and font properties have already been set in the DC.
-	// Return the advance width, including box margins, borders etc
-
-	if (txt.empty())
-	{
-		// Empty string gets special handling:
-		// The box is drawn in shorter width, to emphasize it's empty
-		// GetTextExtent has to be called with a non-empty string, otherwise it returns the wrong height
-		dc.GetTextExtent(" ", &tw, &th);
-		dc.DrawRectangle(x, y-2, 4, th+4);
-		return 3;
-	}
-	else
-	{
-		dc.GetTextExtent(txt, &tw, &th);
-		dc.DrawRectangle(x, y-2, tw+4, th+4);
-		dc.DrawText(txt, x+2, y);
-		return tw+3;
-	}
+    int tw, th;
+    // Assume the pen, brush and font properties have already been set in the DC.
+    // Return the advance width, including box margins, borders etc
+
+    if (txt.empty()) {
+        // Empty string gets special handling:
+        // The box is drawn in shorter width, to emphasize it's empty
+        // GetTextExtent has to be called with a non-empty string, otherwise it returns the wrong height
+        dc.GetTextExtent(" ", &tw, &th);
+        dc.DrawRectangle(x, y - 2, 4, th + 4);
+        return 3;
+    }
+    else {
+        dc.GetTextExtent(txt, &tw, &th);
+        dc.DrawRectangle(x, y - 2, tw + 4, th + 4);
+        dc.DrawText(txt, x + 2, y);
+        return tw + 3;
+    }
 }
 
 void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &)
 {
-	wxPaintDC dc(this);
-
-	wxColour outer_text(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
-	wxColour outer_back(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
-	wxColour outer_frame(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
-	wxColour inner_back(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-	wxColour inner_text(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
-	wxColour sel_back(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
-	wxColour sel_text(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
-
-	// Y coordinates of the top and bottom lines
-	int y_line1, y_line2, y_line3;
-	// Next X coordinate to draw a matched syllable at
-	int next_x;
-
-	wxSize client_size = GetClientSize();
-
-	// Calculate the text line positions
-	{
-		int x, y, x2;
-		GetTextExtent(label_source, &x, &y);
-		y_line1 = 2;
-		y_line2 = y_line1 + y + 3;
-		y_line3 = y_line2 + y + 3;
-		GetTextExtent(label_destination, &x2, &y);
-		next_x = (x2 > x) ? x2 : x;
-		next_x += 3;
-	}
-
-	// Paint the labels
-	dc.SetTextBackground(outer_back);
-	if (FindFocus() == this)
-		dc.SetTextForeground(outer_text);
-	else
-		dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
-	dc.SetFont(GetFont());
-	dc.SetBackgroundMode(wxTRANSPARENT);
-	dc.DrawText(label_source, wxPoint(0, y_line1));
-	dc.DrawText(label_destination, wxPoint(0, y_line2));
-
-	// Horizontal lines through the width of the control
-	dc.SetPen(wxPen(outer_frame));
-	dc.DrawLine(next_x-1, y_line1-2, client_size.GetWidth(), y_line1-2);
-	dc.DrawLine(next_x-1, y_line2-2, client_size.GetWidth(), y_line2-2);
-	dc.DrawLine(next_x-1, y_line3-2, client_size.GetWidth(), y_line3-2);
-
-	// Draw matched groups
-	int this_total_matchgroup_render_width = 0;
-	bool scroll_arrows_drawn = false;
-	for (auto& grp : matched_groups)
-	{
-		int prev_x = next_x;
-
-		// Skip groups that would cause the input part to be too far right
-		this_total_matchgroup_render_width += grp.last_render_width;
-		if (last_total_matchgroup_render_width - this_total_matchgroup_render_width > client_size.x / 2)
-		{
-			// If we're skipping some syllables, show an arrow as feedback that something is scrolled off
-			if (!scroll_arrows_drawn)
-			{
-				dc.SetBrush(wxBrush(outer_frame));
-				wxPoint triangle[3];
-				int height = y_line2 - y_line1;
-				triangle[0] = wxPoint(next_x-3, height/2);
-				triangle[1] = wxPoint(next_x-3+height/2, 0);
-				triangle[2] = wxPoint(next_x-3+height/2, height);
-				dc.DrawPolygon(3, triangle, 0, 0);
-				dc.DrawPolygon(3, triangle, 0, height);
-				next_x += height/2 - 4;
-			}
-			scroll_arrows_drawn = true;
-			continue;
-		}
-
-		dc.SetPen(wxPen(outer_frame));
-		dc.SetBrush(wxBrush(inner_back));
-		dc.SetTextBackground(inner_back);
-		dc.SetTextForeground(inner_text);
-
-		// 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);
-
-		// Matched destination text
-		{
-			const int adv = DrawBoxedText(dc, to_wx(grp.dst), next_x, y_line2);
-
-			// Adjust next_x here while we have the text_w
-			next_x = syl_x > next_x + adv ? syl_x : next_x + adv;
-		}
-
-		// Spacing between groups
-		next_x += 3;
-		grp.last_render_width = next_x - prev_x;
-	}
-
-	last_total_matchgroup_render_width = this_total_matchgroup_render_width;
-
-	// Spacing between grouped and ungrouped parts
-	next_x += 4;
-
-	// Remaining source syllables
-	int syl_x = next_x;
-	// Start out with highlight colours
-	dc.SetTextBackground(sel_back);
-	dc.SetTextForeground(sel_text);
-	dc.SetBrush(wxBrush(sel_back));
-	for (size_t j = 0; j < unmatched_source.size(); ++j)
-	{
-		// Switch to regular colours after all selected syllables
-		if (j == source_sel_length)
-		{
-			dc.SetTextBackground(inner_back);
-			dc.SetTextForeground(inner_text);
-			dc.SetBrush(wxBrush(inner_back));
-		}
-
-		syl_x += DrawBoxedText(dc, to_wx(unmatched_source[j].text), syl_x, y_line1);
-	}
-
-	// Remaining destination
-	if (match_begin != match_end)
-	{
-		dc.SetTextBackground(sel_back);
-		dc.SetTextForeground(sel_text);
-		dc.SetBrush(wxBrush(sel_back));
-		wxString str;
-		for (auto it = match_begin; it != match_end; ++it)
-			str += to_wx(it->str());
-		next_x += DrawBoxedText(dc, str, next_x, y_line2);
-	}
-
-	if (match_end != destination.end())
-	{
-		dc.SetTextBackground(inner_back);
-		dc.SetTextForeground(inner_text);
-		dc.SetBrush(wxBrush(inner_back));
-		wxString str;
-		for (auto it = match_end; it != destination.end(); ++it)
-			str += to_wx(it->str());
-		DrawBoxedText(dc, str, next_x, y_line2);
-	}
+    wxPaintDC dc(this);
+
+    wxColour outer_text(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
+    wxColour outer_back(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
+    wxColour outer_frame(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
+    wxColour inner_back(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+    wxColour inner_text(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+    wxColour sel_back(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+    wxColour sel_text(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
+
+    // Y coordinates of the top and bottom lines
+    int y_line1, y_line2, y_line3;
+    // Next X coordinate to draw a matched syllable at
+    int next_x;
+
+    wxSize client_size = GetClientSize();
+
+    // Calculate the text line positions
+    {
+        int x, y, x2;
+        GetTextExtent(label_source, &x, &y);
+        y_line1 = 2;
+        y_line2 = y_line1 + y + 3;
+        y_line3 = y_line2 + y + 3;
+        GetTextExtent(label_destination, &x2, &y);
+        next_x = (x2 > x) ? x2 : x;
+        next_x += 3;
+    }
+
+    // Paint the labels
+    dc.SetTextBackground(outer_back);
+    if (FindFocus() == this)
+        dc.SetTextForeground(outer_text);
+    else
+        dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+    dc.SetFont(GetFont());
+    dc.SetBackgroundMode(wxTRANSPARENT);
+    dc.DrawText(label_source, wxPoint(0, y_line1));
+    dc.DrawText(label_destination, wxPoint(0, y_line2));
+
+    // Horizontal lines through the width of the control
+    dc.SetPen(wxPen(outer_frame));
+    dc.DrawLine(next_x - 1, y_line1 - 2, client_size.GetWidth(), y_line1 - 2);
+    dc.DrawLine(next_x - 1, y_line2 - 2, client_size.GetWidth(), y_line2 - 2);
+    dc.DrawLine(next_x - 1, y_line3 - 2, client_size.GetWidth(), y_line3 - 2);
+
+    // Draw matched groups
+    int this_total_matchgroup_render_width = 0;
+    bool scroll_arrows_drawn = false;
+    for (auto &grp : matched_groups) {
+        int prev_x = next_x;
+
+        // Skip groups that would cause the input part to be too far right
+        this_total_matchgroup_render_width += grp.last_render_width;
+        if (last_total_matchgroup_render_width - this_total_matchgroup_render_width > client_size.x / 2) {
+            // If we're skipping some syllables, show an arrow as feedback that something is scrolled off
+            if (!scroll_arrows_drawn) {
+                dc.SetBrush(wxBrush(outer_frame));
+                wxPoint triangle[3];
+                int height = y_line2 - y_line1;
+                triangle[0] = wxPoint(next_x - 3, height / 2);
+                triangle[1] = wxPoint(next_x - 3 + height / 2, 0);
+                triangle[2] = wxPoint(next_x - 3 + height / 2, height);
+                dc.DrawPolygon(3, triangle, 0, 0);
+                dc.DrawPolygon(3, triangle, 0, height);
+                next_x += height / 2 - 4;
+            }
+            scroll_arrows_drawn = true;
+            continue;
+        }
+
+        dc.SetPen(wxPen(outer_frame));
+        dc.SetBrush(wxBrush(inner_back));
+        dc.SetTextBackground(inner_back);
+        dc.SetTextForeground(inner_text);
+
+        // 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);
+
+        // Matched destination text
+        {
+            const int adv = DrawBoxedText(dc, to_wx(grp.dst), next_x, y_line2);
+
+            // Adjust next_x here while we have the text_w
+            next_x = syl_x > next_x + adv ? syl_x : next_x + adv;
+        }
+
+        // Spacing between groups
+        next_x += 3;
+        grp.last_render_width = next_x - prev_x;
+    }
+
+    last_total_matchgroup_render_width = this_total_matchgroup_render_width;
+
+    // Spacing between grouped and ungrouped parts
+    next_x += 4;
+
+    // Remaining source syllables
+    int syl_x = next_x;
+    // Start out with highlight colours
+    dc.SetTextBackground(sel_back);
+    dc.SetTextForeground(sel_text);
+    dc.SetBrush(wxBrush(sel_back));
+    for (size_t j = 0; j < unmatched_source.size(); ++j) {
+        // Switch to regular colours after all selected syllables
+        if (j == source_sel_length) {
+            dc.SetTextBackground(inner_back);
+            dc.SetTextForeground(inner_text);
+            dc.SetBrush(wxBrush(inner_back));
+        }
+
+        syl_x += DrawBoxedText(dc, to_wx(unmatched_source[j].text), syl_x, y_line1);
+    }
+
+    // Remaining destination
+    if (match_begin != match_end) {
+        dc.SetTextBackground(sel_back);
+        dc.SetTextForeground(sel_text);
+        dc.SetBrush(wxBrush(sel_back));
+        wxString str;
+        for (auto it = match_begin; it != match_end; ++it)
+            str += to_wx(it->str());
+        next_x += DrawBoxedText(dc, str, next_x, y_line2);
+    }
+
+    if (match_end != destination.end()) {
+        dc.SetTextBackground(inner_back);
+        dc.SetTextForeground(inner_text);
+        dc.SetBrush(wxBrush(inner_back));
+        wxString str;
+        for (auto it = match_end; it != destination.end(); ++it)
+            str += to_wx(it->str());
+        DrawBoxedText(dc, str, next_x, y_line2);
+    }
 }
 
 void KaraokeLineMatchDisplay::SetInputData(AssDialogue *src, AssDialogue *dst)
 {
-	last_total_matchgroup_render_width = 0;
-
-	matched_groups.clear();
-
-	unmatched_source.clear();
-	source_sel_length = 0;
-	if (src)
-	{
-		AssKaraoke kara(src);
-		copy(kara.begin(), kara.end(), back_inserter(unmatched_source));
-		source_sel_length = 1;
-	}
-
-	destination_str = dst ? dst->GetStrippedText() : "";
-	using namespace boost::locale::boundary;
-	destination = ssegment_index(character, begin(destination_str), end(destination_str));
-	match_begin = match_end = destination.begin();
-	if (!destination_str.empty())
-		++match_end;
-
-	Refresh(true);
+    last_total_matchgroup_render_width = 0;
+
+    matched_groups.clear();
+
+    unmatched_source.clear();
+    source_sel_length = 0;
+    if (src) {
+        AssKaraoke kara(src);
+        copy(kara.begin(), kara.end(), back_inserter(unmatched_source));
+        source_sel_length = 1;
+    }
+
+    destination_str = dst ? dst->GetStrippedText() : "";
+    using namespace boost::locale::boundary;
+    destination = ssegment_index(character, begin(destination_str), end(destination_str));
+    match_begin = match_end = destination.begin();
+    if (!destination_str.empty())
+        ++match_end;
+
+    Refresh(true);
 }
 
 std::string KaraokeLineMatchDisplay::GetOutputLine() const
 {
-	std::string res;
+    std::string res;
 
-	for (auto const& match : matched_groups)
-	{
-		int duration = 0;
-		for (auto const& syl : match.src)
-			duration += syl.duration;
-		res += "{\\k" + std::to_string(duration / 10) + "}" + match.dst;
-	}
+    for (auto const &match : matched_groups) {
+        int duration = 0;
+        for (auto const &syl : match.src)
+            duration += syl.duration;
+        res += "{\\k" + std::to_string(duration / 10) + "}" + match.dst;
+    }
 
-	return res;
+    return res;
 }
 
 void KaraokeLineMatchDisplay::IncreaseSourceMatch()
 {
-	source_sel_length = std::min(source_sel_length + 1, GetRemainingSource());
-	Refresh(true);
+    source_sel_length = std::min(source_sel_length + 1, GetRemainingSource());
+    Refresh(true);
 }
 
 void KaraokeLineMatchDisplay::DecreaseSourceMatch()
 {
-	source_sel_length = std::max<size_t>(source_sel_length, 1) - 1;
-	Refresh(true);
+    source_sel_length = std::max<size_t>(source_sel_length, 1) - 1;
+    Refresh(true);
 }
 
 void KaraokeLineMatchDisplay::IncreseDestinationMatch()
 {
-	if (match_end != destination.end()) {
-		++match_end;
-		Refresh(true);
-	}
+    if (match_end != destination.end()) {
+        ++match_end;
+        Refresh(true);
+    }
 }
 
 void KaraokeLineMatchDisplay::DecreaseDestinationMatch()
 {
-	if (match_end != match_begin) {
-		--match_end;
-		Refresh(true);
-	}
+    if (match_end != match_begin) {
+        --match_end;
+        Refresh(true);
+    }
 }
 
 void KaraokeLineMatchDisplay::AutoMatchJapanese()
 {
-	std::vector<std::string> source;
-	for (auto const& syl : unmatched_source)
-		source.emplace_back(syl.text);
-	auto result = agi::auto_match_karaoke(source, match_begin == destination.end() ? "" : &*match_begin->begin());
-	source_sel_length = result.source_length;
-	match_end = std::next(match_begin, result.destination_length);
+    std::vector<std::string> source;
+    for (auto const &syl : unmatched_source)
+        source.emplace_back(syl.text);
+    auto result = agi::auto_match_karaoke(source, match_begin == destination.end() ? "" : &*match_begin->begin());
+    source_sel_length = result.source_length;
+    match_end = std::next(match_begin, result.destination_length);
 }
 
 bool KaraokeLineMatchDisplay::AcceptMatch()
 {
-	// Completely empty match
-	if (source_sel_length == 0 && match_begin == match_end) return false;
+    // Completely empty match
+    if (source_sel_length == 0 && match_begin == match_end) return false;
 
-	MatchGroup match;
+    MatchGroup match;
 
-	assert(source_sel_length <= unmatched_source.size());
-	copy(unmatched_source.begin(), unmatched_source.begin() + source_sel_length, back_inserter(match.src));
-	unmatched_source.erase(unmatched_source.begin(), unmatched_source.begin() + source_sel_length);
-	source_sel_length = 0;
+    assert(source_sel_length <= unmatched_source.size());
+    copy(unmatched_source.begin(), unmatched_source.begin() + source_sel_length, back_inserter(match.src));
+    unmatched_source.erase(unmatched_source.begin(), unmatched_source.begin() + source_sel_length);
+    source_sel_length = 0;
 
-	match.dst = std::string(match_begin->begin(), match_end == destination.end() ? destination_str.end() : match_end->begin());
-	match_begin = match_end;
+    match.dst = std::string(match_begin->begin(), match_end == destination.end() ? destination_str.end() : match_end->begin());
+    match_begin = match_end;
 
-	matched_groups.emplace_back(std::move(match));
+    matched_groups.emplace_back(std::move(match));
 
-	IncreaseSourceMatch();
-	IncreseDestinationMatch();
-	Refresh(true);
+    IncreaseSourceMatch();
+    IncreseDestinationMatch();
+    Refresh(true);
 
-	return true;
+    return true;
 }
 
 bool KaraokeLineMatchDisplay::UndoMatch()
 {
-	if (matched_groups.empty())
-		return false;
+    if (matched_groups.empty())
+        return false;
 
-	MatchGroup &group = matched_groups.back();
+    MatchGroup &group = matched_groups.back();
 
-	source_sel_length = group.src.size();
-	copy(group.src.rbegin(), group.src.rend(), front_inserter(unmatched_source));
-	group.src.clear();
+    source_sel_length = group.src.size();
+    copy(group.src.rbegin(), group.src.rend(), front_inserter(unmatched_source));
+    group.src.clear();
 
-	match_end = match_begin;
-	for (size_t size = group.dst.size(); size > 0; size -= match_begin->length())
-		--match_begin;
+    match_end = match_begin;
+    for (size_t size = group.dst.size(); size > 0; size -= match_begin->length())
+        --match_begin;
 
-	matched_groups.pop_back();
+    matched_groups.pop_back();
 
-	Refresh(true);
+    Refresh(true);
 
-	return true;
+    return true;
 }
 
 class DialogKanjiTimer final : public wxDialog {
-	AssFile *subs;
+    AssFile *subs;
 
-	KaraokeLineMatchDisplay *display;
+    KaraokeLineMatchDisplay *display;
 
-	wxComboBox *SourceStyle, *DestStyle;
-	wxCheckBox *Interpolate;
+    wxComboBox *SourceStyle, *DestStyle;
+    wxCheckBox *Interpolate;
 
-	std::vector<std::pair<AssDialogue*, std::string>> LinesToChange;
+    std::vector<std::pair<AssDialogue *, std::string>> LinesToChange;
 
-	AssDialogue *currentSourceLine = nullptr;
-	AssDialogue *currentDestinationLine = nullptr;
+    AssDialogue *currentSourceLine = nullptr;
+    AssDialogue *currentDestinationLine = nullptr;
 
-	void OnClose(wxCommandEvent &event);
-	void OnStart(wxCommandEvent &event);
-	void OnLink(wxCommandEvent &event);
-	void OnUnlink(wxCommandEvent &event);
-	void OnSkipSource(wxCommandEvent &event);
-	void OnSkipDest(wxCommandEvent &event);
-	void OnGoBack(wxCommandEvent &event);
-	void OnAccept(wxCommandEvent &event);
-	void OnKeyDown(wxKeyEvent &event);
+    void OnClose(wxCommandEvent &event);
+    void OnStart(wxCommandEvent &event);
+    void OnLink(wxCommandEvent &event);
+    void OnUnlink(wxCommandEvent &event);
+    void OnSkipSource(wxCommandEvent &event);
+    void OnSkipDest(wxCommandEvent &event);
+    void OnGoBack(wxCommandEvent &event);
+    void OnAccept(wxCommandEvent &event);
+    void OnKeyDown(wxKeyEvent &event);
 
-	void ResetForNewLine();
-	void TryAutoMatch();
+    void ResetForNewLine();
+    void TryAutoMatch();
 
-	AssDialogue *FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style);
-	AssDialogue *FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style);
+    AssDialogue *FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style);
+    AssDialogue *FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style);
 
 public:
-	DialogKanjiTimer(agi::Context *context);
+    DialogKanjiTimer(agi::Context *context);
 };
 
 DialogKanjiTimer::DialogKanjiTimer(agi::Context *c)
-: wxDialog(c->parent, -1, _("Kanji timing"))
-, subs(c->ass.get())
+    : wxDialog(c->parent, -1, _("Kanji timing"))
+    , subs(c->ass.get())
 {
-	SetIcon(GETICON(kara_timing_copier_16));
-
-	wxSizer *DisplayBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Text"));
-	wxSizer *StylesBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Styles"));
-	auto StylesGridSizer = new wxFlexGridSizer(2, 2, 6, 6);
-	wxSizer *HelpBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Shortcut Keys"));
-	wxSizer *ButtonsBoxSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Commands"));
-	wxSizer *MainStackSizer = new wxBoxSizer(wxVERTICAL);
-	wxSizer *BottomShelfSizer = new wxBoxSizer(wxHORIZONTAL);
-	wxSizer *BottomLeftStackSizer = new wxBoxSizer(wxVERTICAL);
-
-	display = new KaraokeLineMatchDisplay(this);
-
-	//Checkbox
-	Interpolate = new wxCheckBox(this,-1,_("Attempt to &interpolate kanji."),wxDefaultPosition,wxDefaultSize,wxALIGN_LEFT);
-	Interpolate->SetValue(OPT_GET("Tool/Kanji Timer/Interpolation")->GetBool());
-
-	wxArrayString styles = to_wx(subs->GetStyles());
-	SourceStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
-	DestStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
-
-	wxStaticText *ShortcutKeys = new wxStaticText(this,-1,_("When the destination textbox has focus, use the following keys:\n\nRight Arrow: Increase dest. selection length\nLeft Arrow: Decrease dest. selection length\nUp Arrow: Increase source selection length\nDown Arrow: Decrease source selection length\nEnter: Link, accept line when done\nBackspace: Unlink last"));
-
-	//Buttons
-	wxButton *Start = new wxButton(this, -1,_("S&tart!"));
-	wxButton *Link = new wxButton(this, -1,_("&Link"));
-	wxButton *Unlink = new wxButton(this, -1,_("&Unlink"));
-	wxButton *SkipSourceLine = new wxButton(this, -1,_("Skip &Source Line"));
-	wxButton *SkipDestLine = new wxButton(this, -1,_("Skip &Dest Line"));
-	wxButton *GoBackLine = new wxButton(this, -1,_("&Go Back a Line"));
-	wxButton *AcceptLine = new wxButton(this, -1,_("&Accept Line"));
-	wxButton *CloseKT = new wxButton(this,wxID_CLOSE,_("&Close"));
-
-	//Frame: Text
-	DisplayBoxSizer->Add(display, 0, wxEXPAND|wxALL, 6);
-	DisplayBoxSizer->Add(Interpolate, 0, wxEXPAND|wxALL, 6);
-	//Frame: Styles
-	StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_SOURCE), 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
-	StylesGridSizer->Add(SourceStyle, 1, wxEXPAND);
-	StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_DEST), 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
-	StylesGridSizer->Add(DestStyle, 1, wxEXPAND);
-	StylesBoxSizer->Add(StylesGridSizer, 1, wxEXPAND|wxALL, 6);
-	//Frame: Shortcut Keys
-	HelpBoxSizer->Add(ShortcutKeys, 1, wxALIGN_CENTER_HORIZONTAL|wxRIGHT, 6);
-	//Frame: Commands
-	ButtonsBoxSizer->AddStretchSpacer(1);
-	ButtonsBoxSizer->Add(Start, 0, wxEXPAND|wxALL, 6);
-	ButtonsBoxSizer->Add(Link, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->Add(Unlink, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->Add(SkipSourceLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->Add(SkipDestLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->Add(GoBackLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->Add(AcceptLine, 0, wxEXPAND|(wxALL&~wxTOP), 6);
-	ButtonsBoxSizer->AddStretchSpacer(1);
-
-	// Button sizer
-	auto buttonSizer = new wxStdDialogButtonSizer();
-	buttonSizer->AddButton(new HelpButton(this,"Kanji Timer"));
-	buttonSizer->SetAffirmativeButton(CloseKT);
-	buttonSizer->Realize();
-
-	// Layout it all
-	BottomLeftStackSizer->Add(StylesBoxSizer, 0, wxEXPAND|wxBOTTOM, 6);
-	BottomLeftStackSizer->Add(HelpBoxSizer, 1, wxEXPAND, 6);
-	BottomShelfSizer->Add(BottomLeftStackSizer, 1, wxEXPAND|wxRIGHT, 6);
-	BottomShelfSizer->Add(ButtonsBoxSizer, 0, wxEXPAND, 6);
-	MainStackSizer->Add(DisplayBoxSizer, 0, wxEXPAND|wxALL, 6);
-	MainStackSizer->Add(BottomShelfSizer, 1, wxEXPAND|wxLEFT|wxRIGHT, 6);
-	MainStackSizer->Add(buttonSizer, 0, wxEXPAND|wxALL, 6);
-
-	SetSizerAndFit(MainStackSizer);
-	CenterOnParent();
-
-	Bind(wxEVT_KEY_DOWN, &DialogKanjiTimer::OnKeyDown, this);
-	display->Bind(wxEVT_KEY_DOWN, &DialogKanjiTimer::OnKeyDown, this);
-
-	CloseKT->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnClose, this);
-	Start->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnStart, this);
-	Link->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnLink, this);
-	Unlink->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnUnlink, this);
-	SkipSourceLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnSkipSource, this);
-	SkipDestLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnSkipDest, this);
-	GoBackLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnGoBack, this);
-	AcceptLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnAccept, this);
+    SetIcon(GETICON(kara_timing_copier_16));
+
+    wxSizer *DisplayBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Text"));
+    wxSizer *StylesBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Styles"));
+    auto StylesGridSizer = new wxFlexGridSizer(2, 2, 6, 6);
+    wxSizer *HelpBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Shortcut Keys"));
+    wxSizer *ButtonsBoxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Commands"));
+    wxSizer *MainStackSizer = new wxBoxSizer(wxVERTICAL);
+    wxSizer *BottomShelfSizer = new wxBoxSizer(wxHORIZONTAL);
+    wxSizer *BottomLeftStackSizer = new wxBoxSizer(wxVERTICAL);
+
+    display = new KaraokeLineMatchDisplay(this);
+
+    //Checkbox
+    Interpolate = new wxCheckBox(this, -1, _("Attempt to &interpolate kanji."), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
+    Interpolate->SetValue(OPT_GET("Tool/Kanji Timer/Interpolation")->GetBool());
+
+    wxArrayString styles = to_wx(subs->GetStyles());
+    SourceStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
+    DestStyle = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(160, -1), styles, wxCB_READONLY);
+
+    wxStaticText *ShortcutKeys = new wxStaticText(this, -1, _("When the destination textbox has focus, use the following keys:\n\nRight Arrow: Increase dest. selection length\nLeft Arrow: Decrease dest. selection length\nUp Arrow: Increase source selection length\nDown Arrow: Decrease source selection length\nEnter: Link, accept line when done\nBackspace: Unlink last"));
+
+    //Buttons
+    wxButton *Start = new wxButton(this, -1, _("S&tart!"));
+    wxButton *Link = new wxButton(this, -1, _("&Link"));
+    wxButton *Unlink = new wxButton(this, -1, _("&Unlink"));
+    wxButton *SkipSourceLine = new wxButton(this, -1, _("Skip &Source Line"));
+    wxButton *SkipDestLine = new wxButton(this, -1, _("Skip &Dest Line"));
+    wxButton *GoBackLine = new wxButton(this, -1, _("&Go Back a Line"));
+    wxButton *AcceptLine = new wxButton(this, -1, _("&Accept Line"));
+    wxButton *CloseKT = new wxButton(this, wxID_CLOSE, _("&Close"));
+
+    //Frame: Text
+    DisplayBoxSizer->Add(display, 0, wxEXPAND | wxALL, 6);
+    DisplayBoxSizer->Add(Interpolate, 0, wxEXPAND | wxALL, 6);
+    //Frame: Styles
+    StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_SOURCE), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+    StylesGridSizer->Add(SourceStyle, 1, wxEXPAND);
+    StylesGridSizer->Add(new wxStaticText(this, -1, TEXT_LABEL_DEST), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+    StylesGridSizer->Add(DestStyle, 1, wxEXPAND);
+    StylesBoxSizer->Add(StylesGridSizer, 1, wxEXPAND | wxALL, 6);
+    //Frame: Shortcut Keys
+    HelpBoxSizer->Add(ShortcutKeys, 1, wxALIGN_CENTER_HORIZONTAL | wxRIGHT, 6);
+    //Frame: Commands
+    ButtonsBoxSizer->AddStretchSpacer(1);
+    ButtonsBoxSizer->Add(Start, 0, wxEXPAND | wxALL, 6);
+    ButtonsBoxSizer->Add(Link, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->Add(Unlink, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->Add(SkipSourceLine, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->Add(SkipDestLine, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->Add(GoBackLine, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->Add(AcceptLine, 0, wxEXPAND | (wxALL & ~wxTOP), 6);
+    ButtonsBoxSizer->AddStretchSpacer(1);
+
+    // Button sizer
+    auto buttonSizer = new wxStdDialogButtonSizer();
+    buttonSizer->AddButton(new HelpButton(this, "Kanji Timer"));
+    buttonSizer->SetAffirmativeButton(CloseKT);
+    buttonSizer->Realize();
+
+    // Layout it all
+    BottomLeftStackSizer->Add(StylesBoxSizer, 0, wxEXPAND | wxBOTTOM, 6);
+    BottomLeftStackSizer->Add(HelpBoxSizer, 1, wxEXPAND, 6);
+    BottomShelfSizer->Add(BottomLeftStackSizer, 1, wxEXPAND | wxRIGHT, 6);
+    BottomShelfSizer->Add(ButtonsBoxSizer, 0, wxEXPAND, 6);
+    MainStackSizer->Add(DisplayBoxSizer, 0, wxEXPAND | wxALL, 6);
+    MainStackSizer->Add(BottomShelfSizer, 1, wxEXPAND | wxLEFT | wxRIGHT, 6);
+    MainStackSizer->Add(buttonSizer, 0, wxEXPAND | wxALL, 6);
+
+    SetSizerAndFit(MainStackSizer);
+    CenterOnParent();
+
+    Bind(wxEVT_KEY_DOWN, &DialogKanjiTimer::OnKeyDown, this);
+    display->Bind(wxEVT_KEY_DOWN, &DialogKanjiTimer::OnKeyDown, this);
+
+    CloseKT->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnClose, this);
+    Start->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnStart, this);
+    Link->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnLink, this);
+    Unlink->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnUnlink, this);
+    SkipSourceLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnSkipSource, this);
+    SkipDestLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnSkipDest, this);
+    GoBackLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnGoBack, this);
+    AcceptLine->Bind(wxEVT_BUTTON, &DialogKanjiTimer::OnAccept, this);
 }
 
-void DialogKanjiTimer::OnClose(wxCommandEvent &) {
-	OPT_SET("Tool/Kanji Timer/Interpolation")->SetBool(Interpolate->IsChecked());
+void DialogKanjiTimer::OnClose(wxCommandEvent &)
+{
+    OPT_SET("Tool/Kanji Timer/Interpolation")->SetBool(Interpolate->IsChecked());
 
-	for (auto& line : LinesToChange)
-		line.first->Text = line.second;
+    for (auto &line : LinesToChange)
+        line.first->Text = line.second;
 
-	if (LinesToChange.size()) {
-		subs->Commit(_("kanji timing"), AssFile::COMMIT_DIAG_TEXT);
-		LinesToChange.clear();
-	}
-	Close();
+    if (LinesToChange.size()) {
+        subs->Commit(_("kanji timing"), AssFile::COMMIT_DIAG_TEXT);
+        LinesToChange.clear();
+    }
+    Close();
 }
 
-void DialogKanjiTimer::OnStart(wxCommandEvent &) {
-	if (SourceStyle->GetValue().empty() || DestStyle->GetValue().empty())
-		wxMessageBox(_("Select source and destination styles first."),_("Error"),wxICON_EXCLAMATION | wxOK);
-	else if (SourceStyle->GetValue() == DestStyle->GetValue())
-		wxMessageBox(_("The source and destination styles must be different."),_("Error"),wxICON_EXCLAMATION | wxOK);
-	else {
-		currentDestinationLine = currentSourceLine = &*subs->Events.begin();
-		auto sourceStyle = from_wx(SourceStyle->GetValue());
-		auto destStyle = from_wx(DestStyle->GetValue());
-		if (currentSourceLine->Style != sourceStyle)
-			currentSourceLine = FindNextStyleMatch(currentSourceLine, sourceStyle);
-		if (currentDestinationLine->Style != destStyle)
-			currentDestinationLine = FindNextStyleMatch(currentDestinationLine, destStyle);
-		ResetForNewLine();
-	}
-	LinesToChange.clear();
+void DialogKanjiTimer::OnStart(wxCommandEvent &)
+{
+    if (SourceStyle->GetValue().empty() || DestStyle->GetValue().empty())
+        wxMessageBox(_("Select source and destination styles first."), _("Error"), wxICON_EXCLAMATION | wxOK);
+    else if (SourceStyle->GetValue() == DestStyle->GetValue())
+        wxMessageBox(_("The source and destination styles must be different."), _("Error"), wxICON_EXCLAMATION | wxOK);
+    else {
+        currentDestinationLine = currentSourceLine = &*subs->Events.begin();
+        auto sourceStyle = from_wx(SourceStyle->GetValue());
+        auto destStyle = from_wx(DestStyle->GetValue());
+        if (currentSourceLine->Style != sourceStyle)
+            currentSourceLine = FindNextStyleMatch(currentSourceLine, sourceStyle);
+        if (currentDestinationLine->Style != destStyle)
+            currentDestinationLine = FindNextStyleMatch(currentDestinationLine, destStyle);
+        ResetForNewLine();
+    }
+    LinesToChange.clear();
 }
 
-void DialogKanjiTimer::OnLink(wxCommandEvent &) {
-	if (display->AcceptMatch())
-		TryAutoMatch();
-	else
-		wxBell();
+void DialogKanjiTimer::OnLink(wxCommandEvent &)
+{
+    if (display->AcceptMatch())
+        TryAutoMatch();
+    else
+        wxBell();
 }
 
-void DialogKanjiTimer::OnUnlink(wxCommandEvent &) {
-	if (!display->UndoMatch())
-		wxBell();
-	// Don't auto-match here, undoing sets the selection to the undone match
+void DialogKanjiTimer::OnUnlink(wxCommandEvent &)
+{
+    if (!display->UndoMatch())
+        wxBell();
+    // Don't auto-match here, undoing sets the selection to the undone match
 }
 
-void DialogKanjiTimer::OnSkipSource(wxCommandEvent &) {
-	currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
-	ResetForNewLine();
+void DialogKanjiTimer::OnSkipSource(wxCommandEvent &)
+{
+    currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
+    ResetForNewLine();
 }
 
-void DialogKanjiTimer::OnSkipDest(wxCommandEvent &) {
-	currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
-	ResetForNewLine();
+void DialogKanjiTimer::OnSkipDest(wxCommandEvent &)
+{
+    currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
+    ResetForNewLine();
 }
 
-void DialogKanjiTimer::OnGoBack(wxCommandEvent &) {
-	if (LinesToChange.size())
-		LinesToChange.pop_back(); //If we go back, then take out the modified line we saved.
+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, from_wx(SourceStyle->GetValue()));
-	currentDestinationLine = FindPrevStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
-	ResetForNewLine();
+    currentSourceLine = FindPrevStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
+    currentDestinationLine = FindPrevStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
+    ResetForNewLine();
 }
 
-void DialogKanjiTimer::OnAccept(wxCommandEvent &) {
-	if (!currentDestinationLine) return;
+void DialogKanjiTimer::OnAccept(wxCommandEvent &)
+{
+    if (!currentDestinationLine) return;
 
-	if (display->GetRemainingSource() > 0)
-		wxMessageBox(_("Group all of the source text."),_("Error"),wxICON_EXCLAMATION | wxOK);
-	else {
-		LinesToChange.emplace_back(currentDestinationLine, display->GetOutputLine());
+    if (display->GetRemainingSource() > 0)
+        wxMessageBox(_("Group all of the source text."), _("Error"), wxICON_EXCLAMATION | wxOK);
+    else {
+        LinesToChange.emplace_back(currentDestinationLine, display->GetOutputLine());
 
-		currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
-		currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
-		ResetForNewLine();
-	}
+        currentSourceLine = FindNextStyleMatch(currentSourceLine, from_wx(SourceStyle->GetValue()));
+        currentDestinationLine = FindNextStyleMatch(currentDestinationLine, from_wx(DestStyle->GetValue()));
+        ResetForNewLine();
+    }
 }
 
-void DialogKanjiTimer::OnKeyDown(wxKeyEvent &event) {
-	wxCommandEvent evt;
-	switch(event.GetKeyCode()) {
-		case WXK_ESCAPE :
-			OnClose(evt);
-			break;
-		case WXK_BACK :
-			OnUnlink(evt);
-			break;
-		case WXK_RIGHT : //inc dest selection len
-			display->IncreseDestinationMatch();
-			break;
-		case WXK_LEFT : //dec dest selection len
-			display->DecreaseDestinationMatch();
-			break;
-		case WXK_UP : //inc source selection len
-			display->IncreaseSourceMatch();
-			break;
-		case WXK_DOWN : //dec source selection len
-			display->DecreaseSourceMatch();
-			break;
-		case WXK_RETURN :
-			if (display->GetRemainingSource() == 0 && display->GetRemainingDestination() == 0)
-				OnAccept(evt);
-			else
-				OnLink(evt);
-			break;
-		default :
-			event.Skip();
-	}
+void DialogKanjiTimer::OnKeyDown(wxKeyEvent &event)
+{
+    wxCommandEvent evt;
+    switch (event.GetKeyCode()) {
+    case WXK_ESCAPE :
+        OnClose(evt);
+        break;
+    case WXK_BACK :
+        OnUnlink(evt);
+        break;
+    case WXK_RIGHT : //inc dest selection len
+        display->IncreseDestinationMatch();
+        break;
+    case WXK_LEFT : //dec dest selection len
+        display->DecreaseDestinationMatch();
+        break;
+    case WXK_UP : //inc source selection len
+        display->IncreaseSourceMatch();
+        break;
+    case WXK_DOWN : //dec source selection len
+        display->DecreaseSourceMatch();
+        break;
+    case WXK_RETURN :
+        if (display->GetRemainingSource() == 0 && display->GetRemainingDestination() == 0)
+            OnAccept(evt);
+        else
+            OnLink(evt);
+        break;
+    default :
+        event.Skip();
+    }
 }
 
 void DialogKanjiTimer::ResetForNewLine()
 {
-	if (!currentSourceLine || !currentDestinationLine)
-	{
-		currentSourceLine = currentDestinationLine = nullptr;
-		wxBell();
-	}
+    if (!currentSourceLine || !currentDestinationLine) {
+        currentSourceLine = currentDestinationLine = nullptr;
+        wxBell();
+    }
 
-	display->SetInputData(currentSourceLine, currentDestinationLine);
+    display->SetInputData(currentSourceLine, currentDestinationLine);
 
-	TryAutoMatch();
+    TryAutoMatch();
 
-	display->SetFocus();
+    display->SetFocus();
 }
 
 void DialogKanjiTimer::TryAutoMatch()
 {
-	if (Interpolate->GetValue())
-		display->AutoMatchJapanese();
+    if (Interpolate->GetValue())
+        display->AutoMatchJapanese();
 }
 
 template<typename Iterator>
-static AssDialogue *find_next(Iterator from, Iterator to, std::string const& style_name) {
-	for (; from != to; ++from)
-	{
-		if (from->Style == style_name && !from->Text.get().empty())
-			return &*from;
-	}
-
-	return nullptr;
+static AssDialogue *find_next(Iterator from, Iterator to, std::string const &style_name)
+{
+    for (; from != to; ++from) {
+        if (from->Style == style_name && !from->Text.get().empty())
+            return &*from;
+    }
+
+    return nullptr;
 }
 
 AssDialogue *DialogKanjiTimer::FindNextStyleMatch(AssDialogue *search_from, const std::string &search_style)
 {
-	if (!search_from) return search_from;
-	return find_next(++subs->iterator_to(*search_from), subs->Events.end(), search_style);
+    if (!search_from) return search_from;
+    return find_next(++subs->iterator_to(*search_from), subs->Events.end(), search_style);
 }
 
 AssDialogue *DialogKanjiTimer::FindPrevStyleMatch(AssDialogue *search_from, const std::string &search_style)
 {
-	if (!search_from) return search_from;
-	return find_next(EntryList<AssDialogue>::reverse_iterator(subs->iterator_to(*search_from)), subs->Events.rend(), search_style);
+    if (!search_from) return search_from;
+    return find_next(EntryList<AssDialogue>::reverse_iterator(subs->iterator_to(*search_from)), subs->Events.rend(), search_style);
 }
 }
 
-void ShowKanjiTimerDialog(agi::Context *c) {
-	DialogKanjiTimer(c).ShowModal();
+void ShowKanjiTimerDialog(agi::Context *c)
+{
+    DialogKanjiTimer(c).ShowModal();
 }
diff --git a/src/dialog_log.cpp b/src/dialog_log.cpp
index 96fad3fbd1e421d46a9de281ff50f9311b6ed1d1..efb8c56a225d7714bd7668d4f63b7deff7a0673c 100644
--- a/src/dialog_log.cpp
+++ b/src/dialog_log.cpp
@@ -44,77 +44,78 @@
 
 namespace {
 class EmitLog final : public agi::log::Emitter {
-	wxTextCtrl *text_ctrl;
+    wxTextCtrl *text_ctrl;
 public:
-	EmitLog(wxTextCtrl *t)
-	: text_ctrl(t)
-	{
-		for (auto const& sm : agi::log::log->GetMessages())
-			log(sm);
-	}
+    EmitLog(wxTextCtrl *t)
+        : text_ctrl(t) {
+        for (auto const &sm : agi::log::log->GetMessages())
+            log(sm);
+    }
 
-	void log(agi::log::SinkMessage const& sm) override {
-		time_t time = sm.time / 1000000000;
+    void log(agi::log::SinkMessage const &sm) override {
+        time_t time = sm.time / 1000000000;
 #ifndef _WIN32
-		tm tmtime;
-		localtime_r(&time, &tmtime);
-		auto log = fmt_wx("%c %02d:%02d:%02d %-6d <%-25s> [%s:%s:%d]  %s\n",
-			agi::log::Severity_ID[sm.severity],
-			tmtime.tm_hour,
-			tmtime.tm_min,
-			tmtime.tm_sec,
-			(sm.time % 1000000000),
-			sm.section,
-			sm.file,
-			sm.func,
-			sm.line,
-			sm.message);
+        tm tmtime;
+        localtime_r(&time, &tmtime);
+        auto log = fmt_wx("%c %02d:%02d:%02d %-6d <%-25s> [%s:%s:%d]  %s\n",
+                          agi::log::Severity_ID[sm.severity],
+                          tmtime.tm_hour,
+                          tmtime.tm_min,
+                          tmtime.tm_sec,
+                          (sm.time % 1000000000),
+                          sm.section,
+                          sm.file,
+                          sm.func,
+                          sm.line,
+                          sm.message);
 #else
-		auto log = fmt_wx("%c %-6ld.%09ld <%-25s> [%s:%s:%d]  %s\n",
-			agi::log::Severity_ID[sm.severity],
-			(sm.time / 1000000000),
-			(sm.time % 1000000000),
-			sm.section,
-			sm.file,
-			sm.func,
-			sm.line,
-			sm.message);
+        auto log = fmt_wx("%c %-6ld.%09ld <%-25s> [%s:%s:%d]  %s\n",
+                          agi::log::Severity_ID[sm.severity],
+                          (sm.time / 1000000000),
+                          (sm.time % 1000000000),
+                          sm.section,
+                          sm.file,
+                          sm.func,
+                          sm.line,
+                          sm.message);
 #endif
 
-		if (wxIsMainThread())
-			text_ctrl->AppendText(log);
-		else
-			agi::dispatch::Main().Async([=]{ text_ctrl->AppendText(log); });
-	}
+        if (wxIsMainThread())
+            text_ctrl->AppendText(log);
+        else
+            agi::dispatch::Main().Async([ = ] { text_ctrl->AppendText(log); });
+    }
 };
 
 class LogWindow : public wxDialog {
-	agi::log::Emitter *emit_log;
+    agi::log::Emitter *emit_log;
 
 public:
-	LogWindow(agi::Context *c);
-	~LogWindow();
+    LogWindow(agi::Context *c);
+    ~LogWindow();
 };
 
 LogWindow::LogWindow(agi::Context *c)
-: wxDialog(c->parent, -1, _("Log window"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER)
+    : wxDialog(c->parent, -1, _("Log window"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER)
 {
-	wxTextCtrl *text_ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(700,300), wxTE_MULTILINE|wxTE_READONLY);
-	text_ctrl->SetDefaultStyle(wxTextAttr(wxNullColour, wxNullColour, wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)));
+    wxTextCtrl *text_ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(700, 300), wxTE_MULTILINE | wxTE_READONLY);
+    text_ctrl->SetDefaultStyle(wxTextAttr(wxNullColour, wxNullColour, wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)));
 
-	wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
-	sizer->Add(text_ctrl, wxSizerFlags(1).Expand().Border());
-	sizer->Add(new wxButton(this, wxID_OK), wxSizerFlags(0).Border().Right());
-	SetSizerAndFit(sizer);
+    wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+    sizer->Add(text_ctrl, wxSizerFlags(1).Expand().Border());
+    sizer->Add(new wxButton(this, wxID_OK), wxSizerFlags(0).Border().Right());
+    SetSizerAndFit(sizer);
 
-	agi::log::log->Subscribe(std::unique_ptr<agi::log::Emitter>(emit_log = new EmitLog(text_ctrl)));
+    agi::log::log->Subscribe(std::unique_ptr<agi::log::Emitter>(emit_log = new EmitLog(text_ctrl)));
 }
 
-LogWindow::~LogWindow() {
-	agi::log::log->Unsubscribe(emit_log);
+LogWindow::~LogWindow()
+{
+    agi::log::log->Unsubscribe(emit_log);
 }
 }
 
-void ShowLogWindow(agi::Context *c) {
-	c->dialog->Show<LogWindow>(c);
+void ShowLogWindow(agi::Context *c)
+{
+    c->dialog->Show<LogWindow>(c);
 }
diff --git a/src/dialog_manager.h b/src/dialog_manager.h
index b81e2be6717632ad22aa3ecf7763ff7eaf007e25..5aaff60e4c7491362e8166177aac3ba8586ac9a3 100644
--- a/src/dialog_manager.h
+++ b/src/dialog_manager.h
@@ -30,90 +30,90 @@ namespace agi { struct Context; }
 /// created, so that commands can be send to the appropriate places and so that
 /// the same dialog can't be opened twice at once.
 class DialogManager {
-	using dialog_pair = std::pair<const std::type_info *, wxDialog *>;
-	/// Dialogs which currently exist
-	std::vector<dialog_pair> created_dialogs;
+    using dialog_pair = std::pair<const std::type_info *, wxDialog *>;
+    /// Dialogs which currently exist
+    std::vector<dialog_pair> created_dialogs;
 
-	/// Close handler which deletes and unregisters closed modeless dialogs
-	template<typename Event>
-	void OnClose(Event &evt) {
-		evt.Skip();
-		Destroy(static_cast<wxWindow *>(evt.GetEventObject()));
-	}
+    /// Close handler which deletes and unregisters closed modeless dialogs
+    template<typename Event>
+    void OnClose(Event &evt) {
+        evt.Skip();
+        Destroy(static_cast<wxWindow *>(evt.GetEventObject()));
+    }
 
-	void Destroy(wxWindow *dialog) {
-		while (!dialog->IsTopLevel()) dialog = dialog->GetParent();
-		dialog->Destroy();
+    void Destroy(wxWindow *dialog) {
+        while (!dialog->IsTopLevel()) dialog = dialog->GetParent();
+        dialog->Destroy();
 
-		for (auto it = created_dialogs.begin(); it != created_dialogs.end(); ++it) {
-			if (it->second == dialog) {
-				created_dialogs.erase(it);
-				return;
-			}
-		}
-	}
+        for (auto it = created_dialogs.begin(); it != created_dialogs.end(); ++it) {
+            if (it->second == dialog) {
+                created_dialogs.erase(it);
+                return;
+            }
+        }
+    }
 
-	std::vector<dialog_pair>::iterator Find(std::type_info const& type) {
-		for (auto it = begin(created_dialogs); it != end(created_dialogs); ++it) {
-			if (*it->first == type)
-				return it;
-		}
-		return end(created_dialogs);
-	}
+    std::vector<dialog_pair>::iterator Find(std::type_info const &type) {
+        for (auto it = begin(created_dialogs); it != end(created_dialogs); ++it) {
+            if (*it->first == type)
+                return it;
+        }
+        return end(created_dialogs);
+    }
 
 public:
-	/// Show a modeless dialog of the given type, creating it if needed
-	/// @tparam DialogType Type of dialog to show
-	template<class DialogType>
-	void Show(agi::Context *c) {
-		for (auto const& diag : created_dialogs) {
-			if (*diag.first == typeid(DialogType)) {
-				diag.second->Show();
-				diag.second->SetFocus();
-			}
-		}
+    /// Show a modeless dialog of the given type, creating it if needed
+    /// @tparam DialogType Type of dialog to show
+    template<class DialogType>
+    void Show(agi::Context *c) {
+        for (auto const &diag : created_dialogs) {
+            if (*diag.first == typeid(DialogType)) {
+                diag.second->Show();
+                diag.second->SetFocus();
+            }
+        }
 
-		try {
-			wxDialog *d = new DialogType(c);
-			created_dialogs.emplace_back(&typeid(DialogType), d);
-			d->Bind(wxEVT_CLOSE_WINDOW, &DialogManager::OnClose<wxCloseEvent>, this);
-			d->Bind(wxEVT_BUTTON, &DialogManager::OnClose<wxCommandEvent>, this, wxID_CANCEL);
-			d->Show();
-			SetFloatOnParent(d);
-		}
-		catch (agi::UserCancelException const&) { }
-	}
+        try {
+            wxDialog *d = new DialogType(c);
+            created_dialogs.emplace_back(&typeid(DialogType), d);
+            d->Bind(wxEVT_CLOSE_WINDOW, &DialogManager::OnClose<wxCloseEvent>, this);
+            d->Bind(wxEVT_BUTTON, &DialogManager::OnClose<wxCommandEvent>, this, wxID_CANCEL);
+            d->Show();
+            SetFloatOnParent(d);
+        }
+        catch (agi::UserCancelException const &) { }
+    }
 
-	/// Show a modal dialog of the given type, creating it if needed
-	/// @tparam DialogType Type of dialog to show
-	template<class DialogType>
-	void ShowModal(agi::Context *c) {
-		DialogType diag(c);
-		created_dialogs.emplace_back(&typeid(DialogType), &diag);
-		try {
-			diag.ShowModal();
-		}
-		catch (...) {
-			created_dialogs.erase(Find(typeid(DialogType)));
-			throw;
-		}
-		created_dialogs.erase(Find(typeid(DialogType)));
-	}
+    /// Show a modal dialog of the given type, creating it if needed
+    /// @tparam DialogType Type of dialog to show
+    template<class DialogType>
+    void ShowModal(agi::Context *c) {
+        DialogType diag(c);
+        created_dialogs.emplace_back(&typeid(DialogType), &diag);
+        try {
+            diag.ShowModal();
+        }
+        catch (...) {
+            created_dialogs.erase(Find(typeid(DialogType)));
+            throw;
+        }
+        created_dialogs.erase(Find(typeid(DialogType)));
+    }
 
-	/// Get the dialog of the given type
-	/// @tparam DialogType Type of dialog to get
-	/// @return A pointer to a DialogType or nullptr if no dialog of the given type has been created
-	template<class DialogType>
-	DialogType *Get() const {
-		auto it = const_cast<DialogManager *>(this)->Find(typeid(DialogType));
-		return it != created_dialogs.end() ? static_cast<DialogType*>(it->second) : nullptr;
-	}
+    /// Get the dialog of the given type
+    /// @tparam DialogType Type of dialog to get
+    /// @return A pointer to a DialogType or nullptr if no dialog of the given type has been created
+    template<class DialogType>
+    DialogType *Get() const {
+        auto it = const_cast<DialogManager *>(this)->Find(typeid(DialogType));
+        return it != created_dialogs.end() ? static_cast<DialogType *>(it->second) : nullptr;
+    }
 
-	~DialogManager() {
-		for (auto const& it : created_dialogs) {
-			it.second->Unbind(wxEVT_CLOSE_WINDOW, &DialogManager::OnClose<wxCloseEvent>, this);
-			it.second->Unbind(wxEVT_BUTTON, &DialogManager::OnClose<wxCommandEvent>, this, wxID_CANCEL);
-			it.second->Destroy();
-		}
-	}
+    ~DialogManager() {
+        for (auto const &it : created_dialogs) {
+            it.second->Unbind(wxEVT_CLOSE_WINDOW, &DialogManager::OnClose<wxCloseEvent>, this);
+            it.second->Unbind(wxEVT_BUTTON, &DialogManager::OnClose<wxCommandEvent>, this, wxID_CANCEL);
+            it.second->Destroy();
+        }
+    }
 };
diff --git a/src/dialog_paste_over.cpp b/src/dialog_paste_over.cpp
index 1bc0df8a531ed9bab5695ee67e526a6b2caa2fd2..e04fff2c675be5fc39a1eeda3370698e5cc693a3 100644
--- a/src/dialog_paste_over.cpp
+++ b/src/dialog_paste_over.cpp
@@ -39,101 +39,106 @@
 
 namespace {
 struct DialogPasteOver {
-	wxDialog d;
-	wxCheckListBox *ListBox;
+    wxDialog d;
+    wxCheckListBox *ListBox;
 
-	void CheckAll(bool check);
+    void CheckAll(bool check);
 
-	void OnOK(wxCommandEvent &);
-	void OnTimes(wxCommandEvent &);
-	void OnText(wxCommandEvent &);
+    void OnOK(wxCommandEvent &);
+    void OnTimes(wxCommandEvent &);
+    void OnText(wxCommandEvent &);
 
-	DialogPasteOver(wxWindow *parent);
+    DialogPasteOver(wxWindow *parent);
 };
 
 DialogPasteOver::DialogPasteOver(wxWindow *parent)
-: d(parent, -1, _("Select Fields to Paste Over"))
+    : d(parent, -1, _("Select Fields to Paste Over"))
 {
-	// Label and list sizer
-	wxSizer *ListSizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Fields"));
-	ListSizer->Add(new wxStaticText(&d, -1, _("Please select the fields that you want to paste over:")), wxSizerFlags());
-
-	// List box
-	wxArrayString choices;
-	choices.Add(_("Comment"));
-	choices.Add(_("Layer"));
-	choices.Add(_("Start Time"));
-	choices.Add(_("End Time"));
-	choices.Add(_("Style"));
-	choices.Add(_("Actor"));
-	choices.Add(_("Margin Left"));
-	choices.Add(_("Margin Right"));
-	choices.Add(_("Margin Vertical"));
-	choices.Add(_("Effect"));
-	choices.Add(_("Text"));
-	ListBox = new wxCheckListBox(&d, -1, wxDefaultPosition, wxDefaultSize, choices);
-	ListSizer->Add(ListBox, wxSizerFlags(0).Expand().Border(wxTOP));
-
-	std::vector<bool> options = OPT_GET("Tool/Paste Lines Over/Fields")->GetListBool();
-	if (options.size() != choices.size())
-		options.resize(choices.size(), false);
-
-	for (size_t i = 0; i < choices.size(); ++i)
-		ListBox->Check(i, options[i]);
-
-	// Top buttons
-	wxButton *btn;
-	wxSizer *TopButtonSizer = new wxBoxSizer(wxHORIZONTAL);
-
-	TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&All")), wxSizerFlags(1));
-	btn->Bind(wxEVT_BUTTON, std::bind(&DialogPasteOver::CheckAll, this, true));
-	TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&None")), wxSizerFlags(1));
-	btn->Bind(wxEVT_BUTTON, std::bind(&DialogPasteOver::CheckAll, this, false));
-	TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&Times")), wxSizerFlags(1));
-	btn->Bind(wxEVT_BUTTON, &DialogPasteOver::OnTimes, this);
-	TopButtonSizer->Add(btn = new wxButton(&d, -1, _("T&ext")), wxSizerFlags(1));
-	btn->Bind(wxEVT_BUTTON, &DialogPasteOver::OnText, this);
-
-	// Buttons
-	auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	d.Bind(wxEVT_BUTTON, &DialogPasteOver::OnOK, this, wxID_OK);
-	d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Paste Over"), wxID_HELP);
-
-	// Main sizer
-	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(ListSizer,0,wxEXPAND | wxLEFT | wxRIGHT,5);
-	MainSizer->Add(TopButtonSizer,0,wxLEFT | wxRIGHT | wxEXPAND,5);
-	MainSizer->Add(ButtonSizer,0,wxALL | wxEXPAND,5);
-	d.SetSizerAndFit(MainSizer);
-	d.CenterOnParent();
+    // Label and list sizer
+    wxSizer *ListSizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Fields"));
+    ListSizer->Add(new wxStaticText(&d, -1, _("Please select the fields that you want to paste over:")), wxSizerFlags());
+
+    // List box
+    wxArrayString choices;
+    choices.Add(_("Comment"));
+    choices.Add(_("Layer"));
+    choices.Add(_("Start Time"));
+    choices.Add(_("End Time"));
+    choices.Add(_("Style"));
+    choices.Add(_("Actor"));
+    choices.Add(_("Margin Left"));
+    choices.Add(_("Margin Right"));
+    choices.Add(_("Margin Vertical"));
+    choices.Add(_("Effect"));
+    choices.Add(_("Text"));
+    ListBox = new wxCheckListBox(&d, -1, wxDefaultPosition, wxDefaultSize, choices);
+    ListSizer->Add(ListBox, wxSizerFlags(0).Expand().Border(wxTOP));
+
+    std::vector<bool> options = OPT_GET("Tool/Paste Lines Over/Fields")->GetListBool();
+    if (options.size() != choices.size())
+        options.resize(choices.size(), false);
+
+    for (size_t i = 0; i < choices.size(); ++i)
+        ListBox->Check(i, options[i]);
+
+    // Top buttons
+    wxButton *btn;
+    wxSizer *TopButtonSizer = new wxBoxSizer(wxHORIZONTAL);
+
+    TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&All")), wxSizerFlags(1));
+    btn->Bind(wxEVT_BUTTON, std::bind(&DialogPasteOver::CheckAll, this, true));
+    TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&None")), wxSizerFlags(1));
+    btn->Bind(wxEVT_BUTTON, std::bind(&DialogPasteOver::CheckAll, this, false));
+    TopButtonSizer->Add(btn = new wxButton(&d, -1, _("&Times")), wxSizerFlags(1));
+    btn->Bind(wxEVT_BUTTON, &DialogPasteOver::OnTimes, this);
+    TopButtonSizer->Add(btn = new wxButton(&d, -1, _("T&ext")), wxSizerFlags(1));
+    btn->Bind(wxEVT_BUTTON, &DialogPasteOver::OnText, this);
+
+    // Buttons
+    auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    d.Bind(wxEVT_BUTTON, &DialogPasteOver::OnOK, this, wxID_OK);
+    d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Paste Over"), wxID_HELP);
+
+    // Main sizer
+    wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(ListSizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5);
+    MainSizer->Add(TopButtonSizer, 0, wxLEFT | wxRIGHT | wxEXPAND, 5);
+    MainSizer->Add(ButtonSizer, 0, wxALL | wxEXPAND, 5);
+    d.SetSizerAndFit(MainSizer);
+    d.CenterOnParent();
 }
 
-void DialogPasteOver::OnOK(wxCommandEvent &) {
-	std::vector<bool> options;
-	for (size_t i = 0; i < ListBox->GetCount(); ++i)
-		options.push_back(ListBox->IsChecked(i));
-	OPT_SET("Tool/Paste Lines Over/Fields")->SetListBool(std::move(options));
+void DialogPasteOver::OnOK(wxCommandEvent &)
+{
+    std::vector<bool> options;
+    for (size_t i = 0; i < ListBox->GetCount(); ++i)
+        options.push_back(ListBox->IsChecked(i));
+    OPT_SET("Tool/Paste Lines Over/Fields")->SetListBool(std::move(options));
 
-	d.EndModal(wxID_OK);
+    d.EndModal(wxID_OK);
 }
 
-void DialogPasteOver::OnText(wxCommandEvent &) {
-	CheckAll(false);
-	ListBox->Check(10, true);
+void DialogPasteOver::OnText(wxCommandEvent &)
+{
+    CheckAll(false);
+    ListBox->Check(10, true);
 }
 
-void DialogPasteOver::OnTimes(wxCommandEvent &) {
-	CheckAll(false);
-	ListBox->Check(2, true);
-	ListBox->Check(3, true);
+void DialogPasteOver::OnTimes(wxCommandEvent &)
+{
+    CheckAll(false);
+    ListBox->Check(2, true);
+    ListBox->Check(3, true);
 }
 
-void DialogPasteOver::CheckAll(bool check) {
-	for (size_t i = 0; i < ListBox->GetCount(); ++i)
-		ListBox->Check(i, check);
+void DialogPasteOver::CheckAll(bool check)
+{
+    for (size_t i = 0; i < ListBox->GetCount(); ++i)
+        ListBox->Check(i, check);
 }
 }
 
-bool ShowPasteOverDialog(wxWindow *parent) {
-	return DialogPasteOver(parent).d.ShowModal() == wxID_OK;
+bool ShowPasteOverDialog(wxWindow *parent)
+{
+    return DialogPasteOver(parent).d.ShowModal() == wxID_OK;
 }
diff --git a/src/dialog_progress.cpp b/src/dialog_progress.cpp
index bcf4de96d680a2ea85a13665f447539347830d39..53cb921262f8e6759497cd3ff562bd904990cc27 100644
--- a/src/dialog_progress.cpp
+++ b/src/dialog_progress.cpp
@@ -40,221 +40,230 @@
 using agi::dispatch::Main;
 
 namespace {
-	void set_taskbar_progress(int progress) {
+void set_taskbar_progress(int progress)
+{
 #ifdef _MSC_VER
-		int major, minor;
-		wxGetOsVersion(&major, &minor);
-		if (major < 6 || (major == 6 && minor < 1)) return;
-
-		ITaskbarList3 *taskbar;
-		auto hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
-			__uuidof(ITaskbarList3), (LPVOID *)&taskbar);
-		if (FAILED(hr)) return;
-
-		hr = taskbar->HrInit();
-		if (FAILED(hr)) {
-			taskbar->Release();
-			return;
-		}
-
-		auto hwnd = wxTheApp->GetTopWindow()->GetHWND();
-		if (progress == 0 || progress == 100)
-			taskbar->SetProgressState(hwnd, TBPF_NOPROGRESS);
-		else if (progress == -1)
-			taskbar->SetProgressState(hwnd, TBPF_INDETERMINATE);
-		else
-			taskbar->SetProgressValue(hwnd, progress, 100);
-
-		taskbar->Release();
+    int major, minor;
+    wxGetOsVersion(&major, &minor);
+    if (major < 6 || (major == 6 && minor < 1)) return;
+
+    ITaskbarList3 *taskbar;
+    auto hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
+                                 __uuidof(ITaskbarList3), (LPVOID *)&taskbar);
+    if (FAILED(hr)) return;
+
+    hr = taskbar->HrInit();
+    if (FAILED(hr)) {
+        taskbar->Release();
+        return;
+    }
+
+    auto hwnd = wxTheApp->GetTopWindow()->GetHWND();
+    if (progress == 0 || progress == 100)
+        taskbar->SetProgressState(hwnd, TBPF_NOPROGRESS);
+    else if (progress == -1)
+        taskbar->SetProgressState(hwnd, TBPF_INDETERMINATE);
+    else
+        taskbar->SetProgressValue(hwnd, progress, 100);
+
+    taskbar->Release();
 #endif
-	}
+}
 }
 
 class DialogProgressSink final : public agi::ProgressSink {
-	DialogProgress *dialog;
-	std::atomic<bool> cancelled{false};
-	int progress = 0;
+    DialogProgress *dialog;
+    std::atomic<bool> cancelled{false};
+    int progress = 0;
 
 public:
-	DialogProgressSink(DialogProgress *dialog) : dialog(dialog) { }
-
-	void SetTitle(std::string const& title) override {
-		Main().Async([=]{ dialog->title->SetLabelText(to_wx(title)); });
-	}
-
-	void SetMessage(std::string const& msg) override {
-		Main().Async([=]{ dialog->text->SetLabelText(to_wx(msg)); });
-	}
-
-	void SetProgress(int64_t cur, int64_t max) override {
-		int new_progress = mid<int>(0, double(cur) / max * 300, 300);
-		if (new_progress != progress) {
-			progress = new_progress;
-			Main().Async([=]{ dialog->SetProgress(new_progress); });
-		}
-	}
-
-	void Log(std::string const& str) override {
-		Main().Async([=]{ dialog->pending_log += to_wx(str); });
-	}
-
-	bool IsCancelled() override {
-		return cancelled;
-	}
-
-	void Cancel() {
-		cancelled = true;
-	}
-
-	void SetIndeterminate() override {
-		Main().Async([=]{ dialog->pulse_timer.Start(1000); });
-	}
+    DialogProgressSink(DialogProgress *dialog) : dialog(dialog) { }
+
+    void SetTitle(std::string const &title) override {
+        Main().Async([ = ] { dialog->title->SetLabelText(to_wx(title)); });
+    }
+
+    void SetMessage(std::string const &msg) override {
+        Main().Async([ = ] { dialog->text->SetLabelText(to_wx(msg)); });
+    }
+
+    void SetProgress(int64_t cur, int64_t max) override {
+        int new_progress = mid<int>(0, double(cur) / max * 300, 300);
+        if (new_progress != progress) {
+            progress = new_progress;
+            Main().Async([ = ] { dialog->SetProgress(new_progress); });
+        }
+    }
+
+    void Log(std::string const &str) override {
+        Main().Async([ = ] { dialog->pending_log += to_wx(str); });
+    }
+
+    bool IsCancelled() override {
+        return cancelled;
+    }
+
+    void Cancel() {
+        cancelled = true;
+    }
+
+    void SetIndeterminate() override {
+        Main().Async([ = ] { dialog->pulse_timer.Start(1000); });
+    }
 };
 
-DialogProgress::DialogProgress(wxWindow *parent, wxString const& title_text, wxString const& message)
-: wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
-, pulse_timer(GetEventHandler())
+DialogProgress::DialogProgress(wxWindow *parent, wxString const &title_text, wxString const &message)
+    : wxDialog(parent, -1, title_text, wxDefaultPosition, wxDefaultSize, wxBORDER_RAISED)
+    , pulse_timer(GetEventHandler())
 {
-	title = new wxStaticText(this, -1, title_text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
-	gauge = new wxGauge(this, -1, 300, wxDefaultPosition, wxSize(300,20));
-	text = new wxStaticText(this, -1, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
-	cancel_button = new wxButton(this, wxID_CANCEL);
-	log_output = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(600, 240), wxTE_MULTILINE | wxTE_READONLY);
-
-	// make the title a slightly larger font
-	wxFont title_font = title->GetFont();
-	int fontsize = title_font.GetPointSize();
-	title_font.SetPointSize(fontsize * 1.375);
-	title_font.SetWeight(wxFONTWEIGHT_BOLD);
-	title->SetFont(title_font);
-
-	wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
-	sizer->Add(title, wxSizerFlags().Expand());
-	sizer->Add(gauge, wxSizerFlags(1).Expand().Border());
-	sizer->Add(text, wxSizerFlags().Expand());
-	sizer->Add(cancel_button, wxSizerFlags().Center().Border());
-	sizer->Add(log_output, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	sizer->Hide(log_output);
-
-	SetSizerAndFit(sizer);
-	CenterOnParent();
-
-	Bind(wxEVT_SHOW, &DialogProgress::OnShow, this);
-	Bind(wxEVT_TIMER, [=](wxTimerEvent&) { gauge->Pulse(); });
+    title = new wxStaticText(this, -1, title_text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
+    gauge = new wxGauge(this, -1, 300, wxDefaultPosition, wxSize(300, 20));
+    text = new wxStaticText(this, -1, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE | wxST_NO_AUTORESIZE);
+    cancel_button = new wxButton(this, wxID_CANCEL);
+    log_output = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(600, 240), wxTE_MULTILINE | wxTE_READONLY);
+
+    // make the title a slightly larger font
+    wxFont title_font = title->GetFont();
+    int fontsize = title_font.GetPointSize();
+    title_font.SetPointSize(fontsize * 1.375);
+    title_font.SetWeight(wxFONTWEIGHT_BOLD);
+    title->SetFont(title_font);
+
+    wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+    sizer->Add(title, wxSizerFlags().Expand());
+    sizer->Add(gauge, wxSizerFlags(1).Expand().Border());
+    sizer->Add(text, wxSizerFlags().Expand());
+    sizer->Add(cancel_button, wxSizerFlags().Center().Border());
+    sizer->Add(log_output, wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    sizer->Hide(log_output);
+
+    SetSizerAndFit(sizer);
+    CenterOnParent();
+
+    Bind(wxEVT_SHOW, &DialogProgress::OnShow, this);
+    Bind(wxEVT_TIMER, [ = ](wxTimerEvent &) { gauge->Pulse(); });
 }
 
-void DialogProgress::Run(std::function<void(agi::ProgressSink*)> task) {
-	DialogProgressSink ps(this);
-	this->ps = &ps;
-
-	auto current_title = from_wx(title->GetLabelText());
-	agi::dispatch::Background().Async([=]{
-		agi::osx::AppNapDisabler app_nap_disabler(current_title);
-		try {
-			task(this->ps);
-		}
-		catch (agi::Exception const& e) {
-			this->ps->Log(e.GetMessage());
-		}
-
-		Main().Async([this]{
-			pulse_timer.Stop();
-			Unbind(wxEVT_IDLE, &DialogProgress::OnIdle, this);
-
-			// 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_BUTTON, &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 {
-				if (!pending_log.empty()) {
-					wxIdleEvent evt;
-					OnIdle(evt);
-				}
-				cancel_button->SetLabelText(_("Close"));
-				gauge->SetValue(300);
-			}
-			set_taskbar_progress(0);
-		});
-	});
-
-	if (!ShowModal())
-		throw agi::UserCancelException("Cancelled by user");
+void DialogProgress::Run(std::function<void(agi::ProgressSink *)> task)
+{
+    DialogProgressSink ps(this);
+    this->ps = &ps;
+
+    auto current_title = from_wx(title->GetLabelText());
+    agi::dispatch::Background().Async([ = ] {
+        agi::osx::AppNapDisabler app_nap_disabler(current_title);
+        try
+        {
+            task(this->ps);
+        }
+        catch (agi::Exception const &e)
+        {
+            this->ps->Log(e.GetMessage());
+        }
+
+        Main().Async([this]{
+            pulse_timer.Stop();
+            Unbind(wxEVT_IDLE, &DialogProgress::OnIdle, this);
+
+            // 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_BUTTON, &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
+            {
+                if (!pending_log.empty()) {
+                    wxIdleEvent evt;
+                    OnIdle(evt);
+                }
+                cancel_button->SetLabelText(_("Close"));
+                gauge->SetValue(300);
+            }
+            set_taskbar_progress(0);
+        });
+    });
+
+    if (!ShowModal())
+        throw agi::UserCancelException("Cancelled by user");
 }
 
-void DialogProgress::OnShow(wxShowEvent& evt) {
-	if (!evt.IsShown()) return;
-
-	// Restore the cancel button in case it was previously switched to a close
-	// button
-	Bind(wxEVT_BUTTON, &DialogProgress::OnCancel, this, wxID_CANCEL);
-	Bind(wxEVT_IDLE, &DialogProgress::OnIdle, this);
-	cancel_button->SetLabelText(_("Cancel"));
-	cancel_button->Enable();
-
-	wxSizer *sizer = GetSizer();
-	if (sizer->IsShown(log_output)) {
-		sizer->Hide(log_output);
-		Layout();
-		sizer->Fit(this);
-		log_output->Clear();
-	}
+void DialogProgress::OnShow(wxShowEvent &evt)
+{
+    if (!evt.IsShown()) return;
+
+    // Restore the cancel button in case it was previously switched to a close
+    // button
+    Bind(wxEVT_BUTTON, &DialogProgress::OnCancel, this, wxID_CANCEL);
+    Bind(wxEVT_IDLE, &DialogProgress::OnIdle, this);
+    cancel_button->SetLabelText(_("Cancel"));
+    cancel_button->Enable();
+
+    wxSizer *sizer = GetSizer();
+    if (sizer->IsShown(log_output)) {
+        sizer->Hide(log_output);
+        Layout();
+        sizer->Fit(this);
+        log_output->Clear();
+    }
 }
 
-void DialogProgress::OnIdle(wxIdleEvent&) {
-	if (progress_current > progress_target) {
-		progress_current = progress_target;
-		gauge->SetValue(progress_current);
-		set_taskbar_progress(progress_current / 3);
-	}
-	else if (progress_current < progress_target) {
-		using namespace std::chrono;
-		auto now = steady_clock::now();
-		int ms = mid<int>(0, duration_cast<milliseconds>(now - progress_anim_start_time).count(), progress_anim_duration);
-		int dist = (progress_target - progress_anim_start_value) * ms / progress_anim_duration;
-		if (dist) {
-			progress_current = progress_anim_start_value + dist;
-			gauge->SetValue(progress_current);
-			set_taskbar_progress(progress_current / 3);
-		}
-	}
-
-	if (!pending_log) return;
-
-	if (log_output->IsEmpty()) {
-		wxSizer *sizer = GetSizer();
-		sizer->Show(log_output);
-		Layout();
-		sizer->Fit(this);
-		CenterOnParent();
-	}
-
-	*log_output << pending_log;
-	log_output->SetInsertionPointEnd();
-	pending_log.clear();
+void DialogProgress::OnIdle(wxIdleEvent &)
+{
+    if (progress_current > progress_target) {
+        progress_current = progress_target;
+        gauge->SetValue(progress_current);
+        set_taskbar_progress(progress_current / 3);
+    }
+    else if (progress_current < progress_target) {
+        using namespace std::chrono;
+        auto now = steady_clock::now();
+        int ms = mid<int>(0, duration_cast<milliseconds>(now - progress_anim_start_time).count(), progress_anim_duration);
+        int dist = (progress_target - progress_anim_start_value) * ms / progress_anim_duration;
+        if (dist) {
+            progress_current = progress_anim_start_value + dist;
+            gauge->SetValue(progress_current);
+            set_taskbar_progress(progress_current / 3);
+        }
+    }
+
+    if (!pending_log) return;
+
+    if (log_output->IsEmpty()) {
+        wxSizer *sizer = GetSizer();
+        sizer->Show(log_output);
+        Layout();
+        sizer->Fit(this);
+        CenterOnParent();
+    }
+
+    *log_output << pending_log;
+    log_output->SetInsertionPointEnd();
+    pending_log.clear();
 }
 
-void DialogProgress::OnCancel(wxCommandEvent &) {
-	ps->Cancel();
-	cancel_button->Enable(false);
-	cancel_button->SetLabelText(_("Cancelling..."));
+void DialogProgress::OnCancel(wxCommandEvent &)
+{
+    ps->Cancel();
+    cancel_button->Enable(false);
+    cancel_button->SetLabelText(_("Cancelling..."));
 }
 
-void DialogProgress::SetProgress(int target) {
-	if (target == progress_target) return;
-	using namespace std::chrono;
-
-	progress_anim_start_value = progress_current;
-	auto now = steady_clock::now();
-	if (progress_target == 0)
-		progress_anim_duration = 1000;
-	else
-		progress_anim_duration = std::max<int>(100, duration_cast<milliseconds>(now - progress_anim_start_time).count() * 11 / 10);
-	progress_anim_start_time = now;
-	progress_target = target;
+void DialogProgress::SetProgress(int target)
+{
+    if (target == progress_target) return;
+    using namespace std::chrono;
+
+    progress_anim_start_value = progress_current;
+    auto now = steady_clock::now();
+    if (progress_target == 0)
+        progress_anim_duration = 1000;
+    else
+        progress_anim_duration = std::max<int>(100, duration_cast<milliseconds>(now - progress_anim_start_time).count() * 11 / 10);
+    progress_anim_start_time = now;
+    progress_target = target;
 }
diff --git a/src/dialog_progress.h b/src/dialog_progress.h
index d4e4667d07e85f95222e0ef7b05b58e91577c6b7..936fc33465aed20ae9acc8786bc3463a419d02eb 100644
--- a/src/dialog_progress.h
+++ b/src/dialog_progress.h
@@ -32,37 +32,37 @@ class wxTextCtrl;
 /// @class DialogProgress
 /// @brief Progress-bar dialog box for displaying during long operations
 class DialogProgress final : public wxDialog, public agi::BackgroundRunner {
-	friend class DialogProgressSink;
-	DialogProgressSink *ps;
+    friend class DialogProgressSink;
+    DialogProgressSink *ps;
 
-	wxStaticText *title;
-	wxStaticText *text;
-	wxGauge *gauge;
-	wxButton *cancel_button;
-	wxTextCtrl *log_output;
+    wxStaticText *title;
+    wxStaticText *text;
+    wxGauge *gauge;
+    wxButton *cancel_button;
+    wxTextCtrl *log_output;
 
-	wxTimer pulse_timer;
+    wxTimer pulse_timer;
 
-	wxString pending_log;
-	int progress_anim_start_value = 0;
-	int progress_current = 0;
-	int progress_target = 0;
-	std::chrono::steady_clock::time_point progress_anim_start_time;
-	int progress_anim_duration = 0;
+    wxString pending_log;
+    int progress_anim_start_value = 0;
+    int progress_current = 0;
+    int progress_target = 0;
+    std::chrono::steady_clock::time_point progress_anim_start_time;
+    int progress_anim_duration = 0;
 
-	void OnShow(wxShowEvent&);
-	void OnCancel(wxCommandEvent &);
-	void OnIdle(wxIdleEvent&);
+    void OnShow(wxShowEvent &);
+    void OnCancel(wxCommandEvent &);
+    void OnIdle(wxIdleEvent &);
 
-	void SetProgress(int target);
+    void SetProgress(int target);
 
 public:
-	/// Constructor
-	/// @param parent Parent window of the dialog
-	/// @param title Initial title of the dialog
-	/// @param message Initial message of the dialog
-	DialogProgress(wxWindow *parent, wxString const& title="", wxString const& message="");
+    /// Constructor
+    /// @param parent Parent window of the dialog
+    /// @param title Initial title of the dialog
+    /// @param message Initial message of the dialog
+    DialogProgress(wxWindow *parent, wxString const &title = "", wxString const &message = "");
 
-	/// BackgroundWorker implementation
-	void Run(std::function<void(agi::ProgressSink *)> task) override;
+    /// BackgroundWorker implementation
+    void Run(std::function<void(agi::ProgressSink *)> task) override;
 };
diff --git a/src/dialog_properties.cpp b/src/dialog_properties.cpp
index 501a96f19af30639dd1bb9fe9fa6d8e6e4ebd548..9d1f525e29f82bc16f1cacd559a3e99c92d8bc8b 100644
--- a/src/dialog_properties.cpp
+++ b/src/dialog_properties.cpp
@@ -49,166 +49,171 @@
 
 namespace {
 class DialogProperties {
-	wxDialog d;
-	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<std::pair<std::string, wxTextCtrl*>> properties;
-
-	// Things that effect rendering
-	wxComboBox *WrapStyle;   ///< Wrapping style for long lines
-	wxTextCtrl *ResX;        ///< Script x resolution
-	wxTextCtrl *ResY;        ///< Script y resolution
-	wxCheckBox *ScaleBorder; ///< If script resolution != video resolution how should borders be handled
-	wxComboBox *YCbCrMatrix;
-
-	/// OK button handler
-	void OnOK(wxCommandEvent &event);
-	/// Set script resolution to video resolution button
-	void OnSetFromVideo(wxCommandEvent &event);
-	/// Set a script info field
-	/// @param key Name of field
-	/// @param value New value
-	/// @return Did the value actually need to be changed?
-	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, std::string const& property);
+    wxDialog d;
+    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<std::pair<std::string, wxTextCtrl *>> properties;
+
+    // Things that effect rendering
+    wxComboBox *WrapStyle;   ///< Wrapping style for long lines
+    wxTextCtrl *ResX;        ///< Script x resolution
+    wxTextCtrl *ResY;        ///< Script y resolution
+    wxCheckBox *ScaleBorder; ///< If script resolution != video resolution how should borders be handled
+    wxComboBox *YCbCrMatrix;
+
+    /// OK button handler
+    void OnOK(wxCommandEvent &event);
+    /// Set script resolution to video resolution button
+    void OnSetFromVideo(wxCommandEvent &event);
+    /// Set a script info field
+    /// @param key Name of field
+    /// @param value New value
+    /// @return Did the value actually need to be changed?
+    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, std::string const &property);
 
 public:
-	/// Constructor
-	/// @param c Project context
-	DialogProperties(agi::Context *c);
-	void ShowModal() { d.ShowModal(); }
+    /// Constructor
+    /// @param c Project context
+    DialogProperties(agi::Context *c);
+    void ShowModal() { d.ShowModal(); }
 };
 
 DialogProperties::DialogProperties(agi::Context *c)
-: d(c->parent, -1, _("Script Properties"))
-, c(c)
+    : d(c->parent, -1, _("Script Properties"))
+    , c(c)
 {
-	d.SetIcon(GETICON(properties_toolbutton_16));
-
-	// Script details crap
-	wxSizer *TopSizer = new wxStaticBoxSizer(wxHORIZONTAL,&d,_("Script"));
-	auto TopSizerGrid = new wxFlexGridSizer(0,2,5,5);
-
-	AddProperty(TopSizerGrid, _("Title:"), "Title");
-	AddProperty(TopSizerGrid, _("Original script:"), "Original Script");
-	AddProperty(TopSizerGrid, _("Translation:"), "Original Translation");
-	AddProperty(TopSizerGrid, _("Editing:"), "Original Editing");
-	AddProperty(TopSizerGrid, _("Timing:"), "Original Timing");
-	AddProperty(TopSizerGrid, _("Synch point:"), "Synch Point");
-	AddProperty(TopSizerGrid, _("Updated by:"), "Script Updated By");
-	AddProperty(TopSizerGrid, _("Update details:"), "Update Details");
-
-	TopSizerGrid->AddGrowableCol(1,1);
-	TopSizer->Add(TopSizerGrid,1,wxALL | wxEXPAND,0);
-
-	// Resolution box
-	ResX = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxSize(50,20),0,IntValidator(c->ass->GetScriptInfoAsInt("PlayResX")));
-	ResY = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxSize(50,20),0,IntValidator(c->ass->GetScriptInfoAsInt("PlayResY")));
-
-	wxButton *FromVideo = new wxButton(&d,-1,_("From &video"));
-	if (!c->project->VideoProvider())
-		FromVideo->Enable(false);
-	else
-		FromVideo->Bind(wxEVT_BUTTON, &DialogProperties::OnSetFromVideo, this);
-
-	auto res_sizer = new wxBoxSizer(wxHORIZONTAL);
-	res_sizer->Add(ResX, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
-	res_sizer->Add(new wxStaticText(&d, -1, "x"), 0, wxALIGN_CENTER | wxRIGHT, 5);
-	res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
-	res_sizer->Add(FromVideo, 1, 0, 0);
-
-	YCbCrMatrix = new wxComboBox(&d, -1, to_wx(c->ass->GetScriptInfo("YCbCr Matrix")),
-		 wxDefaultPosition, wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
-
-	auto matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
-	matrix_sizer->Add(new wxStaticText(&d, -1, "YCbCr Matrix:"), wxSizerFlags().Center());
-	matrix_sizer->Add(YCbCrMatrix, wxSizerFlags(1).Expand().Border(wxLEFT));
-
-	auto res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Resolution"));
-	res_box->Add(res_sizer, wxSizerFlags().Expand());
-	res_box->Add(matrix_sizer, wxSizerFlags().Border(wxTOP).Expand());
-
-	// Options
-	wxSizer *optionsBox = new wxStaticBoxSizer(wxHORIZONTAL,&d,_("Options"));
-	auto optionsGrid = new wxFlexGridSizer(3,2,5,5);
-	wxString wrap_opts[] = {
-		_("0: Smart wrapping, top line is wider"),
-		_("1: End-of-line word wrapping, only \\N breaks"),
-		_("2: No word wrapping, both \\n and \\N break"),
-		_("3: Smart wrapping, bottom line is wider")
-	};
-	WrapStyle = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 4, wrap_opts, wxCB_READONLY);
-	WrapStyle->SetSelection(c->ass->GetScriptInfoAsInt("WrapStyle"));
-	optionsGrid->Add(new wxStaticText(&d,-1,_("Wrap Style: ")),0,wxALIGN_CENTER_VERTICAL,0);
-	optionsGrid->Add(WrapStyle,1,wxEXPAND,0);
-
-	ScaleBorder = new wxCheckBox(&d,-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(boost::iequals(c->ass->GetScriptInfo("ScaledBorderAndShadow"), "yes"));
-	optionsGrid->AddSpacer(0);
-	optionsGrid->Add(ScaleBorder,1,wxEXPAND,0);
-	optionsGrid->AddGrowableCol(1,1);
-	optionsBox->Add(optionsGrid,1,wxEXPAND,0);
-
-	// Button sizer
-	auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	d.Bind(wxEVT_BUTTON, &DialogProperties::OnOK, this, wxID_OK);
-	d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Properties"), wxID_HELP);
-
-	// MainSizer
-	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(TopSizer,0,wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND,5);
-	MainSizer->Add(res_box,0,wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND,5);
-	MainSizer->Add(optionsBox,0,wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND,5);
-	MainSizer->Add(ButtonSizer,0,wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND,5);
-
-	d.SetSizerAndFit(MainSizer);
-	d.CenterOnParent();
+    d.SetIcon(GETICON(properties_toolbutton_16));
+
+    // Script details crap
+    wxSizer *TopSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Script"));
+    auto TopSizerGrid = new wxFlexGridSizer(0, 2, 5, 5);
+
+    AddProperty(TopSizerGrid, _("Title:"), "Title");
+    AddProperty(TopSizerGrid, _("Original script:"), "Original Script");
+    AddProperty(TopSizerGrid, _("Translation:"), "Original Translation");
+    AddProperty(TopSizerGrid, _("Editing:"), "Original Editing");
+    AddProperty(TopSizerGrid, _("Timing:"), "Original Timing");
+    AddProperty(TopSizerGrid, _("Synch point:"), "Synch Point");
+    AddProperty(TopSizerGrid, _("Updated by:"), "Script Updated By");
+    AddProperty(TopSizerGrid, _("Update details:"), "Update Details");
+
+    TopSizerGrid->AddGrowableCol(1, 1);
+    TopSizer->Add(TopSizerGrid, 1, wxALL | wxEXPAND, 0);
+
+    // Resolution box
+    ResX = new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, 20), 0, IntValidator(c->ass->GetScriptInfoAsInt("PlayResX")));
+    ResY = new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, 20), 0, IntValidator(c->ass->GetScriptInfoAsInt("PlayResY")));
+
+    wxButton *FromVideo = new wxButton(&d, -1, _("From &video"));
+    if (!c->project->VideoProvider())
+        FromVideo->Enable(false);
+    else
+        FromVideo->Bind(wxEVT_BUTTON, &DialogProperties::OnSetFromVideo, this);
+
+    auto res_sizer = new wxBoxSizer(wxHORIZONTAL);
+    res_sizer->Add(ResX, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
+    res_sizer->Add(new wxStaticText(&d, -1, "x"), 0, wxALIGN_CENTER | wxRIGHT, 5);
+    res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
+    res_sizer->Add(FromVideo, 1, 0, 0);
+
+    YCbCrMatrix = new wxComboBox(&d, -1, to_wx(c->ass->GetScriptInfo("YCbCr Matrix")),
+                                 wxDefaultPosition, wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
+
+    auto matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
+    matrix_sizer->Add(new wxStaticText(&d, -1, "YCbCr Matrix:"), wxSizerFlags().Center());
+    matrix_sizer->Add(YCbCrMatrix, wxSizerFlags(1).Expand().Border(wxLEFT));
+
+    auto res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Resolution"));
+    res_box->Add(res_sizer, wxSizerFlags().Expand());
+    res_box->Add(matrix_sizer, wxSizerFlags().Border(wxTOP).Expand());
+
+    // Options
+    wxSizer *optionsBox = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Options"));
+    auto optionsGrid = new wxFlexGridSizer(3, 2, 5, 5);
+    wxString wrap_opts[] = {
+        _("0: Smart wrapping, top line is wider"),
+        _("1: End-of-line word wrapping, only \\N breaks"),
+        _("2: No word wrapping, both \\n and \\N break"),
+        _("3: Smart wrapping, bottom line is wider")
+    };
+    WrapStyle = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 4, wrap_opts, wxCB_READONLY);
+    WrapStyle->SetSelection(c->ass->GetScriptInfoAsInt("WrapStyle"));
+    optionsGrid->Add(new wxStaticText(&d, -1, _("Wrap Style: ")), 0, wxALIGN_CENTER_VERTICAL, 0);
+    optionsGrid->Add(WrapStyle, 1, wxEXPAND, 0);
+
+    ScaleBorder = new wxCheckBox(&d, -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(boost::iequals(c->ass->GetScriptInfo("ScaledBorderAndShadow"), "yes"));
+    optionsGrid->AddSpacer(0);
+    optionsGrid->Add(ScaleBorder, 1, wxEXPAND, 0);
+    optionsGrid->AddGrowableCol(1, 1);
+    optionsBox->Add(optionsGrid, 1, wxEXPAND, 0);
+
+    // Button sizer
+    auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    d.Bind(wxEVT_BUTTON, &DialogProperties::OnOK, this, wxID_OK);
+    d.Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Properties"), wxID_HELP);
+
+    // MainSizer
+    wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(TopSizer, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
+    MainSizer->Add(res_box, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
+    MainSizer->Add(optionsBox, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
+    MainSizer->Add(ButtonSizer, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
+
+    d.SetSizerAndFit(MainSizer);
+    d.CenterOnParent();
 }
 
-void DialogProperties::AddProperty(wxSizer *sizer, wxString const& label, std::string const& property) {
-	wxTextCtrl *ctrl = new wxTextCtrl(&d, -1, to_wx(c->ass->GetScriptInfo(property)), wxDefaultPosition, wxSize(200, 20));
-	sizer->Add(new wxStaticText(&d, -1, label), wxSizerFlags().Center().Left());
-	sizer->Add(ctrl, wxSizerFlags(1).Expand());
-	properties.push_back({property, ctrl});
+void DialogProperties::AddProperty(wxSizer *sizer, wxString const &label, std::string const &property)
+{
+    wxTextCtrl *ctrl = new wxTextCtrl(&d, -1, to_wx(c->ass->GetScriptInfo(property)), wxDefaultPosition, wxSize(200, 20));
+    sizer->Add(new wxStaticText(&d, -1, label), wxSizerFlags().Center().Left());
+    sizer->Add(ctrl, wxSizerFlags(1).Expand());
+    properties.push_back({property, ctrl});
 }
 
-void DialogProperties::OnOK(wxCommandEvent &) {
-	int count = 0;
-	for (auto const& prop : properties)
-		count += SetInfoIfDifferent(prop.first, from_wx(prop.second->GetValue()));
+void DialogProperties::OnOK(wxCommandEvent &)
+{
+    int count = 0;
+    for (auto const &prop : properties)
+        count += SetInfoIfDifferent(prop.first, from_wx(prop.second->GetValue()));
 
-	count += SetInfoIfDifferent("PlayResX", from_wx(ResX->GetValue()));
-	count += SetInfoIfDifferent("PlayResY", from_wx(ResY->GetValue()));
-	count += SetInfoIfDifferent("WrapStyle", std::to_string(WrapStyle->GetSelection()));
-	count += SetInfoIfDifferent("ScaledBorderAndShadow", ScaleBorder->GetValue() ? "yes" : "no");
-	count += SetInfoIfDifferent("YCbCr Matrix", from_wx(YCbCrMatrix->GetValue()));
+    count += SetInfoIfDifferent("PlayResX", from_wx(ResX->GetValue()));
+    count += SetInfoIfDifferent("PlayResY", from_wx(ResY->GetValue()));
+    count += SetInfoIfDifferent("WrapStyle", std::to_string(WrapStyle->GetSelection()));
+    count += SetInfoIfDifferent("ScaledBorderAndShadow", ScaleBorder->GetValue() ? "yes" : "no");
+    count += SetInfoIfDifferent("YCbCr Matrix", from_wx(YCbCrMatrix->GetValue()));
 
-	if (count) c->ass->Commit(_("property changes"), AssFile::COMMIT_SCRIPTINFO);
+    if (count) c->ass->Commit(_("property changes"), AssFile::COMMIT_SCRIPTINFO);
 
-	d.EndModal(!!count);
+    d.EndModal(!!count);
 }
 
-int DialogProperties::SetInfoIfDifferent(std::string const& key, std::string const&value) {
-	if (c->ass->GetScriptInfo(key) != value) {
-		c->ass->SetScriptInfo(key, value);
-		return 1;
-	}
-	return 0;
+int DialogProperties::SetInfoIfDifferent(std::string const &key, std::string const &value)
+{
+    if (c->ass->GetScriptInfo(key) != value) {
+        c->ass->SetScriptInfo(key, value);
+        return 1;
+    }
+    return 0;
 }
 
-void DialogProperties::OnSetFromVideo(wxCommandEvent &) {
-	ResX->SetValue(std::to_wstring(c->project->VideoProvider()->GetWidth()));
-	ResY->SetValue(std::to_wstring(c->project->VideoProvider()->GetHeight()));
+void DialogProperties::OnSetFromVideo(wxCommandEvent &)
+{
+    ResX->SetValue(std::to_wstring(c->project->VideoProvider()->GetWidth()));
+    ResY->SetValue(std::to_wstring(c->project->VideoProvider()->GetHeight()));
 }
 }
 
-void ShowPropertiesDialog(agi::Context *c) {
-	DialogProperties(c).ShowModal();
+void ShowPropertiesDialog(agi::Context *c)
+{
+    DialogProperties(c).ShowModal();
 }
diff --git a/src/dialog_resample.cpp b/src/dialog_resample.cpp
index bf616cb8390de77476d716da8218454f44d8c3f3..e518685c42b222a9236598209fd8f27ebac68857 100644
--- a/src/dialog_resample.cpp
+++ b/src/dialog_resample.cpp
@@ -40,229 +40,235 @@ namespace {
 ///
 /// Populate a ResampleSettings structure with data from the user
 struct DialogResample {
-	wxDialog d;
-	agi::Context *c; ///< Project context
-
-	int script_w;
-	int script_h;
-	YCbCrMatrix script_mat;
-	int video_w = 0;
-	int video_h = 0;
-	YCbCrMatrix video_mat;
-
-	wxSpinCtrl *source_x;
-	wxSpinCtrl *source_y;
-	wxSpinCtrl *dest_x;
-	wxSpinCtrl *dest_y;
-	wxComboBox *source_matrix;
-	wxComboBox *dest_matrix;
-	wxCheckBox *symmetrical;
-	wxRadioBox *ar_mode;
-	wxSpinCtrl *margin_ctrl[4];
-
-	wxButton *from_script;
-	wxButton *from_video;
-
-	void SetSourceFromScript(wxCommandEvent &);
-	/// Set the destination resolution to the video's resolution
-	void SetDestFromVideo(wxCommandEvent &);
-	/// Symmetrical checkbox toggle handler
-	void OnSymmetrical(wxCommandEvent &);
-	/// Copy margin values over if symmetrical is enabled
-	void OnMarginChange(wxSpinCtrl *src, wxSpinCtrl *dst);
-	void UpdateButtons();
+    wxDialog d;
+    agi::Context *c; ///< Project context
+
+    int script_w;
+    int script_h;
+    YCbCrMatrix script_mat;
+    int video_w = 0;
+    int video_h = 0;
+    YCbCrMatrix video_mat;
+
+    wxSpinCtrl *source_x;
+    wxSpinCtrl *source_y;
+    wxSpinCtrl *dest_x;
+    wxSpinCtrl *dest_y;
+    wxComboBox *source_matrix;
+    wxComboBox *dest_matrix;
+    wxCheckBox *symmetrical;
+    wxRadioBox *ar_mode;
+    wxSpinCtrl *margin_ctrl[4];
+
+    wxButton *from_script;
+    wxButton *from_video;
+
+    void SetSourceFromScript(wxCommandEvent &);
+    /// Set the destination resolution to the video's resolution
+    void SetDestFromVideo(wxCommandEvent &);
+    /// Symmetrical checkbox toggle handler
+    void OnSymmetrical(wxCommandEvent &);
+    /// Copy margin values over if symmetrical is enabled
+    void OnMarginChange(wxSpinCtrl *src, wxSpinCtrl *dst);
+    void UpdateButtons();
 
 public:
-	/// Constructor
-	/// @param context Project context
-	/// @param[out] settings Settings struct to populate
-	DialogResample(agi::Context *context, ResampleSettings &settings);
+    /// Constructor
+    /// @param context Project context
+    /// @param[out] settings Settings struct to populate
+    DialogResample(agi::Context *context, ResampleSettings &settings);
 };
 
 enum {
-	LEFT = 0,
-	RIGHT = 1,
-	TOP = 2,
-	BOTTOM = 3
+    LEFT = 0,
+    RIGHT = 1,
+    TOP = 2,
+    BOTTOM = 3
 };
 
 DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings)
-: d(c->parent, -1, _("Resample Resolution"))
-, c(c)
+    : d(c->parent, -1, _("Resample Resolution"))
+    , c(c)
 {
-	d.SetIcon(GETICON(resample_toolbutton_16));
-
-	memset(&settings, 0, sizeof(settings));
-	c->ass->GetResolution(script_w, script_h);
-	settings.source_x = script_w;
-	settings.source_y = script_h;
-	settings.source_matrix = script_mat = MatrixFromString(c->ass->GetScriptInfo("YCbCr Matrix"));
-
-	if (auto provider = c->project->VideoProvider()) {
-		settings.dest_x = video_w = provider->GetWidth();
-		settings.dest_y = video_h = provider->GetHeight();
-		settings.dest_matrix = video_mat = MatrixFromString(provider->GetRealColorSpace());
-	}
-	else {
-		settings.dest_x = script_w;
-		settings.dest_y = script_h;
-		settings.dest_matrix = script_mat;
-		video_mat = YCbCrMatrix::rgb;
-	}
-
-	// Create all controls and set validators
-	for (size_t i = 0; i < 4; ++i) {
-		margin_ctrl[i] = new wxSpinCtrl(&d, -1, "0", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, -9999, 9999, 0);
-		margin_ctrl[i]->SetValidator(wxGenericValidator(&settings.margin[i]));
-	}
-
-	symmetrical = new wxCheckBox(&d, -1, _("&Symmetrical"));
-	symmetrical->SetValue(true);
-
-	margin_ctrl[RIGHT]->Enable(false);
-	margin_ctrl[BOTTOM]->Enable(false);
-
-	source_x = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
-	source_y = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
-	source_matrix = new wxComboBox(&d, -1, "", wxDefaultPosition,
-		wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
-	dest_x = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
-	dest_y = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
-	dest_matrix = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize,
-		to_wx(MatrixNames()), wxCB_READONLY);
-
-	source_x->SetValidator(wxGenericValidator(&settings.source_x));
-	source_y->SetValidator(wxGenericValidator(&settings.source_y));
-	source_matrix->SetValidator(MakeEnumBinder(&settings.source_matrix));
-	dest_x->SetValidator(wxGenericValidator(&settings.dest_x));
-	dest_y->SetValidator(wxGenericValidator(&settings.dest_y));
-	dest_matrix->SetValidator(MakeEnumBinder(&settings.dest_matrix));
-
-	from_video = new wxButton(&d, -1, _("From &video"));
-	from_video->Enable(false);
-	from_script = new wxButton(&d, -1, _("From s&cript"));
-	from_script->Enable(false);
-
-	wxString ar_modes[] = {_("Stretch"), _("Add borders"), _("Remove borders"), _("Manual")};
-	ar_mode = new wxRadioBox(&d, -1, _("Aspect Ratio Handling"), wxDefaultPosition,
-		wxDefaultSize, boost::size(ar_modes), ar_modes, 1, 4, MakeEnumBinder(&settings.ar_mode));
-
-	// Position the controls
-	auto margin_sizer = new wxGridSizer(3, 3, 5, 5);
-	margin_sizer->AddSpacer(1);
-	margin_sizer->Add(margin_ctrl[TOP], wxSizerFlags(1).Expand());
-	margin_sizer->AddSpacer(1);
-	margin_sizer->Add(margin_ctrl[LEFT], wxSizerFlags(1).Expand());
-	margin_sizer->Add(symmetrical, wxSizerFlags(1).Expand());
-	margin_sizer->Add(margin_ctrl[RIGHT], wxSizerFlags(1).Expand());
-	margin_sizer->AddSpacer(1);
-	margin_sizer->Add(margin_ctrl[BOTTOM], wxSizerFlags(1).Expand());
-	margin_sizer->AddSpacer(1);
-
-	auto margin_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Margin offset"));
-	margin_box->Add(margin_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
-
-	auto source_res_sizer = new wxBoxSizer(wxHORIZONTAL);
-	source_res_sizer->Add(source_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
-	source_res_sizer->Add(new wxStaticText(&d, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
-	source_res_sizer->Add(source_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
-	source_res_sizer->Add(from_script, wxSizerFlags(1));
-
-	auto source_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
-	source_matrix_sizer->Add(new wxStaticText(&d, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
-	source_matrix_sizer->Add(source_matrix, wxSizerFlags(1).Center().Right());
-
-	auto source_res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Source Resolution"));
-	source_res_box->Add(source_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
-	source_res_box->Add(source_matrix_sizer, wxSizerFlags(1).Expand());
-
-	auto dest_res_sizer = new wxBoxSizer(wxHORIZONTAL);
-	dest_res_sizer->Add(dest_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
-	dest_res_sizer->Add(new wxStaticText(&d, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
-	dest_res_sizer->Add(dest_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
-	dest_res_sizer->Add(from_video, wxSizerFlags(1));
-
-	auto dest_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
-	dest_matrix_sizer->Add(new wxStaticText(&d, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
-	dest_matrix_sizer->Add(dest_matrix, wxSizerFlags(1).Center().Right());
-
-	auto dest_res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Destination Resolution"));
-	dest_res_box->Add(dest_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
-	dest_res_box->Add(dest_matrix_sizer, wxSizerFlags(1).Expand());
-
-	auto main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(source_res_box, wxSizerFlags().Expand().Border());
-	main_sizer->Add(dest_res_box, wxSizerFlags().Expand().Border());
-	main_sizer->Add(ar_mode, wxSizerFlags().Expand().Border());
-	main_sizer->Add(margin_box, wxSizerFlags(1).Expand().Border());
-	main_sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
-	d.SetSizerAndFit(main_sizer);
-	d.CenterOnParent();
-
-	d.TransferDataToWindow();
-	UpdateButtons();
-
-	// Bind events
-	using std::bind;
-	d.Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Resample resolution"), wxID_HELP);
-	d.Bind(wxEVT_SPINCTRL, [=](wxCommandEvent&) { UpdateButtons(); });
-	d.Bind(wxEVT_RADIOBOX, [=](wxCommandEvent&) { UpdateButtons(); });
-	from_video->Bind(wxEVT_BUTTON, &DialogResample::SetDestFromVideo, this);
-	from_script->Bind(wxEVT_BUTTON, &DialogResample::SetSourceFromScript, this);
-	symmetrical->Bind(wxEVT_CHECKBOX, &DialogResample::OnSymmetrical, this);
-	margin_ctrl[LEFT]->Bind(wxEVT_SPINCTRL, bind(&DialogResample::OnMarginChange, this, margin_ctrl[LEFT], margin_ctrl[RIGHT]));
-	margin_ctrl[TOP]->Bind(wxEVT_SPINCTRL, bind(&DialogResample::OnMarginChange, this, margin_ctrl[TOP], margin_ctrl[BOTTOM]));
+    d.SetIcon(GETICON(resample_toolbutton_16));
+
+    memset(&settings, 0, sizeof(settings));
+    c->ass->GetResolution(script_w, script_h);
+    settings.source_x = script_w;
+    settings.source_y = script_h;
+    settings.source_matrix = script_mat = MatrixFromString(c->ass->GetScriptInfo("YCbCr Matrix"));
+
+    if (auto provider = c->project->VideoProvider()) {
+        settings.dest_x = video_w = provider->GetWidth();
+        settings.dest_y = video_h = provider->GetHeight();
+        settings.dest_matrix = video_mat = MatrixFromString(provider->GetRealColorSpace());
+    }
+    else {
+        settings.dest_x = script_w;
+        settings.dest_y = script_h;
+        settings.dest_matrix = script_mat;
+        video_mat = YCbCrMatrix::rgb;
+    }
+
+    // Create all controls and set validators
+    for (size_t i = 0; i < 4; ++i) {
+        margin_ctrl[i] = new wxSpinCtrl(&d, -1, "0", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, -9999, 9999, 0);
+        margin_ctrl[i]->SetValidator(wxGenericValidator(&settings.margin[i]));
+    }
+
+    symmetrical = new wxCheckBox(&d, -1, _("&Symmetrical"));
+    symmetrical->SetValue(true);
+
+    margin_ctrl[RIGHT]->Enable(false);
+    margin_ctrl[BOTTOM]->Enable(false);
+
+    source_x = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
+    source_y = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
+    source_matrix = new wxComboBox(&d, -1, "", wxDefaultPosition,
+                                   wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY);
+    dest_x = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
+    dest_y = new wxSpinCtrl(&d, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX);
+    dest_matrix = new wxComboBox(&d, -1, "", wxDefaultPosition, wxDefaultSize,
+                                 to_wx(MatrixNames()), wxCB_READONLY);
+
+    source_x->SetValidator(wxGenericValidator(&settings.source_x));
+    source_y->SetValidator(wxGenericValidator(&settings.source_y));
+    source_matrix->SetValidator(MakeEnumBinder(&settings.source_matrix));
+    dest_x->SetValidator(wxGenericValidator(&settings.dest_x));
+    dest_y->SetValidator(wxGenericValidator(&settings.dest_y));
+    dest_matrix->SetValidator(MakeEnumBinder(&settings.dest_matrix));
+
+    from_video = new wxButton(&d, -1, _("From &video"));
+    from_video->Enable(false);
+    from_script = new wxButton(&d, -1, _("From s&cript"));
+    from_script->Enable(false);
+
+    wxString ar_modes[] = {_("Stretch"), _("Add borders"), _("Remove borders"), _("Manual")};
+    ar_mode = new wxRadioBox(&d, -1, _("Aspect Ratio Handling"), wxDefaultPosition,
+                             wxDefaultSize, boost::size(ar_modes), ar_modes, 1, 4, MakeEnumBinder(&settings.ar_mode));
+
+    // Position the controls
+    auto margin_sizer = new wxGridSizer(3, 3, 5, 5);
+    margin_sizer->AddSpacer(1);
+    margin_sizer->Add(margin_ctrl[TOP], wxSizerFlags(1).Expand());
+    margin_sizer->AddSpacer(1);
+    margin_sizer->Add(margin_ctrl[LEFT], wxSizerFlags(1).Expand());
+    margin_sizer->Add(symmetrical, wxSizerFlags(1).Expand());
+    margin_sizer->Add(margin_ctrl[RIGHT], wxSizerFlags(1).Expand());
+    margin_sizer->AddSpacer(1);
+    margin_sizer->Add(margin_ctrl[BOTTOM], wxSizerFlags(1).Expand());
+    margin_sizer->AddSpacer(1);
+
+    auto margin_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Margin offset"));
+    margin_box->Add(margin_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
+
+    auto source_res_sizer = new wxBoxSizer(wxHORIZONTAL);
+    source_res_sizer->Add(source_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
+    source_res_sizer->Add(new wxStaticText(&d, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
+    source_res_sizer->Add(source_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
+    source_res_sizer->Add(from_script, wxSizerFlags(1));
+
+    auto source_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
+    source_matrix_sizer->Add(new wxStaticText(&d, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
+    source_matrix_sizer->Add(source_matrix, wxSizerFlags(1).Center().Right());
+
+    auto source_res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Source Resolution"));
+    source_res_box->Add(source_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
+    source_res_box->Add(source_matrix_sizer, wxSizerFlags(1).Expand());
+
+    auto dest_res_sizer = new wxBoxSizer(wxHORIZONTAL);
+    dest_res_sizer->Add(dest_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
+    dest_res_sizer->Add(new wxStaticText(&d, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT));
+    dest_res_sizer->Add(dest_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL));
+    dest_res_sizer->Add(from_video, wxSizerFlags(1));
+
+    auto dest_matrix_sizer = new wxBoxSizer(wxHORIZONTAL);
+    dest_matrix_sizer->Add(new wxStaticText(&d, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center());
+    dest_matrix_sizer->Add(dest_matrix, wxSizerFlags(1).Center().Right());
+
+    auto dest_res_box = new wxStaticBoxSizer(wxVERTICAL, &d, _("Destination Resolution"));
+    dest_res_box->Add(dest_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM));
+    dest_res_box->Add(dest_matrix_sizer, wxSizerFlags(1).Expand());
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(source_res_box, wxSizerFlags().Expand().Border());
+    main_sizer->Add(dest_res_box, wxSizerFlags().Expand().Border());
+    main_sizer->Add(ar_mode, wxSizerFlags().Expand().Border());
+    main_sizer->Add(margin_box, wxSizerFlags(1).Expand().Border());
+    main_sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Expand().Border(wxALL & ~wxTOP));
+    d.SetSizerAndFit(main_sizer);
+    d.CenterOnParent();
+
+    d.TransferDataToWindow();
+    UpdateButtons();
+
+    // Bind events
+    using std::bind;
+    d.Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Resample resolution"), wxID_HELP);
+    d.Bind(wxEVT_SPINCTRL, [ = ](wxCommandEvent &) { UpdateButtons(); });
+    d.Bind(wxEVT_RADIOBOX, [ = ](wxCommandEvent &) { UpdateButtons(); });
+    from_video->Bind(wxEVT_BUTTON, &DialogResample::SetDestFromVideo, this);
+    from_script->Bind(wxEVT_BUTTON, &DialogResample::SetSourceFromScript, this);
+    symmetrical->Bind(wxEVT_CHECKBOX, &DialogResample::OnSymmetrical, this);
+    margin_ctrl[LEFT]->Bind(wxEVT_SPINCTRL, bind(&DialogResample::OnMarginChange, this, margin_ctrl[LEFT], margin_ctrl[RIGHT]));
+    margin_ctrl[TOP]->Bind(wxEVT_SPINCTRL, bind(&DialogResample::OnMarginChange, this, margin_ctrl[TOP], margin_ctrl[BOTTOM]));
 }
 
-void DialogResample::SetDestFromVideo(wxCommandEvent &) {
-	dest_x->SetValue(video_w);
-	dest_y->SetValue(video_h);
-	dest_matrix->SetSelection((int)video_mat);
+void DialogResample::SetDestFromVideo(wxCommandEvent &)
+{
+    dest_x->SetValue(video_w);
+    dest_y->SetValue(video_h);
+    dest_matrix->SetSelection((int)video_mat);
 }
 
-void DialogResample::SetSourceFromScript(wxCommandEvent&) {
-	source_x->SetValue(script_w);
-	source_y->SetValue(script_h);
-	source_matrix->SetSelection((int)script_mat);
+void DialogResample::SetSourceFromScript(wxCommandEvent &)
+{
+    source_x->SetValue(script_w);
+    source_y->SetValue(script_h);
+    source_matrix->SetSelection((int)script_mat);
 }
 
-void DialogResample::UpdateButtons() {
-	from_video->Enable(c->project->VideoProvider() &&
-		(dest_x->GetValue() != video_w || dest_y->GetValue() != video_h));
-	from_script->Enable(source_x->GetValue() != script_w || source_y->GetValue() != script_h);
-
-	auto source_ar = double(source_x->GetValue()) / source_y->GetValue();
-	auto dest_ar = double(dest_x->GetValue()) / dest_y->GetValue();
-	bool ar_changed = std::abs(source_ar - dest_ar) / dest_ar > .01;
-
-	ar_mode->Enable(ar_changed);
-
-	bool margins = ar_changed && ar_mode->GetSelection() == (int)ResampleARMode::Manual;
-	symmetrical->Enable(margins);
-	margin_ctrl[LEFT]->Enable(margins);
-	margin_ctrl[TOP]->Enable(margins);
-	margin_ctrl[RIGHT]->Enable(margins && !symmetrical->GetValue());
-	margin_ctrl[BOTTOM]->Enable(margins && !symmetrical->GetValue());
+void DialogResample::UpdateButtons()
+{
+    from_video->Enable(c->project->VideoProvider() &&
+                       (dest_x->GetValue() != video_w || dest_y->GetValue() != video_h));
+    from_script->Enable(source_x->GetValue() != script_w || source_y->GetValue() != script_h);
+
+    auto source_ar = double(source_x->GetValue()) / source_y->GetValue();
+    auto dest_ar = double(dest_x->GetValue()) / dest_y->GetValue();
+    bool ar_changed = std::abs(source_ar - dest_ar) / dest_ar > .01;
+
+    ar_mode->Enable(ar_changed);
+
+    bool margins = ar_changed && ar_mode->GetSelection() == (int)ResampleARMode::Manual;
+    symmetrical->Enable(margins);
+    margin_ctrl[LEFT]->Enable(margins);
+    margin_ctrl[TOP]->Enable(margins);
+    margin_ctrl[RIGHT]->Enable(margins && !symmetrical->GetValue());
+    margin_ctrl[BOTTOM]->Enable(margins && !symmetrical->GetValue());
 }
 
-void DialogResample::OnSymmetrical(wxCommandEvent &) {
-	bool state = !symmetrical->IsChecked();
+void DialogResample::OnSymmetrical(wxCommandEvent &)
+{
+    bool state = !symmetrical->IsChecked();
 
-	margin_ctrl[RIGHT]->Enable(state);
-	margin_ctrl[BOTTOM]->Enable(state);
+    margin_ctrl[RIGHT]->Enable(state);
+    margin_ctrl[BOTTOM]->Enable(state);
 
-	if (!state) {
-		margin_ctrl[RIGHT]->SetValue(margin_ctrl[LEFT]->GetValue());
-		margin_ctrl[BOTTOM]->SetValue(margin_ctrl[TOP]->GetValue());
-	}
+    if (!state) {
+        margin_ctrl[RIGHT]->SetValue(margin_ctrl[LEFT]->GetValue());
+        margin_ctrl[BOTTOM]->SetValue(margin_ctrl[TOP]->GetValue());
+    }
 }
 
-void DialogResample::OnMarginChange(wxSpinCtrl *src, wxSpinCtrl *dst) {
-	if (symmetrical->IsChecked())
-		dst->SetValue(src->GetValue());
+void DialogResample::OnMarginChange(wxSpinCtrl *src, wxSpinCtrl *dst)
+{
+    if (symmetrical->IsChecked())
+        dst->SetValue(src->GetValue());
 }
 }
 
-bool PromptForResampleSettings(agi::Context *c, ResampleSettings &settings) {
-	return DialogResample(c, settings).d.ShowModal() == wxID_OK;
+bool PromptForResampleSettings(agi::Context *c, ResampleSettings &settings)
+{
+    return DialogResample(c, settings).d.ShowModal() == wxID_OK;
 }
diff --git a/src/dialog_search_replace.cpp b/src/dialog_search_replace.cpp
index 7c2cb91b065f4ef9763f17e5a211d771d45c838e..9769f2334e95b78cf893add2c45898e2076b3271 100644
--- a/src/dialog_search_replace.cpp
+++ b/src/dialog_search_replace.cpp
@@ -42,154 +42,159 @@
 #include <wx/textctrl.h>
 #include <wx/valgen.h>
 
-DialogSearchReplace::DialogSearchReplace(agi::Context* c, bool replace)
-: wxDialog(c->parent, -1, replace ? _("Replace") : _("Find"))
-, c(c)
-, settings(agi::make_unique<SearchReplaceSettings>())
-, has_replace(replace)
+DialogSearchReplace::DialogSearchReplace(agi::Context *c, bool replace)
+    : wxDialog(c->parent, -1, replace ? _("Replace") : _("Find"))
+    , c(c)
+    , settings(agi::make_unique<SearchReplaceSettings>())
+    , has_replace(replace)
 {
-	auto recent_find(lagi_MRU_wxAS("Find"));
-	auto recent_replace(lagi_MRU_wxAS("Replace"));
-
-	settings->field = static_cast<SearchReplaceSettings::Field>(OPT_GET("Tool/Search Replace/Field")->GetInt());
-	settings->limit_to = static_cast<SearchReplaceSettings::Limit>(OPT_GET("Tool/Search Replace/Affect")->GetInt());
-	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();
-	settings->skip_tags = OPT_GET("Tool/Search Replace/Skip Tags")->GetBool();
-	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 | wxTE_PROCESS_ENTER, StringBinder(&settings->find));
-	find_edit->SetMaxLength(0);
-	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 | wxTE_PROCESS_ENTER, StringBinder(&settings->replace_with));
-		replace_edit->SetMaxLength(0);
-		find_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), wxSizerFlags().Center().Left());
-		find_sizer->Add(replace_edit);
-	}
-
-	auto options_sizer = new wxBoxSizer(wxVERTICAL);
-	options_sizer->Add(new wxCheckBox(this, -1, _("&Match case"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->match_case)), wxSizerFlags().Border(wxBOTTOM));
-	options_sizer->Add(new wxCheckBox(this, -1, _("&Use regular expressions"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->use_regex)), wxSizerFlags().Border(wxBOTTOM));
-	options_sizer->Add(new wxCheckBox(this, -1, _("&Skip Comments"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->ignore_comments)), wxSizerFlags().Border(wxBOTTOM));
-	options_sizer->Add(new wxCheckBox(this, -1, _("S&kip Override Tags"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->skip_tags)));
-
-	auto left_sizer = new wxBoxSizer(wxVERTICAL);
-	left_sizer->Add(find_sizer, wxSizerFlags().DoubleBorder(wxBOTTOM));
-	left_sizer->Add(options_sizer);
-
-	wxString field[] = { _("&Text"), _("St&yle"), _("A&ctor"), _("&Effect") };
-	wxString affect[] = { _("A&ll rows"), _("Selected &rows") };
-	auto limit_sizer = new wxBoxSizer(wxHORIZONTAL);
-	limit_sizer->Add(new wxRadioBox(this, -1, _("In Field"), wxDefaultPosition, wxDefaultSize, countof(field), field, 0, wxRA_SPECIFY_COLS, MakeEnumBinder(&settings->field)), wxSizerFlags().Border(wxRIGHT));
-	limit_sizer->Add(new wxRadioBox(this, -1, _("Limit to"), wxDefaultPosition, wxDefaultSize, countof(affect), affect, 0, wxRA_SPECIFY_COLS, MakeEnumBinder(&settings->limit_to)));
-
-	auto find_next = new wxButton(this, -1, _("&Find next"));
-	auto replace_next = new wxButton(this, -1, _("Replace &next"));
-	auto replace_all = new wxButton(this, -1, _("Replace &all"));
-	find_next->SetDefault();
-
-	auto button_sizer = new wxBoxSizer(wxVERTICAL);
-	button_sizer->Add(find_next, wxSizerFlags().Border(wxBOTTOM));
-	button_sizer->Add(replace_next, wxSizerFlags().Border(wxBOTTOM));
-	button_sizer->Add(replace_all, wxSizerFlags().Border(wxBOTTOM));
-	button_sizer->Add(new wxButton(this, wxID_CANCEL));
-
-	if (!has_replace) {
-		button_sizer->Hide(replace_next);
-		button_sizer->Hide(replace_all);
-	}
-
-	auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
-	top_sizer->Add(left_sizer, wxSizerFlags().Border());
-	top_sizer->Add(button_sizer, wxSizerFlags().Border());
-
-	auto main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(top_sizer);
-	main_sizer->Add(limit_sizer, wxSizerFlags().Border());
-	SetSizerAndFit(main_sizer);
-	CenterOnParent();
-
-	TransferDataToWindow();
-	find_edit->SetFocus();
-	find_edit->SelectAll();
-
-	find_edit->Bind(wxEVT_TEXT_ENTER, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::FindNext));
-	if (has_replace)
-	  replace_edit->Bind(wxEVT_TEXT_ENTER, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceNext));
-	find_next->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::FindNext));
-	replace_next->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceNext));
-	replace_all->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceAll));
+    auto recent_find(lagi_MRU_wxAS("Find"));
+    auto recent_replace(lagi_MRU_wxAS("Replace"));
+
+    settings->field = static_cast<SearchReplaceSettings::Field>(OPT_GET("Tool/Search Replace/Field")->GetInt());
+    settings->limit_to = static_cast<SearchReplaceSettings::Limit>(OPT_GET("Tool/Search Replace/Affect")->GetInt());
+    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();
+    settings->skip_tags = OPT_GET("Tool/Search Replace/Skip Tags")->GetBool();
+    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 | wxTE_PROCESS_ENTER, StringBinder(&settings->find));
+    find_edit->SetMaxLength(0);
+    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 | wxTE_PROCESS_ENTER, StringBinder(&settings->replace_with));
+        replace_edit->SetMaxLength(0);
+        find_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), wxSizerFlags().Center().Left());
+        find_sizer->Add(replace_edit);
+    }
+
+    auto options_sizer = new wxBoxSizer(wxVERTICAL);
+    options_sizer->Add(new wxCheckBox(this, -1, _("&Match case"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->match_case)), wxSizerFlags().Border(wxBOTTOM));
+    options_sizer->Add(new wxCheckBox(this, -1, _("&Use regular expressions"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->use_regex)), wxSizerFlags().Border(wxBOTTOM));
+    options_sizer->Add(new wxCheckBox(this, -1, _("&Skip Comments"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->ignore_comments)), wxSizerFlags().Border(wxBOTTOM));
+    options_sizer->Add(new wxCheckBox(this, -1, _("S&kip Override Tags"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&settings->skip_tags)));
+
+    auto left_sizer = new wxBoxSizer(wxVERTICAL);
+    left_sizer->Add(find_sizer, wxSizerFlags().DoubleBorder(wxBOTTOM));
+    left_sizer->Add(options_sizer);
+
+    wxString field[] = { _("&Text"), _("St&yle"), _("A&ctor"), _("&Effect") };
+    wxString affect[] = { _("A&ll rows"), _("Selected &rows") };
+    auto limit_sizer = new wxBoxSizer(wxHORIZONTAL);
+    limit_sizer->Add(new wxRadioBox(this, -1, _("In Field"), wxDefaultPosition, wxDefaultSize, countof(field), field, 0, wxRA_SPECIFY_COLS, MakeEnumBinder(&settings->field)), wxSizerFlags().Border(wxRIGHT));
+    limit_sizer->Add(new wxRadioBox(this, -1, _("Limit to"), wxDefaultPosition, wxDefaultSize, countof(affect), affect, 0, wxRA_SPECIFY_COLS, MakeEnumBinder(&settings->limit_to)));
+
+    auto find_next = new wxButton(this, -1, _("&Find next"));
+    auto replace_next = new wxButton(this, -1, _("Replace &next"));
+    auto replace_all = new wxButton(this, -1, _("Replace &all"));
+    find_next->SetDefault();
+
+    auto button_sizer = new wxBoxSizer(wxVERTICAL);
+    button_sizer->Add(find_next, wxSizerFlags().Border(wxBOTTOM));
+    button_sizer->Add(replace_next, wxSizerFlags().Border(wxBOTTOM));
+    button_sizer->Add(replace_all, wxSizerFlags().Border(wxBOTTOM));
+    button_sizer->Add(new wxButton(this, wxID_CANCEL));
+
+    if (!has_replace) {
+        button_sizer->Hide(replace_next);
+        button_sizer->Hide(replace_all);
+    }
+
+    auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
+    top_sizer->Add(left_sizer, wxSizerFlags().Border());
+    top_sizer->Add(button_sizer, wxSizerFlags().Border());
+
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(top_sizer);
+    main_sizer->Add(limit_sizer, wxSizerFlags().Border());
+    SetSizerAndFit(main_sizer);
+    CenterOnParent();
+
+    TransferDataToWindow();
+    find_edit->SetFocus();
+    find_edit->SelectAll();
+
+    find_edit->Bind(wxEVT_TEXT_ENTER, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::FindNext));
+    if (has_replace)
+        replace_edit->Bind(wxEVT_TEXT_ENTER, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceNext));
+    find_next->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::FindNext));
+    replace_next->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceNext));
+    replace_all->Bind(wxEVT_BUTTON, std::bind(&DialogSearchReplace::FindReplace, this, &SearchReplaceEngine::ReplaceAll));
 }
 
-DialogSearchReplace::~DialogSearchReplace() {
+DialogSearchReplace::~DialogSearchReplace()
+{
 }
 
-void DialogSearchReplace::FindReplace(bool (SearchReplaceEngine::*func)()) {
-	TransferDataFromWindow();
-
-	if (settings->find.empty())
-		return;
-
-	c->search->Configure(*settings);
-	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", settings->find);
-	if (has_replace)
-		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);
-	OPT_SET("Tool/Search Replace/Skip Comments")->SetBool(settings->ignore_comments);
-	OPT_SET("Tool/Search Replace/Skip Tags")->SetBool(settings->skip_tags);
-	OPT_SET("Tool/Search Replace/Field")->SetInt(static_cast<int>(settings->field));
-	OPT_SET("Tool/Search Replace/Affect")->SetInt(static_cast<int>(settings->limit_to));
-
-	UpdateDropDowns();
+void DialogSearchReplace::FindReplace(bool (SearchReplaceEngine::*func)())
+{
+    TransferDataFromWindow();
+
+    if (settings->find.empty())
+        return;
+
+    c->search->Configure(*settings);
+    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", settings->find);
+    if (has_replace)
+        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);
+    OPT_SET("Tool/Search Replace/Skip Comments")->SetBool(settings->ignore_comments);
+    OPT_SET("Tool/Search Replace/Skip Tags")->SetBool(settings->skip_tags);
+    OPT_SET("Tool/Search Replace/Field")->SetInt(static_cast<int>(settings->field));
+    OPT_SET("Tool/Search Replace/Affect")->SetInt(static_cast<int>(settings->limit_to));
+
+    UpdateDropDowns();
 }
 
-static void update_mru(wxComboBox *cb, const char *mru_name) {
-	cb->Freeze();
-	cb->Clear();
-	cb->Append(lagi_MRU_wxAS(mru_name));
-	if (!cb->IsListEmpty())
-		cb->SetSelection(0);
-	cb->Thaw();
+static void update_mru(wxComboBox *cb, const char *mru_name)
+{
+    cb->Freeze();
+    cb->Clear();
+    cb->Append(lagi_MRU_wxAS(mru_name));
+    if (!cb->IsListEmpty())
+        cb->SetSelection(0);
+    cb->Thaw();
 }
 
-void DialogSearchReplace::UpdateDropDowns() {
-	update_mru(find_edit, "Find");
+void DialogSearchReplace::UpdateDropDowns()
+{
+    update_mru(find_edit, "Find");
 
-	if (has_replace)
-		update_mru(replace_edit, "Replace");
+    if (has_replace)
+        update_mru(replace_edit, "Replace");
 }
 
-void DialogSearchReplace::Show(agi::Context *context, bool replace) {
-	static DialogSearchReplace *diag = nullptr;
+void DialogSearchReplace::Show(agi::Context *context, bool replace)
+{
+    static DialogSearchReplace *diag = nullptr;
 
-	if (diag && replace != diag->has_replace) {
-		// Already opened, but wrong type - destroy and create the right one
-		diag->Destroy();
-		diag = nullptr;
-	}
+    if (diag && replace != diag->has_replace) {
+        // Already opened, but wrong type - destroy and create the right one
+        diag->Destroy();
+        diag = nullptr;
+    }
 
-	if (!diag)
-		diag = new DialogSearchReplace(context, replace);
+    if (!diag)
+        diag = new DialogSearchReplace(context, replace);
 
-	diag->find_edit->SetFocus();
-	diag->find_edit->SelectAll();
-	diag->wxDialog::Show();
-	diag->Raise();
+    diag->find_edit->SetFocus();
+    diag->find_edit->SelectAll();
+    diag->wxDialog::Show();
+    diag->Raise();
 }
diff --git a/src/dialog_search_replace.h b/src/dialog_search_replace.h
index 2d231e7ec5528c0accd1d73d0a2c31b686ae9e3d..d320a6a958f6ea289aa96932e0e73f448b62486b 100644
--- a/src/dialog_search_replace.h
+++ b/src/dialog_search_replace.h
@@ -29,18 +29,18 @@ struct SearchReplaceSettings;
 class wxComboBox;
 
 class DialogSearchReplace final : public wxDialog {
-	agi::Context *c;
-	std::unique_ptr<SearchReplaceSettings> settings;
-	bool has_replace;
-	wxComboBox *find_edit;
-	wxComboBox *replace_edit;
+    agi::Context *c;
+    std::unique_ptr<SearchReplaceSettings> settings;
+    bool has_replace;
+    wxComboBox *find_edit;
+    wxComboBox *replace_edit;
 
-	void UpdateDropDowns();
-	void FindReplace(bool (SearchReplaceEngine::*func)());
+    void UpdateDropDowns();
+    void FindReplace(bool (SearchReplaceEngine::*func)());
 
 public:
-	static void Show(agi::Context *context, bool with_replace);
+    static void Show(agi::Context *context, bool with_replace);
 
-	DialogSearchReplace(agi::Context* c, bool with_replace);
-	~DialogSearchReplace();
+    DialogSearchReplace(agi::Context *c, bool with_replace);
+    ~DialogSearchReplace();
 };
diff --git a/src/dialog_selected_choices.cpp b/src/dialog_selected_choices.cpp
index b5b3acc2186d29ec215328c214f4d35b154050fc..b548383a807cc63ae4f528e4d5c0280d9e9c370f 100644
--- a/src/dialog_selected_choices.cpp
+++ b/src/dialog_selected_choices.cpp
@@ -20,31 +20,32 @@
 #include <wx/listbox.h>
 #include <wx/sizer.h>
 
-int GetSelectedChoices(wxWindow *parent, wxArrayInt& selections, wxString const& message, wxString const& caption, wxArrayString const& choices) {
-	wxMultiChoiceDialog dialog(parent, message, caption, choices);
+int GetSelectedChoices(wxWindow *parent, wxArrayInt &selections, wxString const &message, wxString const &caption, wxArrayString const &choices)
+{
+    wxMultiChoiceDialog dialog(parent, message, caption, choices);
 
-	auto selAll = new wxButton(&dialog, -1, _("Select &All"));
-	selAll->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
-		wxArrayInt sel(choices.size(), 0);
-		std::iota(sel.begin(), sel.end(), 0);
-		dialog.SetSelections(sel);
-	});
+    auto selAll = new wxButton(&dialog, -1, _("Select &All"));
+    selAll->Bind(wxEVT_BUTTON, [&](wxCommandEvent &) {
+        wxArrayInt sel(choices.size(), 0);
+        std::iota(sel.begin(), sel.end(), 0);
+        dialog.SetSelections(sel);
+    });
 
-	auto selNone = new wxButton(&dialog, -1, _("Select &None"));
-	selNone->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { dialog.SetSelections(wxArrayInt()); });
+    auto selNone = new wxButton(&dialog, -1, _("Select &None"));
+    selNone->Bind(wxEVT_BUTTON, [&](wxCommandEvent &) { dialog.SetSelections(wxArrayInt()); });
 
-	auto buttonSizer = new wxBoxSizer(wxHORIZONTAL);
-	buttonSizer->Add(selAll, wxSizerFlags(0).Left());
-	buttonSizer->Add(selNone, wxSizerFlags(0).Right());
+    auto buttonSizer = new wxBoxSizer(wxHORIZONTAL);
+    buttonSizer->Add(selAll, wxSizerFlags(0).Left());
+    buttonSizer->Add(selNone, wxSizerFlags(0).Right());
 
-	auto sizer = dialog.GetSizer();
-	sizer->Insert(2, buttonSizer, wxSizerFlags(0).Center());
-	sizer->Fit(&dialog);
+    auto sizer = dialog.GetSizer();
+    sizer->Insert(2, buttonSizer, wxSizerFlags(0).Center());
+    sizer->Fit(&dialog);
 
-	dialog.SetSelections(selections);
+    dialog.SetSelections(selections);
 
-	if (dialog.ShowModal() != wxID_OK) return -1;
+    if (dialog.ShowModal() != wxID_OK) return -1;
 
-	selections = dialog.GetSelections();
-	return selections.GetCount();
+    selections = dialog.GetSelections();
+    return selections.GetCount();
 }
diff --git a/src/dialog_selection.cpp b/src/dialog_selection.cpp
index 2adfa764cd7bba99dea901ac43433c81a849fa4d..c1afddbafe6bd1b001db9aa2f5c13aa4d33310f8 100644
--- a/src/dialog_selection.cpp
+++ b/src/dialog_selection.cpp
@@ -40,217 +40,222 @@
 
 namespace {
 class DialogSelection final : public wxDialog {
-	agi::Context *con; ///< Project context
+    agi::Context *con; ///< Project context
 
-	wxTextCtrl *match_text; ///< Text to search for
-	wxCheckBox *case_sensitive; ///< Should the search be case-sensitive
-	wxCheckBox *apply_to_dialogue; ///< Select/deselect uncommented lines
-	wxCheckBox *apply_to_comments; ///< Select/deselect commented lines
-	wxRadioButton *select_unmatching_lines; ///< Select lines which don't match instead
-	wxRadioBox *selection_change_type; ///< What sort of action to take on the selection
-	wxRadioBox *dialogue_field; ///< Which dialogue field to look at
-	wxRadioBox *match_mode;
+    wxTextCtrl *match_text; ///< Text to search for
+    wxCheckBox *case_sensitive; ///< Should the search be case-sensitive
+    wxCheckBox *apply_to_dialogue; ///< Select/deselect uncommented lines
+    wxCheckBox *apply_to_comments; ///< Select/deselect commented lines
+    wxRadioButton *select_unmatching_lines; ///< Select lines which don't match instead
+    wxRadioBox *selection_change_type; ///< What sort of action to take on the selection
+    wxRadioBox *dialogue_field; ///< Which dialogue field to look at
+    wxRadioBox *match_mode;
 
-	void Process(wxCommandEvent&);
+    void Process(wxCommandEvent &);
 
-	/// Dialogue/Comment check handler to ensure at least one is always checked
-	/// @param chk The checkbox to check if both are clear
-	void OnDialogueCheckbox(wxCheckBox *chk);
+    /// Dialogue/Comment check handler to ensure at least one is always checked
+    /// @param chk The checkbox to check if both are clear
+    void OnDialogueCheckbox(wxCheckBox *chk);
 
 public:
-	DialogSelection(agi::Context *c);
-	~DialogSelection();
+    DialogSelection(agi::Context *c);
+    ~DialogSelection();
 };
 
 enum class Action {
-	SET = 0,
-	ADD,
-	SUB,
-	INTERSECT
+    SET = 0,
+    ADD,
+    SUB,
+    INTERSECT
 };
 
 enum Mode {
-	EXACT = 0,
-	CONTAINS,
-	REGEXP
+    EXACT = 0,
+    CONTAINS,
+    REGEXP
 };
 
-std::set<AssDialogue*> process(std::string const& match_text, bool match_case, Mode mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) {
-	SearchReplaceSettings settings = {
-		match_text,
-		std::string(),
-		static_cast<SearchReplaceSettings::Field>(field_n),
-		SearchReplaceSettings::Limit::ALL,
-		match_case,
-		mode == Mode::REGEXP,
-		false,
-		false,
-		mode == Mode::EXACT
-	};
-
-	auto predicate = SearchReplaceEngine::GetMatcher(settings);
-
-	std::set<AssDialogue*> matches;
-	for (auto& diag : ass->Events) {
-		if (diag.Comment && !comments) continue;
-		if (!diag.Comment && !dialogue) continue;
-
-		if (invert != predicate(&diag, 0))
-			matches.insert(&diag);
-	}
-
-	return matches;
+std::set<AssDialogue *> process(std::string const &match_text, bool match_case, Mode mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass)
+{
+    SearchReplaceSettings settings = {
+        match_text,
+        std::string(),
+        static_cast<SearchReplaceSettings::Field>(field_n),
+        SearchReplaceSettings::Limit::ALL,
+        match_case,
+        mode == Mode::REGEXP,
+        false,
+        false,
+        mode == Mode::EXACT
+    };
+
+    auto predicate = SearchReplaceEngine::GetMatcher(settings);
+
+    std::set<AssDialogue *> matches;
+    for (auto &diag : ass->Events) {
+        if (diag.Comment && !comments) continue;
+        if (!diag.Comment && !dialogue) continue;
+
+        if (invert != predicate(&diag, 0))
+            matches.insert(&diag);
+    }
+
+    return matches;
 }
 
 DialogSelection::DialogSelection(agi::Context *c) :
-wxDialog (c->parent, -1, _("Select"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
-, con(c)
+    wxDialog (c->parent, -1, _("Select"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
+    , con(c)
 {
-	SetIcon(GETICON(select_lines_button_16));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-
-	wxSizerFlags main_flags = wxSizerFlags().Expand().Border();
-
-	wxRadioButton *select_matching_lines = nullptr;
-	{
-		wxSizer *match_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Match"));
-		{
-			wxSizerFlags radio_flags = wxSizerFlags().Border(wxLEFT | wxRIGHT);
-			wxSizer *match_radio_line = new wxBoxSizer(wxHORIZONTAL);
-			match_radio_line->Add(select_matching_lines = new wxRadioButton(this, -1, _("&Matches"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP), radio_flags);
-			match_radio_line->Add(select_unmatching_lines = new wxRadioButton(this, -1, _("&Doesn't Match")), radio_flags);
-			match_radio_line->Add(case_sensitive = new wxCheckBox(this, -1, _("Match c&ase")), radio_flags);
-			match_sizer->Add(match_radio_line);
-		}
-		match_sizer->Add(match_text = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Select Lines/Text")->GetString())), main_flags);
-
-		main_sizer->Add(match_sizer, main_flags);
-	}
-
-	{
-		wxString modes[] = { _("&Exact match"), _("&Contains"), _("&Regular Expression match") };
-		main_sizer->Add(match_mode = new wxRadioBox(this, -1, _("Mode"), wxDefaultPosition, wxDefaultSize, 3, modes, 1), main_flags);
-	}
-
-	{
-		wxString fields[] = { _("&Text"), _("&Style"), _("Act&or"), _("E&ffect") };
-		main_sizer->Add(dialogue_field = new wxRadioBox(this, -1, _("In Field"), wxDefaultPosition, wxDefaultSize, 4, fields), main_flags);
-	}
-
-	{
-		wxSizer *comment_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Match dialogues/comments"));
-		comment_sizer->Add(apply_to_dialogue = new wxCheckBox(this, -1, _("D&ialogues")), wxSizerFlags().Border());
-		comment_sizer->Add(apply_to_comments = new wxCheckBox(this, -1, _("Comme&nts")), wxSizerFlags().Border());
-		main_sizer->Add(comment_sizer, main_flags);
-	}
-
-	{
-		wxString actions[] = { _("Set se&lection"), _("&Add to selection"), _("S&ubtract from selection"), _("Intersect &with selection") };
-		main_sizer->Add(selection_change_type = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, 4, actions, 1), main_flags);
-	}
-
-	main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), main_flags);
-
-	SetSizerAndFit(main_sizer);
-	CenterOnParent();
-
-	dialogue_field->SetSelection(OPT_GET("Tool/Select Lines/Field")->GetInt());
-	selection_change_type->SetSelection(OPT_GET("Tool/Select Lines/Action")->GetInt());
-	case_sensitive->SetValue(OPT_GET("Tool/Select Lines/Match/Case")->GetBool());
-	apply_to_dialogue->SetValue(OPT_GET("Tool/Select Lines/Match/Dialogue")->GetBool());
-	apply_to_comments->SetValue(OPT_GET("Tool/Select Lines/Match/Comment")->GetBool());
-	select_unmatching_lines->SetValue(!!OPT_GET("Tool/Select Lines/Condition")->GetInt());
-	select_matching_lines->SetValue(!select_unmatching_lines->GetValue());
-	match_mode->SetSelection(OPT_GET("Tool/Select Lines/Mode")->GetInt());
-
-	Bind(wxEVT_BUTTON, &DialogSelection::Process, this, wxID_OK);
-	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Select Lines"), wxID_HELP);
-	apply_to_comments->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_dialogue));
-	apply_to_dialogue->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_comments));
+    SetIcon(GETICON(select_lines_button_16));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+
+    wxSizerFlags main_flags = wxSizerFlags().Expand().Border();
+
+    wxRadioButton *select_matching_lines = nullptr;
+    {
+        wxSizer *match_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Match"));
+        {
+            wxSizerFlags radio_flags = wxSizerFlags().Border(wxLEFT | wxRIGHT);
+            wxSizer *match_radio_line = new wxBoxSizer(wxHORIZONTAL);
+            match_radio_line->Add(select_matching_lines = new wxRadioButton(this, -1, _("&Matches"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP), radio_flags);
+            match_radio_line->Add(select_unmatching_lines = new wxRadioButton(this, -1, _("&Doesn't Match")), radio_flags);
+            match_radio_line->Add(case_sensitive = new wxCheckBox(this, -1, _("Match c&ase")), radio_flags);
+            match_sizer->Add(match_radio_line);
+        }
+        match_sizer->Add(match_text = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Select Lines/Text")->GetString())), main_flags);
+
+        main_sizer->Add(match_sizer, main_flags);
+    }
+
+    {
+        wxString modes[] = { _("&Exact match"), _("&Contains"), _("&Regular Expression match") };
+        main_sizer->Add(match_mode = new wxRadioBox(this, -1, _("Mode"), wxDefaultPosition, wxDefaultSize, 3, modes, 1), main_flags);
+    }
+
+    {
+        wxString fields[] = { _("&Text"), _("&Style"), _("Act&or"), _("E&ffect") };
+        main_sizer->Add(dialogue_field = new wxRadioBox(this, -1, _("In Field"), wxDefaultPosition, wxDefaultSize, 4, fields), main_flags);
+    }
+
+    {
+        wxSizer *comment_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Match dialogues/comments"));
+        comment_sizer->Add(apply_to_dialogue = new wxCheckBox(this, -1, _("D&ialogues")), wxSizerFlags().Border());
+        comment_sizer->Add(apply_to_comments = new wxCheckBox(this, -1, _("Comme&nts")), wxSizerFlags().Border());
+        main_sizer->Add(comment_sizer, main_flags);
+    }
+
+    {
+        wxString actions[] = { _("Set se&lection"), _("&Add to selection"), _("S&ubtract from selection"), _("Intersect &with selection") };
+        main_sizer->Add(selection_change_type = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, 4, actions, 1), main_flags);
+    }
+
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), main_flags);
+
+    SetSizerAndFit(main_sizer);
+    CenterOnParent();
+
+    dialogue_field->SetSelection(OPT_GET("Tool/Select Lines/Field")->GetInt());
+    selection_change_type->SetSelection(OPT_GET("Tool/Select Lines/Action")->GetInt());
+    case_sensitive->SetValue(OPT_GET("Tool/Select Lines/Match/Case")->GetBool());
+    apply_to_dialogue->SetValue(OPT_GET("Tool/Select Lines/Match/Dialogue")->GetBool());
+    apply_to_comments->SetValue(OPT_GET("Tool/Select Lines/Match/Comment")->GetBool());
+    select_unmatching_lines->SetValue(!!OPT_GET("Tool/Select Lines/Condition")->GetInt());
+    select_matching_lines->SetValue(!select_unmatching_lines->GetValue());
+    match_mode->SetSelection(OPT_GET("Tool/Select Lines/Mode")->GetInt());
+
+    Bind(wxEVT_BUTTON, &DialogSelection::Process, this, wxID_OK);
+    Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Select Lines"), wxID_HELP);
+    apply_to_comments->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_dialogue));
+    apply_to_dialogue->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_comments));
 }
 
-DialogSelection::~DialogSelection() {
-	OPT_SET("Tool/Select Lines/Text")->SetString(from_wx(match_text->GetValue()));
-	OPT_SET("Tool/Select Lines/Condition")->SetInt(select_unmatching_lines->GetValue());
-	OPT_SET("Tool/Select Lines/Field")->SetInt(dialogue_field->GetSelection());
-	OPT_SET("Tool/Select Lines/Action")->SetInt(selection_change_type->GetSelection());
-	OPT_SET("Tool/Select Lines/Mode")->SetInt(match_mode->GetSelection());
-	OPT_SET("Tool/Select Lines/Match/Case")->SetBool(case_sensitive->IsChecked());
-	OPT_SET("Tool/Select Lines/Match/Dialogue")->SetBool(apply_to_dialogue->IsChecked());
-	OPT_SET("Tool/Select Lines/Match/Comment")->SetBool(apply_to_comments->IsChecked());
+DialogSelection::~DialogSelection()
+{
+    OPT_SET("Tool/Select Lines/Text")->SetString(from_wx(match_text->GetValue()));
+    OPT_SET("Tool/Select Lines/Condition")->SetInt(select_unmatching_lines->GetValue());
+    OPT_SET("Tool/Select Lines/Field")->SetInt(dialogue_field->GetSelection());
+    OPT_SET("Tool/Select Lines/Action")->SetInt(selection_change_type->GetSelection());
+    OPT_SET("Tool/Select Lines/Mode")->SetInt(match_mode->GetSelection());
+    OPT_SET("Tool/Select Lines/Match/Case")->SetBool(case_sensitive->IsChecked());
+    OPT_SET("Tool/Select Lines/Match/Dialogue")->SetBool(apply_to_dialogue->IsChecked());
+    OPT_SET("Tool/Select Lines/Match/Comment")->SetBool(apply_to_comments->IsChecked());
 }
 
-void DialogSelection::Process(wxCommandEvent&) {
-	std::set<AssDialogue*> matches;
-
-	try {
-		matches = process(
-			from_wx(match_text->GetValue()), case_sensitive->IsChecked(),
-			static_cast<Mode>(match_mode->GetSelection()), select_unmatching_lines->GetValue(),
-			apply_to_comments->IsChecked(), apply_to_dialogue->IsChecked(),
-			dialogue_field->GetSelection(), con->ass.get());
-	}
-	catch (agi::Exception const&) {
-		Close();
-		return;
-	}
-
-	auto action = static_cast<Action>(selection_change_type->GetSelection());
-
-	Selection old_sel, new_sel;
-	if (action != Action::SET)
-		old_sel = con->selectionController->GetSelectedSet();
-
-	wxString message;
-	size_t count = 0;
-	switch (action) {
-		case Action::SET:
-			new_sel = std::move(matches);
-			message = (count = new_sel.size())
-				? fmt_plural(count, "Selection was set to one line", "Selection was set to %u lines", count)
-				: _("Selection was set to no lines");
-			break;
-
-		case Action::ADD:
-			boost::set_union(old_sel, matches, inserter(new_sel, new_sel.begin()));
-			message = (count = new_sel.size() - old_sel.size())
-				? fmt_plural(count, "One line was added to selection", "%u lines were added to selection", count)
-				: _("No lines were added to selection");
-			break;
-
-		case Action::SUB:
-			boost::set_difference(old_sel, matches, inserter(new_sel, new_sel.begin()));
-			goto sub_message;
-
-		case Action::INTERSECT:
-			boost::set_intersection(old_sel, matches, inserter(new_sel, new_sel.begin()));
-			sub_message:
-			message = (count = old_sel.size() - new_sel.size())
-				? fmt_plural(count, "One line was removed from selection", "%u lines were removed from selection", count)
-				: _("No lines were removed from selection");
-			break;
-	}
-
-	if (count == 0)
-		wxMessageBox(message, _("Selection"), wxOK | wxCENTER, this);
-	else
-		con->frame->StatusTimeout(message);
-
-	AssDialogue *new_active = con->selectionController->GetActiveLine();
-	if (new_sel.size() && !new_sel.count(new_active))
-		new_active = *new_sel.begin();
-	con->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
-
-	Close();
+void DialogSelection::Process(wxCommandEvent &)
+{
+    std::set<AssDialogue *> matches;
+
+    try {
+        matches = process(
+                      from_wx(match_text->GetValue()), case_sensitive->IsChecked(),
+                      static_cast<Mode>(match_mode->GetSelection()), select_unmatching_lines->GetValue(),
+                      apply_to_comments->IsChecked(), apply_to_dialogue->IsChecked(),
+                      dialogue_field->GetSelection(), con->ass.get());
+    }
+    catch (agi::Exception const &) {
+        Close();
+        return;
+    }
+
+    auto action = static_cast<Action>(selection_change_type->GetSelection());
+
+    Selection old_sel, new_sel;
+    if (action != Action::SET)
+        old_sel = con->selectionController->GetSelectedSet();
+
+    wxString message;
+    size_t count = 0;
+    switch (action) {
+    case Action::SET:
+        new_sel = std::move(matches);
+        message = (count = new_sel.size())
+                  ? fmt_plural(count, "Selection was set to one line", "Selection was set to %u lines", count)
+                  : _("Selection was set to no lines");
+        break;
+
+    case Action::ADD:
+        boost::set_union(old_sel, matches, inserter(new_sel, new_sel.begin()));
+        message = (count = new_sel.size() - old_sel.size())
+                  ? fmt_plural(count, "One line was added to selection", "%u lines were added to selection", count)
+                  : _("No lines were added to selection");
+        break;
+
+    case Action::SUB:
+        boost::set_difference(old_sel, matches, inserter(new_sel, new_sel.begin()));
+        goto sub_message;
+
+    case Action::INTERSECT:
+        boost::set_intersection(old_sel, matches, inserter(new_sel, new_sel.begin()));
+sub_message:
+        message = (count = old_sel.size() - new_sel.size())
+                  ? fmt_plural(count, "One line was removed from selection", "%u lines were removed from selection", count)
+                  : _("No lines were removed from selection");
+        break;
+    }
+
+    if (count == 0)
+        wxMessageBox(message, _("Selection"), wxOK | wxCENTER, this);
+    else
+        con->frame->StatusTimeout(message);
+
+    AssDialogue *new_active = con->selectionController->GetActiveLine();
+    if (new_sel.size() && !new_sel.count(new_active))
+        new_active = *new_sel.begin();
+    con->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
+
+    Close();
 }
 
-void DialogSelection::OnDialogueCheckbox(wxCheckBox *chk) {
-	if(!apply_to_dialogue->IsChecked() && !apply_to_comments->GetValue())
-		chk->SetValue(true);
+void DialogSelection::OnDialogueCheckbox(wxCheckBox *chk)
+{
+    if (!apply_to_dialogue->IsChecked() && !apply_to_comments->GetValue())
+        chk->SetValue(true);
 }
 }
 
-void ShowSelectLinesDialog(agi::Context *c) {
-	c->dialog->Show<DialogSelection>(c);
+void ShowSelectLinesDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogSelection>(c);
 }
diff --git a/src/dialog_shift_times.cpp b/src/dialog_shift_times.cpp
index 268e307ea7798ef659b66edabda5c8eecc0498ed..118af7d83f3cb0538e8c9abd8ef9f17527575bcc 100644
--- a/src/dialog_shift_times.cpp
+++ b/src/dialog_shift_times.cpp
@@ -50,384 +50,397 @@
 
 namespace {
 class DialogShiftTimes final : public wxDialog {
-	agi::Context *context;
-
-	agi::fs::path history_filename;
-	json::Array history;
-	agi::vfr::Framerate fps;
-	agi::signal::Connection timecodes_loaded_slot;
-	agi::signal::Connection selected_set_changed_slot;
-
-	TimeEdit *shift_time;
-	wxTextCtrl *shift_frames;
-	wxRadioButton *shift_by_time;
-	wxRadioButton *shift_by_frames;
-	wxRadioButton *shift_forward;
-	wxRadioButton *shift_backward;
-	wxRadioBox *selection_mode;
-	wxRadioBox *time_fields;
-	wxListBox *history_box;
-
-	void SaveHistory(json::Array shifted_blocks);
-	void LoadHistory();
-	void Process(wxCommandEvent&);
-	int Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type);
-
-	void OnClear(wxCommandEvent&);
-	void OnByTime(wxCommandEvent&);
-	void OnByFrames(wxCommandEvent&);
-	void OnHistoryClick(wxCommandEvent&);
-
-	void OnSelectedSetChanged();
-	void OnTimecodesLoaded(agi::vfr::Framerate const& new_fps);
+    agi::Context *context;
+
+    agi::fs::path history_filename;
+    json::Array history;
+    agi::vfr::Framerate fps;
+    agi::signal::Connection timecodes_loaded_slot;
+    agi::signal::Connection selected_set_changed_slot;
+
+    TimeEdit *shift_time;
+    wxTextCtrl *shift_frames;
+    wxRadioButton *shift_by_time;
+    wxRadioButton *shift_by_frames;
+    wxRadioButton *shift_forward;
+    wxRadioButton *shift_backward;
+    wxRadioBox *selection_mode;
+    wxRadioBox *time_fields;
+    wxListBox *history_box;
+
+    void SaveHistory(json::Array shifted_blocks);
+    void LoadHistory();
+    void Process(wxCommandEvent &);
+    int Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type);
+
+    void OnClear(wxCommandEvent &);
+    void OnByTime(wxCommandEvent &);
+    void OnByFrames(wxCommandEvent &);
+    void OnHistoryClick(wxCommandEvent &);
+
+    void OnSelectedSetChanged();
+    void OnTimecodesLoaded(agi::vfr::Framerate const &new_fps);
 
 public:
-	DialogShiftTimes(agi::Context *context);
-	~DialogShiftTimes();
+    DialogShiftTimes(agi::Context *context);
+    ~DialogShiftTimes();
 };
 
-static wxString get_history_string(json::Object &obj) {
-	wxString filename = to_wx(obj["filename"]);
-	if (filename.empty())
-		filename = _("unsaved");
-
-	wxString shift_amount(to_wx(obj["amount"]));
-	if (!obj["is by time"])
-		shift_amount = fmt_tl("%s frames", shift_amount);
-
-	wxString shift_direction = obj["is backward"] ? _("backward") : _("forward");
-
-	int64_t time_field = obj["fields"];
-	wxString fields =
-		time_field == 0 ? _("s+e") :
-		time_field == 1 ? _("s")   :
-		                  _("e")   ;
-
-	json::Array& sel = obj["selection"];
-	wxString lines;
-
-	int64_t sel_mode = obj["mode"];
-	if (sel_mode == 0)
-		lines = _("all");
-	else if (sel_mode == 2) {
-		if (!sel.empty())
-			lines = fmt_tl("from %d onward", (int64_t)static_cast<json::Object&>(sel.front())["start"]);
-	}
-	else {
-		lines += _("sel ");
-		for (auto it = sel.begin(); it != sel.end(); ++it) {
-			json::Object& range = *it;
-			int beg = (int64_t)range["start"];
-			int end = (int64_t)range["end"];
-			if (beg == end)
-				lines += std::to_wstring(beg);
-			else
-				lines += fmt_wx("%d-%d", beg, end);
-			if (it + 1 != sel.end())
-				lines += ";";
-		}
-	}
-
-	return fmt_wx("%s, %s %s, %s, %s", filename, shift_amount, shift_direction, fields, lines);
+static wxString get_history_string(json::Object &obj)
+{
+    wxString filename = to_wx(obj["filename"]);
+    if (filename.empty())
+        filename = _("unsaved");
+
+    wxString shift_amount(to_wx(obj["amount"]));
+    if (!obj["is by time"])
+        shift_amount = fmt_tl("%s frames", shift_amount);
+
+    wxString shift_direction = obj["is backward"] ? _("backward") : _("forward");
+
+    int64_t time_field = obj["fields"];
+    wxString fields =
+        time_field == 0 ? _("s+e") :
+        time_field == 1 ? _("s")   :
+        _("e")   ;
+
+    json::Array &sel = obj["selection"];
+    wxString lines;
+
+    int64_t sel_mode = obj["mode"];
+    if (sel_mode == 0)
+        lines = _("all");
+    else if (sel_mode == 2) {
+        if (!sel.empty())
+            lines = fmt_tl("from %d onward", (int64_t)static_cast<json::Object &>(sel.front())["start"]);
+    }
+    else {
+        lines += _("sel ");
+        for (auto it = sel.begin(); it != sel.end(); ++it) {
+            json::Object &range = *it;
+            int beg = (int64_t)range["start"];
+            int end = (int64_t)range["end"];
+            if (beg == end)
+                lines += std::to_wstring(beg);
+            else
+                lines += fmt_wx("%d-%d", beg, end);
+            if (it + 1 != sel.end())
+                lines += ";";
+        }
+    }
+
+    return fmt_wx("%s, %s %s, %s, %s", filename, shift_amount, shift_direction, fields, lines);
 }
 
 DialogShiftTimes::DialogShiftTimes(agi::Context *context)
-: wxDialog(context->parent, -1, _("Shift Times"))
-, context(context)
-, history_filename(config::path->Decode("?user/shift_history.json"))
-, timecodes_loaded_slot(context->project->AddTimecodesListener(&DialogShiftTimes::OnTimecodesLoaded, this))
-, selected_set_changed_slot(context->selectionController->AddSelectionListener(&DialogShiftTimes::OnSelectedSetChanged, this))
+    : wxDialog(context->parent, -1, _("Shift Times"))
+    , context(context)
+    , history_filename(config::path->Decode("?user/shift_history.json"))
+    , timecodes_loaded_slot(context->project->AddTimecodesListener(&DialogShiftTimes::OnTimecodesLoaded, this))
+    , selected_set_changed_slot(context->selectionController->AddSelectionListener(&DialogShiftTimes::OnSelectedSetChanged, this))
 {
-	SetIcon(GETICON(shift_times_toolbutton_16));
-
-	// Create controls
-	shift_by_time = new wxRadioButton(this, -1, _("&Time: "), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
-	shift_by_time->SetToolTip(_("Shift by time"));
-	shift_by_time->Bind(wxEVT_RADIOBUTTON, &DialogShiftTimes::OnByTime, this);
-
-	shift_by_frames = new wxRadioButton(this, -1 , _("&Frames: "));
-	shift_by_frames->SetToolTip(_("Shift by frames"));
-	shift_by_frames->Bind(wxEVT_RADIOBUTTON, &DialogShiftTimes::OnByFrames, this);
-
-	shift_time = new TimeEdit(this, -1, context);
-	shift_time->SetToolTip(_("Enter time in h:mm:ss.cs notation"));
-
-	shift_frames = new wxTextCtrl(this, -1);
-	shift_frames->SetToolTip(_("Enter number of frames to shift by"));
-
-	shift_forward = new wxRadioButton(this, -1, _("For&ward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
-	shift_forward->SetToolTip(_("Shifts subs forward, making them appear later. Use if they are appearing too soon."));
-
-	shift_backward = new wxRadioButton(this, -1, _("&Backward"));
-	shift_backward->SetToolTip(_("Shifts subs backward, making them appear earlier. Use if they are appearing too late."));
-
-	wxString selection_mode_vals[] = { _("&All rows"), _("Selected &rows"), _("Selection &onward") };
-	selection_mode = new wxRadioBox(this, -1, _("Affect"), wxDefaultPosition, wxDefaultSize, 3, selection_mode_vals, 1);
-
-	wxString time_field_vals[] = { _("Start a&nd End times"), _("&Start times only"), _("&End times only") };
-	time_fields = new wxRadioBox(this, -1, _("Times"), wxDefaultPosition, wxDefaultSize, 3, time_field_vals, 1);
-
-	history_box = new wxListBox(this, -1, wxDefaultPosition, wxSize(350, 100), 0, nullptr, wxLB_HSCROLL);
-
-	wxButton *clear_button = new wxButton(this, -1, _("&Clear"));
-	clear_button->Bind(wxEVT_BUTTON, &DialogShiftTimes::OnClear, this);
-
-	// Set initial control states
-	OnTimecodesLoaded(context->project->Timecodes());
-	OnSelectedSetChanged();
-	LoadHistory();
-
-	shift_time->SetTime(OPT_GET("Tool/Shift Times/Time")->GetInt());
-	*shift_frames << (int)OPT_GET("Tool/Shift Times/Frames")->GetInt();
-	shift_by_frames->SetValue(!OPT_GET("Tool/Shift Times/ByTime")->GetBool() && shift_by_frames->IsEnabled());
-	time_fields->SetSelection(OPT_GET("Tool/Shift Times/Type")->GetInt());
-	selection_mode->SetSelection(OPT_GET("Tool/Shift Times/Affect")->GetInt());
-	shift_backward->SetValue(OPT_GET("Tool/Shift Times/Direction")->GetBool());
-
-	if (shift_by_frames->GetValue())
-		shift_time->Disable();
-	else
-		shift_frames->Disable();
-
-	// Position controls
-	wxSizer *shift_amount_sizer = new wxFlexGridSizer(2, 2, 5, 5);
-	shift_amount_sizer->Add(shift_by_time, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
-	shift_amount_sizer->Add(shift_time, wxSizerFlags(1));
-	shift_amount_sizer->Add(shift_by_frames, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
-	shift_amount_sizer->Add(shift_frames, wxSizerFlags(1));
-
-	wxSizer *shift_direction_sizer = new wxBoxSizer(wxHORIZONTAL);
-	shift_direction_sizer->Add(shift_forward, wxSizerFlags(1).Expand());
-	shift_direction_sizer->Add(shift_backward, wxSizerFlags(1).Expand().Border(wxLEFT));
-
-	wxSizer *shift_by_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Shift by"));
-	shift_by_sizer->Add(shift_amount_sizer, wxSizerFlags().Expand());
-	shift_by_sizer->Add(shift_direction_sizer, wxSizerFlags().Expand().Border(wxTOP));
-
-	wxSizer *left_sizer = new wxBoxSizer(wxVERTICAL);
-	left_sizer->Add(shift_by_sizer, wxSizerFlags().Expand().Border(wxBOTTOM));
-	left_sizer->Add(selection_mode, wxSizerFlags().Expand().Border(wxBOTTOM));
-	left_sizer->Add(time_fields, wxSizerFlags().Expand());
-
-	wxSizer *history_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Load from history"));
-	history_sizer->Add(history_box, wxSizerFlags(1).Expand());
-	history_sizer->Add(clear_button, wxSizerFlags().Expand().Border(wxTOP));
-
-	wxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL);
-	top_sizer->Add(left_sizer, wxSizerFlags().Border(wxALL & ~wxRIGHT).Expand());
-	top_sizer->Add(history_sizer, wxSizerFlags().Border().Expand());
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(top_sizer, wxSizerFlags().Border(wxALL & ~wxBOTTOM));
-	main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Right().Border());
-	SetSizerAndFit(main_sizer);
-	CenterOnParent();
-
-	Bind(wxEVT_BUTTON, &DialogShiftTimes::Process, this, wxID_OK);
-	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Shift Times"), wxID_HELP);
-	shift_time->Bind(wxEVT_TEXT_ENTER, &DialogShiftTimes::Process, this);
-	history_box->Bind(wxEVT_LISTBOX_DCLICK, &DialogShiftTimes::OnHistoryClick, this);
+    SetIcon(GETICON(shift_times_toolbutton_16));
+
+    // Create controls
+    shift_by_time = new wxRadioButton(this, -1, _("&Time: "), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
+    shift_by_time->SetToolTip(_("Shift by time"));
+    shift_by_time->Bind(wxEVT_RADIOBUTTON, &DialogShiftTimes::OnByTime, this);
+
+    shift_by_frames = new wxRadioButton(this, -1, _("&Frames: "));
+    shift_by_frames->SetToolTip(_("Shift by frames"));
+    shift_by_frames->Bind(wxEVT_RADIOBUTTON, &DialogShiftTimes::OnByFrames, this);
+
+    shift_time = new TimeEdit(this, -1, context);
+    shift_time->SetToolTip(_("Enter time in h:mm:ss.cs notation"));
+
+    shift_frames = new wxTextCtrl(this, -1);
+    shift_frames->SetToolTip(_("Enter number of frames to shift by"));
+
+    shift_forward = new wxRadioButton(this, -1, _("For&ward"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
+    shift_forward->SetToolTip(_("Shifts subs forward, making them appear later. Use if they are appearing too soon."));
+
+    shift_backward = new wxRadioButton(this, -1, _("&Backward"));
+    shift_backward->SetToolTip(_("Shifts subs backward, making them appear earlier. Use if they are appearing too late."));
+
+    wxString selection_mode_vals[] = { _("&All rows"), _("Selected &rows"), _("Selection &onward") };
+    selection_mode = new wxRadioBox(this, -1, _("Affect"), wxDefaultPosition, wxDefaultSize, 3, selection_mode_vals, 1);
+
+    wxString time_field_vals[] = { _("Start a&nd End times"), _("&Start times only"), _("&End times only") };
+    time_fields = new wxRadioBox(this, -1, _("Times"), wxDefaultPosition, wxDefaultSize, 3, time_field_vals, 1);
+
+    history_box = new wxListBox(this, -1, wxDefaultPosition, wxSize(350, 100), 0, nullptr, wxLB_HSCROLL);
+
+    wxButton *clear_button = new wxButton(this, -1, _("&Clear"));
+    clear_button->Bind(wxEVT_BUTTON, &DialogShiftTimes::OnClear, this);
+
+    // Set initial control states
+    OnTimecodesLoaded(context->project->Timecodes());
+    OnSelectedSetChanged();
+    LoadHistory();
+
+    shift_time->SetTime(OPT_GET("Tool/Shift Times/Time")->GetInt());
+    *shift_frames << (int)OPT_GET("Tool/Shift Times/Frames")->GetInt();
+    shift_by_frames->SetValue(!OPT_GET("Tool/Shift Times/ByTime")->GetBool() && shift_by_frames->IsEnabled());
+    time_fields->SetSelection(OPT_GET("Tool/Shift Times/Type")->GetInt());
+    selection_mode->SetSelection(OPT_GET("Tool/Shift Times/Affect")->GetInt());
+    shift_backward->SetValue(OPT_GET("Tool/Shift Times/Direction")->GetBool());
+
+    if (shift_by_frames->GetValue())
+        shift_time->Disable();
+    else
+        shift_frames->Disable();
+
+    // Position controls
+    wxSizer *shift_amount_sizer = new wxFlexGridSizer(2, 2, 5, 5);
+    shift_amount_sizer->Add(shift_by_time, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
+    shift_amount_sizer->Add(shift_time, wxSizerFlags(1));
+    shift_amount_sizer->Add(shift_by_frames, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL));
+    shift_amount_sizer->Add(shift_frames, wxSizerFlags(1));
+
+    wxSizer *shift_direction_sizer = new wxBoxSizer(wxHORIZONTAL);
+    shift_direction_sizer->Add(shift_forward, wxSizerFlags(1).Expand());
+    shift_direction_sizer->Add(shift_backward, wxSizerFlags(1).Expand().Border(wxLEFT));
+
+    wxSizer *shift_by_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Shift by"));
+    shift_by_sizer->Add(shift_amount_sizer, wxSizerFlags().Expand());
+    shift_by_sizer->Add(shift_direction_sizer, wxSizerFlags().Expand().Border(wxTOP));
+
+    wxSizer *left_sizer = new wxBoxSizer(wxVERTICAL);
+    left_sizer->Add(shift_by_sizer, wxSizerFlags().Expand().Border(wxBOTTOM));
+    left_sizer->Add(selection_mode, wxSizerFlags().Expand().Border(wxBOTTOM));
+    left_sizer->Add(time_fields, wxSizerFlags().Expand());
+
+    wxSizer *history_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Load from history"));
+    history_sizer->Add(history_box, wxSizerFlags(1).Expand());
+    history_sizer->Add(clear_button, wxSizerFlags().Expand().Border(wxTOP));
+
+    wxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL);
+    top_sizer->Add(left_sizer, wxSizerFlags().Border(wxALL & ~wxRIGHT).Expand());
+    top_sizer->Add(history_sizer, wxSizerFlags().Border().Expand());
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(top_sizer, wxSizerFlags().Border(wxALL & ~wxBOTTOM));
+    main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Right().Border());
+    SetSizerAndFit(main_sizer);
+    CenterOnParent();
+
+    Bind(wxEVT_BUTTON, &DialogShiftTimes::Process, this, wxID_OK);
+    Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Shift Times"), wxID_HELP);
+    shift_time->Bind(wxEVT_TEXT_ENTER, &DialogShiftTimes::Process, this);
+    history_box->Bind(wxEVT_LISTBOX_DCLICK, &DialogShiftTimes::OnHistoryClick, this);
 }
 
-DialogShiftTimes::~DialogShiftTimes() {
-	long shift;
-	shift_frames->GetValue().ToLong(&shift);
-
-	OPT_SET("Tool/Shift Times/Time")->SetInt(shift_time->GetTime());
-	OPT_SET("Tool/Shift Times/Frames")->SetInt(shift);
-	OPT_SET("Tool/Shift Times/ByTime")->SetBool(shift_by_time->GetValue());
-	OPT_SET("Tool/Shift Times/Type")->SetInt(time_fields->GetSelection());
-	OPT_SET("Tool/Shift Times/Affect")->SetInt(selection_mode->GetSelection());
-	OPT_SET("Tool/Shift Times/Direction")->SetBool(shift_backward->GetValue());
+DialogShiftTimes::~DialogShiftTimes()
+{
+    long shift;
+    shift_frames->GetValue().ToLong(&shift);
+
+    OPT_SET("Tool/Shift Times/Time")->SetInt(shift_time->GetTime());
+    OPT_SET("Tool/Shift Times/Frames")->SetInt(shift);
+    OPT_SET("Tool/Shift Times/ByTime")->SetBool(shift_by_time->GetValue());
+    OPT_SET("Tool/Shift Times/Type")->SetInt(time_fields->GetSelection());
+    OPT_SET("Tool/Shift Times/Affect")->SetInt(selection_mode->GetSelection());
+    OPT_SET("Tool/Shift Times/Direction")->SetBool(shift_backward->GetValue());
 }
 
-void DialogShiftTimes::OnTimecodesLoaded(agi::vfr::Framerate const& new_fps) {
-	fps = new_fps;
-	if (fps.IsLoaded()) {
-		shift_by_frames->Enable();
-	}
-	else {
-		shift_by_time->SetValue(true);
-		shift_by_frames->Disable();
-		shift_time->Enable();
-		shift_frames->Disable();
-	}
+void DialogShiftTimes::OnTimecodesLoaded(agi::vfr::Framerate const &new_fps)
+{
+    fps = new_fps;
+    if (fps.IsLoaded()) {
+        shift_by_frames->Enable();
+    }
+    else {
+        shift_by_time->SetValue(true);
+        shift_by_frames->Disable();
+        shift_time->Enable();
+        shift_frames->Disable();
+    }
 }
 
-void DialogShiftTimes::OnSelectedSetChanged() {
-	if (context->selectionController->GetSelectedSet().empty()) {
-		selection_mode->Enable(1, false);
-		selection_mode->Enable(2, false);
-		selection_mode->SetSelection(0);
-	}
-	else {
-		selection_mode->Enable(1, true);
-		selection_mode->Enable(2, true);
-	}
+void DialogShiftTimes::OnSelectedSetChanged()
+{
+    if (context->selectionController->GetSelectedSet().empty()) {
+        selection_mode->Enable(1, false);
+        selection_mode->Enable(2, false);
+        selection_mode->SetSelection(0);
+    }
+    else {
+        selection_mode->Enable(1, true);
+        selection_mode->Enable(2, true);
+    }
 }
 
-void DialogShiftTimes::OnClear(wxCommandEvent &) {
-	agi::fs::Remove(history_filename);
-	history_box->Clear();
-	history.clear();
+void DialogShiftTimes::OnClear(wxCommandEvent &)
+{
+    agi::fs::Remove(history_filename);
+    history_box->Clear();
+    history.clear();
 }
 
-void DialogShiftTimes::OnByTime(wxCommandEvent &) {
-	shift_time->Enable(true);
-	shift_frames->Enable(false);
+void DialogShiftTimes::OnByTime(wxCommandEvent &)
+{
+    shift_time->Enable(true);
+    shift_frames->Enable(false);
 }
 
-void DialogShiftTimes::OnByFrames(wxCommandEvent &) {
-	shift_time->Enable(false);
-	shift_frames->Enable(true);
+void DialogShiftTimes::OnByFrames(wxCommandEvent &)
+{
+    shift_time->Enable(false);
+    shift_frames->Enable(true);
 }
 
-void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) {
-	size_t entry = evt.GetInt();
-	if (entry >= history.size()) return;
-
-	json::Object& obj = history[entry];
-	if (obj["is by time"]) {
-		shift_time->SetTime(agi::Time((std::string)obj["amount"]));
-		shift_by_time->SetValue(true);
-		OnByTime(evt);
-	}
-	else {
-		shift_frames->SetValue(to_wx(obj["amount"]));
-		if (shift_by_frames->IsEnabled()) {
-			shift_by_frames->SetValue(true);
-			OnByFrames(evt);
-		}
-	}
-
-	if (obj["is backward"])
-		shift_backward->SetValue(true);
-	else
-		shift_forward->SetValue(true);
-
-	selection_mode->SetSelection((int64_t)obj["mode"]);
-	time_fields->SetSelection((int64_t)obj["fields"]);
+void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt)
+{
+    size_t entry = evt.GetInt();
+    if (entry >= history.size()) return;
+
+    json::Object &obj = history[entry];
+    if (obj["is by time"]) {
+        shift_time->SetTime(agi::Time((std::string)obj["amount"]));
+        shift_by_time->SetValue(true);
+        OnByTime(evt);
+    }
+    else {
+        shift_frames->SetValue(to_wx(obj["amount"]));
+        if (shift_by_frames->IsEnabled()) {
+            shift_by_frames->SetValue(true);
+            OnByFrames(evt);
+        }
+    }
+
+    if (obj["is backward"])
+        shift_backward->SetValue(true);
+    else
+        shift_forward->SetValue(true);
+
+    selection_mode->SetSelection((int64_t)obj["mode"]);
+    time_fields->SetSelection((int64_t)obj["fields"]);
 }
 
-void DialogShiftTimes::SaveHistory(json::Array shifted_blocks) {
-	json::Object new_entry;
-	new_entry["filename"] = context->subsController->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());
-	new_entry["fields"] = time_fields->GetSelection();
-	new_entry["mode"] = selection_mode->GetSelection();
-	new_entry["selection"] = std::move(shifted_blocks);
-
-	history.insert(history.begin(), std::move(new_entry));
-	if (history.size() > 50)
-		history.resize(50);
-
-	try {
-		agi::JsonWriter::Write(history, agi::io::Save(history_filename).Get());
-	}
-	catch (agi::fs::FileSystemError const& e) {
-		LOG_E("dialog_shift_times/save_history") << "Cannot save shift times history: " << e.GetMessage();
-	}
+void DialogShiftTimes::SaveHistory(json::Array shifted_blocks)
+{
+    json::Object new_entry;
+    new_entry["filename"] = context->subsController->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());
+    new_entry["fields"] = time_fields->GetSelection();
+    new_entry["mode"] = selection_mode->GetSelection();
+    new_entry["selection"] = std::move(shifted_blocks);
+
+    history.insert(history.begin(), std::move(new_entry));
+    if (history.size() > 50)
+        history.resize(50);
+
+    try {
+        agi::JsonWriter::Write(history, agi::io::Save(history_filename).Get());
+    }
+    catch (agi::fs::FileSystemError const &e) {
+        LOG_E("dialog_shift_times/save_history") << "Cannot save shift times history: " << e.GetMessage();
+    }
 }
 
-void DialogShiftTimes::LoadHistory() {
-	history_box->Clear();
-	history_box->Freeze();
-
-	try {
-		json::UnknownElement root;
-		json::Reader::Read(root, *agi::io::Open(history_filename));
-		history = std::move(static_cast<json::Array&>(root));
-
-		for (auto& history_entry : history)
-			history_box->Append(get_history_string(history_entry));
-	}
-	catch (agi::fs::FileSystemError const& e) {
-		LOG_D("dialog_shift_times/load_history") << "Cannot load shift times history: " << e.GetMessage();
-	}
-	catch (json::Exception const& e) {
-		LOG_D("dialog_shift_times/load_history") << "Cannot load shift times history: " << e.what();
-	}
-	catch (...) {
-		history_box->Thaw();
-		throw;
-	}
-
-	history_box->Thaw();
+void DialogShiftTimes::LoadHistory()
+{
+    history_box->Clear();
+    history_box->Freeze();
+
+    try {
+        json::UnknownElement root;
+        json::Reader::Read(root, *agi::io::Open(history_filename));
+        history = std::move(static_cast<json::Array &>(root));
+
+        for (auto &history_entry : history)
+            history_box->Append(get_history_string(history_entry));
+    }
+    catch (agi::fs::FileSystemError const &e) {
+        LOG_D("dialog_shift_times/load_history") << "Cannot load shift times history: " << e.GetMessage();
+    }
+    catch (json::Exception const &e) {
+        LOG_D("dialog_shift_times/load_history") << "Cannot load shift times history: " << e.what();
+    }
+    catch (...) {
+        history_box->Thaw();
+        throw;
+    }
+
+    history_box->Thaw();
 }
 
-void DialogShiftTimes::Process(wxCommandEvent &) {
-	int mode = selection_mode->GetSelection();
-	int type = time_fields->GetSelection();
-	bool reverse = shift_backward->GetValue();
-	bool by_time = shift_by_time->GetValue();
-
-	bool start = type != 2;
-	bool end = type != 1;
-
-	auto const& sel = context->selectionController->GetSelectedSet();
-
-	long shift;
-	if (by_time) {
-		shift = shift_time->GetTime();
-		if (shift == 0) {
-			Close();
-			return;
-		}
-	}
-	else
-		shift_frames->GetValue().ToLong(&shift);
-
-	if (reverse)
-		shift = -shift;
-
-	// Track which rows were shifted for the log
-	int block_start = 0;
-	json::Array shifted_blocks;
-
-	for (auto& line : context->ass->Events) {
-		if (!sel.count(&line)) {
-			if (block_start) {
-				json::Object block;
-				block["start"] = block_start;
-				block["end"] = line.Row;
-				shifted_blocks.push_back(std::move(block));
-				block_start = 0;
-			}
-			if (mode == 1) continue;
-			if (mode == 2 && shifted_blocks.empty()) continue;
-		}
-		else if (!block_start)
-			block_start = line.Row + 1;
-
-		if (start)
-			line.Start = Shift(line.Start, shift, by_time, agi::vfr::START);
-		if (end)
-			line.End = Shift(line.End, shift, by_time, agi::vfr::END);
-	}
-
-	context->ass->Commit(_("shifting"), AssFile::COMMIT_DIAG_TIME);
-
-	if (block_start) {
-		json::Object block;
-		block["start"] = block_start;
-		block["end"] = context->ass->Events.back().Row + 1;
-		shifted_blocks.push_back(std::move(block));
-	}
-
-	SaveHistory(std::move(shifted_blocks));
-	Close();
+void DialogShiftTimes::Process(wxCommandEvent &)
+{
+    int mode = selection_mode->GetSelection();
+    int type = time_fields->GetSelection();
+    bool reverse = shift_backward->GetValue();
+    bool by_time = shift_by_time->GetValue();
+
+    bool start = type != 2;
+    bool end = type != 1;
+
+    auto const &sel = context->selectionController->GetSelectedSet();
+
+    long shift;
+    if (by_time) {
+        shift = shift_time->GetTime();
+        if (shift == 0) {
+            Close();
+            return;
+        }
+    }
+    else
+        shift_frames->GetValue().ToLong(&shift);
+
+    if (reverse)
+        shift = -shift;
+
+    // Track which rows were shifted for the log
+    int block_start = 0;
+    json::Array shifted_blocks;
+
+    for (auto &line : context->ass->Events) {
+        if (!sel.count(&line)) {
+            if (block_start) {
+                json::Object block;
+                block["start"] = block_start;
+                block["end"] = line.Row;
+                shifted_blocks.push_back(std::move(block));
+                block_start = 0;
+            }
+            if (mode == 1) continue;
+            if (mode == 2 && shifted_blocks.empty()) continue;
+        }
+        else if (!block_start)
+            block_start = line.Row + 1;
+
+        if (start)
+            line.Start = Shift(line.Start, shift, by_time, agi::vfr::START);
+        if (end)
+            line.End = Shift(line.End, shift, by_time, agi::vfr::END);
+    }
+
+    context->ass->Commit(_("shifting"), AssFile::COMMIT_DIAG_TIME);
+
+    if (block_start) {
+        json::Object block;
+        block["start"] = block_start;
+        block["end"] = context->ass->Events.back().Row + 1;
+        shifted_blocks.push_back(std::move(block));
+    }
+
+    SaveHistory(std::move(shifted_blocks));
+    Close();
 }
 
-int DialogShiftTimes::Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type) {
-	if (by_time)
-		return initial_time + shift;
-	else
-		return fps.TimeAtFrame(shift + fps.FrameAtTime(initial_time, type), type);
+int DialogShiftTimes::Shift(int initial_time, int shift, bool by_time, agi::vfr::Time type)
+{
+    if (by_time)
+        return initial_time + shift;
+    else
+        return fps.TimeAtFrame(shift + fps.FrameAtTime(initial_time, type), type);
 }
 }
 
-void ShowShiftTimesDialog(agi::Context *c) {
-	c->dialog->Show<DialogShiftTimes>(c);
+void ShowShiftTimesDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogShiftTimes>(c);
 }
diff --git a/src/dialog_spellchecker.cpp b/src/dialog_spellchecker.cpp
index dcca98738b09361640a498a64a69bca0ea7b27b4..9f6bb44009cc4985cdaba53a415f5b7f4957ddfd 100644
--- a/src/dialog_spellchecker.cpp
+++ b/src/dialog_spellchecker.cpp
@@ -47,319 +47,327 @@
 
 namespace {
 class DialogSpellChecker final : public wxDialog {
-	agi::Context *context; ///< The project context
-	std::unique_ptr<agi::SpellChecker> spellchecker; ///< The spellchecking engine
+    agi::Context *context; ///< The project context
+    std::unique_ptr<agi::SpellChecker> spellchecker; ///< The spellchecking engine
 
-	/// Words which the user has indicated should always be corrected
-	std::map<std::string, std::string> auto_replace;
+    /// Words which the user has indicated should always be corrected
+    std::map<std::string, std::string> auto_replace;
 
-	/// Words which the user has temporarily added to the dictionary
-	std::set<std::string> auto_ignore;
+    /// Words which the user has temporarily added to the dictionary
+    std::set<std::string> auto_ignore;
 
-	/// Dictionaries available
-	wxArrayString dictionary_lang_codes;
+    /// Dictionaries available
+    wxArrayString dictionary_lang_codes;
 
-	int word_start; ///< Start index of the current misspelled word
-	int word_len;   ///< Length of the current misspelled word
+    int word_start; ///< Start index of the current misspelled word
+    int word_len;   ///< Length of the current misspelled word
 
-	wxTextCtrl *orig_word;    ///< The word being corrected
-	wxTextCtrl *replace_word; ///< The replacement that will be used if "Replace" is clicked
-	wxListBox *suggest_list;  ///< The list of suggested replacements
+    wxTextCtrl *orig_word;    ///< The word being corrected
+    wxTextCtrl *replace_word; ///< The replacement that will be used if "Replace" is clicked
+    wxListBox *suggest_list;  ///< The list of suggested replacements
 
-	wxComboBox *language;      ///< The list of available languages
-	wxButton *add_button;      ///< Add word to currently active dictionary
-	wxButton *remove_button;   ///< Remove word from currently active dictionary
+    wxComboBox *language;      ///< The list of available languages
+    wxButton *add_button;      ///< Add word to currently active dictionary
+    wxButton *remove_button;   ///< Remove word from currently active dictionary
 
-	AssDialogue *start_line = nullptr;  ///< The first line checked
-	AssDialogue *active_line = nullptr; ///< The most recently checked line
-	bool has_looped = false;            ///< Has the search already looped from the end to beginning?
+    AssDialogue *start_line = nullptr;  ///< The first line checked
+    AssDialogue *active_line = nullptr; ///< The most recently checked line
+    bool has_looped = false;            ///< Has the search already looped from the end to beginning?
 
-	/// Find the next misspelled word and close the dialog if there are none
-	/// @return Are there any more misspelled words?
-	bool FindNext();
+    /// Find the next misspelled word and close the dialog if there are none
+    /// @return Are there any more misspelled words?
+    bool FindNext();
 
-	/// Check a single line for misspellings
-	/// @param active_line Line to check
-	/// @param start_pos Index in the line to start at
-	/// @param[in,out] commit_id Commit id for coalescing autoreplace commits
-	/// @return Was a misspelling found?
-	bool CheckLine(AssDialogue *active_line, int start_pos, int *commit_id);
+    /// Check a single line for misspellings
+    /// @param active_line Line to check
+    /// @param start_pos Index in the line to start at
+    /// @param[in,out] commit_id Commit id for coalescing autoreplace commits
+    /// @return Was a misspelling found?
+    bool CheckLine(AssDialogue *active_line, int start_pos, int *commit_id);
 
-	/// Set the current word to be corrected
-	void SetWord(std::string const& word);
-	/// Correct the currently selected word
-	void Replace();
+    /// Set the current word to be corrected
+    void SetWord(std::string const &word);
+    /// Correct the currently selected word
+    void Replace();
 
-	void OnChangeLanguage(wxCommandEvent&);
-	void OnChangeSuggestion(wxCommandEvent&);
+    void OnChangeLanguage(wxCommandEvent &);
+    void OnChangeSuggestion(wxCommandEvent &);
 
-	void OnReplace(wxCommandEvent&);
+    void OnReplace(wxCommandEvent &);
 
 public:
-	DialogSpellChecker(agi::Context *context);
+    DialogSpellChecker(agi::Context *context);
 };
 
 DialogSpellChecker::DialogSpellChecker(agi::Context *context)
-: wxDialog(context->parent, -1, _("Spell Checker"))
-, context(context)
-, spellchecker(SpellCheckerFactory::GetSpellChecker())
+    : wxDialog(context->parent, -1, _("Spell Checker"))
+    , context(context)
+    , spellchecker(SpellCheckerFactory::GetSpellChecker())
 {
-	SetIcon(GETICON(spellcheck_toolbutton_16));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-
-	auto current_word_sizer = new wxFlexGridSizer(2, 5, 5);
-	main_sizer->Add(current_word_sizer, wxSizerFlags().Expand().Border(wxALL, 5));
-
-	wxSizer *bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
-	main_sizer->Add(bottom_sizer, wxSizerFlags().Expand().Border(~wxTOP & wxALL, 5));
-
-	wxSizer *bottom_left_sizer = new wxBoxSizer(wxVERTICAL);
-	bottom_sizer->Add(bottom_left_sizer, wxSizerFlags().Expand().Border(wxRIGHT, 5));
-
-	wxSizer *actions_sizer = new wxBoxSizer(wxVERTICAL);
-	bottom_sizer->Add(actions_sizer, wxSizerFlags().Expand());
-
-	// Misspelled word and currently selected correction
-	current_word_sizer->AddGrowableCol(1, 1);
-	current_word_sizer->Add(new wxStaticText(this, -1, _("Misspelled word:")), 0, wxALIGN_CENTER_VERTICAL);
-	current_word_sizer->Add(orig_word = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY), wxSizerFlags(1).Expand());
-	current_word_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), 0, wxALIGN_CENTER_VERTICAL);
-	current_word_sizer->Add(replace_word = new wxTextCtrl(this, -1, ""), wxSizerFlags(1).Expand());
-
-	replace_word->Bind(wxEVT_TEXT, [=](wxCommandEvent&) {
-		remove_button->Enable(spellchecker->CanRemoveWord(from_wx(replace_word->GetValue())));
-	});
-
-	// List of suggested corrections
-	suggest_list = new wxListBox(this, -1, wxDefaultPosition, wxSize(300, 150));
-	suggest_list->Bind(wxEVT_LISTBOX, &DialogSpellChecker::OnChangeSuggestion, this);
-	suggest_list->Bind(wxEVT_LISTBOX_DCLICK, &DialogSpellChecker::OnReplace, this);
-	bottom_left_sizer->Add(suggest_list, wxSizerFlags(1).Expand());
-
-	// List of supported spellchecker languages
-	{
-		if (!spellchecker) {
-			wxMessageBox("No spellchecker available.", "Error", wxOK | wxICON_ERROR | wxCENTER);
-			throw agi::UserCancelException("No spellchecker available");
-		}
-
-		dictionary_lang_codes = to_wx(spellchecker->GetLanguageList());
-		if (dictionary_lang_codes.empty()) {
-			wxMessageBox("No spellchecker dictionaries available.", "Error", wxOK | wxICON_ERROR | wxCENTER);
-			throw agi::UserCancelException("No spellchecker dictionaries available");
-		}
-
-		wxArrayString language_names(dictionary_lang_codes);
-		for (size_t i = 0; i < dictionary_lang_codes.size(); ++i) {
-			if (const wxLanguageInfo *info = wxLocale::FindLanguageInfo(dictionary_lang_codes[i]))
-				language_names[i] = info->Description;
-		}
-
-		language = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, language_names, wxCB_DROPDOWN | wxCB_READONLY);
-		wxString cur_lang = to_wx(OPT_GET("Tool/Spell Checker/Language")->GetString());
-		int cur_lang_index = dictionary_lang_codes.Index(cur_lang);
-		if (cur_lang_index == wxNOT_FOUND) cur_lang_index = dictionary_lang_codes.Index("en");
-		if (cur_lang_index == wxNOT_FOUND) cur_lang_index = dictionary_lang_codes.Index("en_US");
-		if (cur_lang_index == wxNOT_FOUND) cur_lang_index = 0;
-		language->SetSelection(cur_lang_index);
-		language->Bind(wxEVT_COMBOBOX, &DialogSpellChecker::OnChangeLanguage, this);
-
-		bottom_left_sizer->Add(language, wxSizerFlags().Expand().Border(wxTOP, 5));
-	}
-
-	{
-		wxSizerFlags button_flags = wxSizerFlags().Expand().Border(wxBOTTOM, 5);
-
-		auto make_checkbox = [&](wxString const& text, const char *opt) {
-			auto checkbox = new wxCheckBox(this, -1, text);
-			actions_sizer->Add(checkbox, button_flags);
-			checkbox->SetValue(OPT_GET(opt)->GetBool());
-			checkbox->Bind(wxEVT_CHECKBOX,
-				[=](wxCommandEvent &evt) { OPT_SET(opt)->SetBool(!!evt.GetInt()); });
-		};
-
-		make_checkbox(_("&Skip Comments"), "Tool/Spell Checker/Skip Comments");
-		make_checkbox(_("Ignore &UPPERCASE words"), "Tool/Spell Checker/Skip Uppercase");
-
-		wxButton *button;
-
-		actions_sizer->Add(button = new wxButton(this, -1, _("&Replace")), button_flags);
-		button->Bind(wxEVT_BUTTON, &DialogSpellChecker::OnReplace, this);
-
-		actions_sizer->Add(button = new wxButton(this, -1, _("Replace &all")), button_flags);
-		button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-			auto_replace[from_wx(orig_word->GetValue())] = from_wx(replace_word->GetValue());
-			Replace();
-			FindNext();
-		});
-
-		actions_sizer->Add(button = new wxButton(this, -1, _("&Ignore")), button_flags);
-		button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { FindNext(); });
-
-		actions_sizer->Add(button = new wxButton(this, -1, _("Ignore a&ll")), button_flags);
-		button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-			auto_ignore.insert(from_wx(orig_word->GetValue()));
-			FindNext();
-		});
-
-		actions_sizer->Add(add_button = new wxButton(this, -1, _("Add to &dictionary")), button_flags);
-		add_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-			spellchecker->AddWord(from_wx(orig_word->GetValue()));
-			FindNext();
-		});
-
-		actions_sizer->Add(remove_button = new wxButton(this, -1, _("Remove fro&m dictionary")), button_flags);
-		remove_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-			spellchecker->RemoveWord(from_wx(replace_word->GetValue()));
-			SetWord(from_wx(orig_word->GetValue()));
-		});
-
-		actions_sizer->Add(new HelpButton(this, "Spell Checker"), button_flags);
-
-		actions_sizer->Add(new wxButton(this, wxID_CANCEL), button_flags.Border(0));
-	}
-
-	SetSizerAndFit(main_sizer);
-	CenterOnParent();
-
-	if (FindNext())
-		Show();
+    SetIcon(GETICON(spellcheck_toolbutton_16));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+
+    auto current_word_sizer = new wxFlexGridSizer(2, 5, 5);
+    main_sizer->Add(current_word_sizer, wxSizerFlags().Expand().Border(wxALL, 5));
+
+    wxSizer *bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
+    main_sizer->Add(bottom_sizer, wxSizerFlags().Expand().Border(~wxTOP & wxALL, 5));
+
+    wxSizer *bottom_left_sizer = new wxBoxSizer(wxVERTICAL);
+    bottom_sizer->Add(bottom_left_sizer, wxSizerFlags().Expand().Border(wxRIGHT, 5));
+
+    wxSizer *actions_sizer = new wxBoxSizer(wxVERTICAL);
+    bottom_sizer->Add(actions_sizer, wxSizerFlags().Expand());
+
+    // Misspelled word and currently selected correction
+    current_word_sizer->AddGrowableCol(1, 1);
+    current_word_sizer->Add(new wxStaticText(this, -1, _("Misspelled word:")), 0, wxALIGN_CENTER_VERTICAL);
+    current_word_sizer->Add(orig_word = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY), wxSizerFlags(1).Expand());
+    current_word_sizer->Add(new wxStaticText(this, -1, _("Replace with:")), 0, wxALIGN_CENTER_VERTICAL);
+    current_word_sizer->Add(replace_word = new wxTextCtrl(this, -1, ""), wxSizerFlags(1).Expand());
+
+    replace_word->Bind(wxEVT_TEXT, [ = ](wxCommandEvent &) {
+        remove_button->Enable(spellchecker->CanRemoveWord(from_wx(replace_word->GetValue())));
+    });
+
+    // List of suggested corrections
+    suggest_list = new wxListBox(this, -1, wxDefaultPosition, wxSize(300, 150));
+    suggest_list->Bind(wxEVT_LISTBOX, &DialogSpellChecker::OnChangeSuggestion, this);
+    suggest_list->Bind(wxEVT_LISTBOX_DCLICK, &DialogSpellChecker::OnReplace, this);
+    bottom_left_sizer->Add(suggest_list, wxSizerFlags(1).Expand());
+
+    // List of supported spellchecker languages
+    {
+        if (!spellchecker) {
+            wxMessageBox("No spellchecker available.", "Error", wxOK | wxICON_ERROR | wxCENTER);
+            throw agi::UserCancelException("No spellchecker available");
+        }
+
+        dictionary_lang_codes = to_wx(spellchecker->GetLanguageList());
+        if (dictionary_lang_codes.empty()) {
+            wxMessageBox("No spellchecker dictionaries available.", "Error", wxOK | wxICON_ERROR | wxCENTER);
+            throw agi::UserCancelException("No spellchecker dictionaries available");
+        }
+
+        wxArrayString language_names(dictionary_lang_codes);
+        for (size_t i = 0; i < dictionary_lang_codes.size(); ++i) {
+            if (const wxLanguageInfo *info = wxLocale::FindLanguageInfo(dictionary_lang_codes[i]))
+                language_names[i] = info->Description;
+        }
+
+        language = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, language_names, wxCB_DROPDOWN | wxCB_READONLY);
+        wxString cur_lang = to_wx(OPT_GET("Tool/Spell Checker/Language")->GetString());
+        int cur_lang_index = dictionary_lang_codes.Index(cur_lang);
+        if (cur_lang_index == wxNOT_FOUND) cur_lang_index = dictionary_lang_codes.Index("en");
+        if (cur_lang_index == wxNOT_FOUND) cur_lang_index = dictionary_lang_codes.Index("en_US");
+        if (cur_lang_index == wxNOT_FOUND) cur_lang_index = 0;
+        language->SetSelection(cur_lang_index);
+        language->Bind(wxEVT_COMBOBOX, &DialogSpellChecker::OnChangeLanguage, this);
+
+        bottom_left_sizer->Add(language, wxSizerFlags().Expand().Border(wxTOP, 5));
+    }
+
+    {
+        wxSizerFlags button_flags = wxSizerFlags().Expand().Border(wxBOTTOM, 5);
+
+        auto make_checkbox = [&](wxString const & text, const char *opt) {
+            auto checkbox = new wxCheckBox(this, -1, text);
+            actions_sizer->Add(checkbox, button_flags);
+            checkbox->SetValue(OPT_GET(opt)->GetBool());
+            checkbox->Bind(wxEVT_CHECKBOX,
+            [ = ](wxCommandEvent & evt) { OPT_SET(opt)->SetBool(!!evt.GetInt()); });
+        };
+
+        make_checkbox(_("&Skip Comments"), "Tool/Spell Checker/Skip Comments");
+        make_checkbox(_("Ignore &UPPERCASE words"), "Tool/Spell Checker/Skip Uppercase");
+
+        wxButton *button;
+
+        actions_sizer->Add(button = new wxButton(this, -1, _("&Replace")), button_flags);
+        button->Bind(wxEVT_BUTTON, &DialogSpellChecker::OnReplace, this);
+
+        actions_sizer->Add(button = new wxButton(this, -1, _("Replace &all")), button_flags);
+        button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+            auto_replace[from_wx(orig_word->GetValue())] = from_wx(replace_word->GetValue());
+            Replace();
+            FindNext();
+        });
+
+        actions_sizer->Add(button = new wxButton(this, -1, _("&Ignore")), button_flags);
+        button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { FindNext(); });
+
+        actions_sizer->Add(button = new wxButton(this, -1, _("Ignore a&ll")), button_flags);
+        button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+            auto_ignore.insert(from_wx(orig_word->GetValue()));
+            FindNext();
+        });
+
+        actions_sizer->Add(add_button = new wxButton(this, -1, _("Add to &dictionary")), button_flags);
+        add_button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+            spellchecker->AddWord(from_wx(orig_word->GetValue()));
+            FindNext();
+        });
+
+        actions_sizer->Add(remove_button = new wxButton(this, -1, _("Remove fro&m dictionary")), button_flags);
+        remove_button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+            spellchecker->RemoveWord(from_wx(replace_word->GetValue()));
+            SetWord(from_wx(orig_word->GetValue()));
+        });
+
+        actions_sizer->Add(new HelpButton(this, "Spell Checker"), button_flags);
+
+        actions_sizer->Add(new wxButton(this, wxID_CANCEL), button_flags.Border(0));
+    }
+
+    SetSizerAndFit(main_sizer);
+    CenterOnParent();
+
+    if (FindNext())
+        Show();
 }
 
-void DialogSpellChecker::OnReplace(wxCommandEvent&) {
-	Replace();
-	FindNext();
+void DialogSpellChecker::OnReplace(wxCommandEvent &)
+{
+    Replace();
+    FindNext();
 }
 
-void DialogSpellChecker::OnChangeLanguage(wxCommandEvent&) {
-	wxString code = dictionary_lang_codes[language->GetSelection()];
-	OPT_SET("Tool/Spell Checker/Language")->SetString(from_wx(code));
+void DialogSpellChecker::OnChangeLanguage(wxCommandEvent &)
+{
+    wxString code = dictionary_lang_codes[language->GetSelection()];
+    OPT_SET("Tool/Spell Checker/Language")->SetString(from_wx(code));
 
-	FindNext();
+    FindNext();
 }
 
-void DialogSpellChecker::OnChangeSuggestion(wxCommandEvent&) {
-	replace_word->SetValue(suggest_list->GetStringSelection());
+void DialogSpellChecker::OnChangeSuggestion(wxCommandEvent &)
+{
+    replace_word->SetValue(suggest_list->GetStringSelection());
 }
 
-bool DialogSpellChecker::FindNext() {
-	AssDialogue *real_active_line = context->selectionController->GetActiveLine();
-	// User has changed the active line; restart search from this position
-	if (real_active_line != active_line) {
-		active_line = real_active_line;
-		has_looped = false;
-		start_line = active_line;
-	}
-
-	int start_pos = context->textSelectionController->GetInsertionPoint();
-	int commit_id = -1;
-
-	if (CheckLine(active_line, start_pos, &commit_id))
-		return true;
-
-	auto it = context->ass->iterator_to(*active_line);
-
-	// Note that it is deliberate that the start line is checked twice, as if
-	// the cursor is past the first misspelled word in the current line, that
-	// word should be hit last
-	while(!has_looped || active_line != start_line) {
-		// Wrap around to the beginning if we hit the end
-		if (++it == context->ass->Events.end()) {
-			it = context->ass->Events.begin();
-			has_looped = true;
-		}
-
-		active_line = &*it;
-		if (CheckLine(active_line, 0, &commit_id))
-			return true;
-	}
-
-	if (IsShown()) {
-		wxMessageBox(_("Aegisub has finished checking spelling of this script."), _("Spell checking complete."));
-		Close();
-	}
-	else {
-		wxMessageBox(_("Aegisub has found no spelling mistakes in this script."), _("Spell checking complete."));
-		throw agi::UserCancelException("No spelling mistakes");
-	}
-
-	return false;
+bool DialogSpellChecker::FindNext()
+{
+    AssDialogue *real_active_line = context->selectionController->GetActiveLine();
+    // User has changed the active line; restart search from this position
+    if (real_active_line != active_line) {
+        active_line = real_active_line;
+        has_looped = false;
+        start_line = active_line;
+    }
+
+    int start_pos = context->textSelectionController->GetInsertionPoint();
+    int commit_id = -1;
+
+    if (CheckLine(active_line, start_pos, &commit_id))
+        return true;
+
+    auto it = context->ass->iterator_to(*active_line);
+
+    // Note that it is deliberate that the start line is checked twice, as if
+    // the cursor is past the first misspelled word in the current line, that
+    // word should be hit last
+    while (!has_looped || active_line != start_line) {
+        // Wrap around to the beginning if we hit the end
+        if (++it == context->ass->Events.end()) {
+            it = context->ass->Events.begin();
+            has_looped = true;
+        }
+
+        active_line = &*it;
+        if (CheckLine(active_line, 0, &commit_id))
+            return true;
+    }
+
+    if (IsShown()) {
+        wxMessageBox(_("Aegisub has finished checking spelling of this script."), _("Spell checking complete."));
+        Close();
+    }
+    else {
+        wxMessageBox(_("Aegisub has found no spelling mistakes in this script."), _("Spell checking complete."));
+        throw agi::UserCancelException("No spelling mistakes");
+    }
+
+    return false;
 }
 
-bool DialogSpellChecker::CheckLine(AssDialogue *active_line, int start_pos, int *commit_id) {
-	if (active_line->Comment && OPT_GET("Tool/Spell Checker/Skip Comments")->GetBool()) return false;
+bool DialogSpellChecker::CheckLine(AssDialogue *active_line, int start_pos, int *commit_id)
+{
+    if (active_line->Comment && OPT_GET("Tool/Spell Checker/Skip Comments")->GetBool()) return false;
 
-	std::string text = active_line->Text;
-	auto tokens = agi::ass::TokenizeDialogueBody(text);
-	agi::ass::SplitWords(text, tokens);
+    std::string text = active_line->Text;
+    auto tokens = agi::ass::TokenizeDialogueBody(text);
+    agi::ass::SplitWords(text, tokens);
 
-	bool ignore_uppercase = OPT_GET("Tool/Spell Checker/Skip Uppercase")->GetBool();
+    bool ignore_uppercase = OPT_GET("Tool/Spell Checker/Skip Uppercase")->GetBool();
 
-	word_start = 0;
-	for (auto const& tok : tokens) {
-		if (tok.type != agi::ass::DialogueTokenType::WORD || word_start < start_pos) {
-			word_start += tok.length;
-			continue;
-		}
+    word_start = 0;
+    for (auto const &tok : tokens) {
+        if (tok.type != agi::ass::DialogueTokenType::WORD || word_start < start_pos) {
+            word_start += tok.length;
+            continue;
+        }
 
-		word_len = tok.length;
-		std::string word = text.substr(word_start, word_len);
+        word_len = tok.length;
+        std::string word = text.substr(word_start, word_len);
 
-		if (auto_ignore.count(word) || spellchecker->CheckWord(word) || (ignore_uppercase && word == boost::locale::to_upper(word))) {
-			word_start += tok.length;
-			continue;
-		}
+        if (auto_ignore.count(word) || spellchecker->CheckWord(word) || (ignore_uppercase && word == boost::locale::to_upper(word))) {
+            word_start += tok.length;
+            continue;
+        }
 
-		auto auto_rep = auto_replace.find(word);
-		if (auto_rep == auto_replace.end()) {
+        auto auto_rep = auto_replace.find(word);
+        if (auto_rep == auto_replace.end()) {
 #ifdef __WXGTK__
-			// http://trac.wxwidgets.org/ticket/14369
-			orig_word->Remove(0, -1);
-			replace_word->Remove(0, -1);
+            // http://trac.wxwidgets.org/ticket/14369
+            orig_word->Remove(0, -1);
+            replace_word->Remove(0, -1);
 #endif
 
-			context->selectionController->SetSelectionAndActive({ active_line }, active_line);
-			SetWord(word);
-			return true;
-		}
-
-		text.replace(word_start, word_len, auto_rep->second);
-		active_line->Text = text;
-		*commit_id = context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT, *commit_id);
-		word_start += auto_rep->second.size();
-	}
-	return false;
+            context->selectionController->SetSelectionAndActive({ active_line }, active_line);
+            SetWord(word);
+            return true;
+        }
+
+        text.replace(word_start, word_len, auto_rep->second);
+        active_line->Text = text;
+        *commit_id = context->ass->Commit(_("spell check replace"), AssFile::COMMIT_DIAG_TEXT, *commit_id);
+        word_start += auto_rep->second.size();
+    }
+    return false;
 }
 
-void DialogSpellChecker::Replace() {
-	AssDialogue *active_line = context->selectionController->GetActiveLine();
-
-	// Only replace if the user hasn't changed the selection to something else
-	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());
-	}
+void DialogSpellChecker::Replace()
+{
+    AssDialogue *active_line = context->selectionController->GetActiveLine();
+
+    // Only replace if the user hasn't changed the selection to something else
+    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());
+    }
 }
 
-void DialogSpellChecker::SetWord(std::string const& word) {
-	orig_word->SetValue(to_wx(word));
+void DialogSpellChecker::SetWord(std::string const &word)
+{
+    orig_word->SetValue(to_wx(word));
 
-	wxArrayString suggestions = to_wx(spellchecker->GetSuggestions(word));
-	replace_word->SetValue(suggestions.size() ? suggestions[0] : to_wx(word));
-	suggest_list->Clear();
-	suggest_list->Append(suggestions);
+    wxArrayString suggestions = to_wx(spellchecker->GetSuggestions(word));
+    replace_word->SetValue(suggestions.size() ? suggestions[0] : to_wx(word));
+    suggest_list->Clear();
+    suggest_list->Append(suggestions);
 
-	context->textSelectionController->SetSelection(word_start, word_start + word_len);
-	context->textSelectionController->SetInsertionPoint(word_start + word_len);
+    context->textSelectionController->SetSelection(word_start, word_start + word_len);
+    context->textSelectionController->SetInsertionPoint(word_start + word_len);
 
-	add_button->Enable(spellchecker->CanAddWord(word));
+    add_button->Enable(spellchecker->CanAddWord(word));
 }
 }
 
-void ShowSpellcheckerDialog(agi::Context *c) {
-	c->dialog->Show<DialogSpellChecker>(c);
+void ShowSpellcheckerDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogSpellChecker>(c);
 }
diff --git a/src/dialog_style_editor.cpp b/src/dialog_style_editor.cpp
index 039caee177702248f731b592867da1bda195085b..d17bcedf3b09e4919ac7b4eabcfc98991f8f1b8a 100644
--- a/src/dialog_style_editor.cpp
+++ b/src/dialog_style_editor.cpp
@@ -65,482 +65,493 @@
 /// Style rename helper that walks a file searching for a style and optionally
 /// updating references to it
 class StyleRenamer {
-	agi::Context *c;
-	bool found_any = false;
-	bool do_replace = false;
-	std::string source_name;
-	std::string new_name;
-
-	/// Process a single override parameter to check if it's \r with this style name
-	static void ProcessTag(std::string const& tag, AssOverrideParameter* param, void *userData) {
-		StyleRenamer *self = static_cast<StyleRenamer*>(userData);
-		if (tag == "\\r" && param->GetType() == VariableDataType::TEXT && param->Get<std::string>() == self->source_name) {
-			if (self->do_replace)
-				param->Set(self->new_name);
-			else
-				self->found_any = true;
-		}
-	}
-
-	void Walk(bool replace) {
-		found_any = false;
-		do_replace = replace;
-
-		for (auto& diag : c->ass->Events) {
-			if (diag.Style == source_name) {
-				if (replace)
-					diag.Style = new_name;
-				else
-					found_any = true;
-			}
-
-			auto blocks = diag.ParseTags();
-			for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
-				block->ProcessParameters(&StyleRenamer::ProcessTag, this);
-			if (replace)
-				diag.UpdateText(blocks);
-
-			if (found_any) return;
-		}
-	}
+    agi::Context *c;
+    bool found_any = false;
+    bool do_replace = false;
+    std::string source_name;
+    std::string new_name;
+
+    /// Process a single override parameter to check if it's \r with this style name
+    static void ProcessTag(std::string const &tag, AssOverrideParameter *param, void *userData) {
+        StyleRenamer *self = static_cast<StyleRenamer *>(userData);
+        if (tag == "\\r" && param->GetType() == VariableDataType::TEXT && param->Get<std::string>() == self->source_name) {
+            if (self->do_replace)
+                param->Set(self->new_name);
+            else
+                self->found_any = true;
+        }
+    }
+
+    void Walk(bool replace) {
+        found_any = false;
+        do_replace = replace;
+
+        for (auto &diag : c->ass->Events) {
+            if (diag.Style == source_name) {
+                if (replace)
+                    diag.Style = new_name;
+                else
+                    found_any = true;
+            }
+
+            auto blocks = diag.ParseTags();
+            for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
+                block->ProcessParameters(&StyleRenamer::ProcessTag, this);
+            if (replace)
+                diag.UpdateText(blocks);
+
+            if (found_any) return;
+        }
+    }
 
 public:
-	StyleRenamer(agi::Context *c, std::string source_name, std::string new_name)
-	: c(c)
-	, source_name(std::move(source_name))
-	, new_name(std::move(new_name))
-	{
-	}
-
-	/// Check if there are any uses of the original style name in the file
-	bool NeedsReplace() {
-		Walk(false);
-		return found_any;
-	}
-
-	/// Replace all uses of the original style name with the new one
-	void Replace() {
-		Walk(true);
-	}
+    StyleRenamer(agi::Context *c, std::string source_name, std::string new_name)
+        : c(c)
+        , source_name(std::move(source_name))
+        , new_name(std::move(new_name)) {
+    }
+
+    /// Check if there are any uses of the original style name in the file
+    bool NeedsReplace() {
+        Walk(false);
+        return found_any;
+    }
+
+    /// Replace all uses of the original style name with the new one
+    void Replace() {
+        Walk(true);
+    }
 };
 
-DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const& new_name, wxArrayString const& font_list)
-: wxDialog (parent, -1, _("Style Editor"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
-, c(c)
-, style(style)
-, store(store)
+DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const &new_name, wxArrayString const &font_list)
+    : wxDialog (parent, -1, _("Style Editor"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+    , c(c)
+    , style(style)
+    , store(store)
 {
-	if (new_name.size()) {
-		is_new = true;
-		style = this->style = new AssStyle(*style);
-		style->name = new_name;
-	}
-	else if (!style) {
-		is_new = true;
-		style = this->style = new AssStyle;
-	}
-
-	work = agi::make_unique<AssStyle>(*style);
-
-	SetIcon(GETICON(style_toolbutton_16));
-
-	auto add_with_label = [&](wxSizer *sizer, wxString const& label, wxWindow *ctrl) {
-		sizer->Add(new wxStaticText(this, -1, label), wxSizerFlags().Center().Border(wxLEFT | wxRIGHT));
-		sizer->Add(ctrl, wxSizerFlags(1).Left().Expand());
-	};
-
-	auto num_text_ctrl = [&](double *value, double min, double max, double step) -> wxSpinCtrlDouble * {
-		auto scd = new wxSpinCtrlDouble(this, -1, "", wxDefaultPosition,
-			wxSize(75, -1), wxSP_ARROW_KEYS, min, max, *value, step);
-		scd->SetValidator(DoubleSpinValidator(value));
-		scd->Bind(wxEVT_SPINCTRLDOUBLE, [=](wxSpinDoubleEvent &evt) {
-			evt.Skip();
-			if (updating) return;
-
-			bool old = updating;
-			updating = true;
-			scd->GetValidator()->TransferFromWindow();
-			updating = old;
-			SubsPreview->SetStyle(*work);
-		});
-		return scd;
-	};
-
-	// Prepare control values
-	wxString EncodingValue = std::to_wstring(style->encoding);
-	wxString alignValues[9] = { "7", "8", "9", "4", "5", "6", "1", "2", "3" };
-
-	// Encoding options
-	wxArrayString encodingStrings;
-	AssStyle::GetEncodings(encodingStrings);
-
-	// Create sizers
-	wxSizer *NameSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Style Name"));
-	wxSizer *FontSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Font"));
-	wxSizer *ColorsSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Colors"));
-	wxSizer *MarginSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Margins"));
-	wxSizer *OutlineBox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Outline"));
-	wxSizer *MiscBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Miscellaneous"));
-	wxSizer *PreviewBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Preview"));
-
-	// Create controls
-	StyleName = new wxTextCtrl(this, -1, to_wx(style->name));
-	FontName = new wxComboBox(this, -1, to_wx(style->font), wxDefaultPosition, wxSize(150, -1), 0, nullptr, wxCB_DROPDOWN);
-	auto FontSize = num_text_ctrl(&work->fontsize, 0, 10000.0, 1.0);
-	BoxBold = new wxCheckBox(this, -1, _("&Bold"));
-	BoxItalic = new wxCheckBox(this, -1, _("&Italic"));
-	BoxUnderline = new wxCheckBox(this, -1, _("&Underline"));
-	BoxStrikeout = new wxCheckBox(this, -1, _("&Strikeout"));
-	ColourButton *colorButton[] = {
-		new ColourButton(this, wxSize(55, 16), true, style->primary, ColorValidator(&work->primary)),
-		new ColourButton(this, wxSize(55, 16), true, style->secondary, ColorValidator(&work->secondary)),
-		new ColourButton(this, wxSize(55, 16), true, style->outline, ColorValidator(&work->outline)),
-		new ColourButton(this, wxSize(55, 16), true, style->shadow, ColorValidator(&work->shadow))
-	};
-	for (int i = 0; i < 3; i++)
-		margin[i] = new wxSpinCtrl(this, -1, std::to_wstring(style->Margin[i]),
-		                           wxDefaultPosition, wxSize(60, -1),
-		                           wxSP_ARROW_KEYS, 0, 9999, style->Margin[i]);
-
-	Alignment = new wxRadioBox(this, -1, _("Alignment"), wxDefaultPosition, wxDefaultSize, 9, alignValues, 3, wxRA_SPECIFY_COLS);
-	auto Outline = num_text_ctrl(&work->outline_w, 0.0, 1000.0, 0.1);
-	auto Shadow = num_text_ctrl(&work->shadow_w, 0.0, 1000.0, 0.1);
-	OutlineType = new wxCheckBox(this, -1, _("&Opaque box"));
-	auto ScaleX = num_text_ctrl(&work->scalex, 0.0, 10000.0, 1.0);
-	auto ScaleY = num_text_ctrl(&work->scaley, 0.0, 10000.0, 1.0);
-	auto Angle = num_text_ctrl(&work->angle, -360.0, 360.0, 1.0);
-	auto Spacing = num_text_ctrl(&work->spacing, 0.0, 1000.0, 0.1);
-	Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
-
-	// Set control tooltips
-	StyleName->SetToolTip(_("Style name"));
-	FontName->SetToolTip(_("Font face"));
-	FontSize->SetToolTip(_("Font size"));
-	colorButton[0]->SetToolTip(_("Choose primary color"));
-	colorButton[1]->SetToolTip(_("Choose secondary color"));
-	colorButton[2]->SetToolTip(_("Choose outline color"));
-	colorButton[3]->SetToolTip(_("Choose shadow color"));
-	margin[0]->SetToolTip(_("Distance from left edge, in pixels"));
-	margin[1]->SetToolTip(_("Distance from right edge, in pixels"));
-	margin[2]->SetToolTip(_("Distance from top/bottom edge, in pixels"));
-	OutlineType->SetToolTip(_("When selected, display an opaque box behind the subtitles instead of an outline around the text"));
-	Outline->SetToolTip(_("Outline width, in pixels"));
-	Shadow->SetToolTip(_("Shadow distance, in pixels"));
-	ScaleX->SetToolTip(_("Scale X, in percentage"));
-	ScaleY->SetToolTip(_("Scale Y, in percentage"));
-	Angle->SetToolTip(_("Angle to rotate in Z axis, in degrees"));
-	Encoding->SetToolTip(_("Encoding, only useful in unicode if the font doesn't have the proper unicode mapping"));
-	Spacing->SetToolTip(_("Character spacing, in pixels"));
-	Alignment->SetToolTip(_("Alignment in screen, in numpad style"));
-
-	// Set up controls
-	BoxBold->SetValue(style->bold);
-	BoxItalic->SetValue(style->italic);
-	BoxUnderline->SetValue(style->underline);
-	BoxStrikeout->SetValue(style->strikeout);
-	OutlineType->SetValue(style->borderstyle == 3);
-	Alignment->SetSelection(AlignToControl(style->alignment));
-	// Fill font face list box
-	FontName->Freeze();
-	FontName->Append(font_list);
-	FontName->SetValue(to_wx(style->font));
-	FontName->Thaw();
-
-	// Set encoding value
-	bool found = false;
-	for (size_t i=0;i<encodingStrings.Count();i++) {
-		if (encodingStrings[i].StartsWith(EncodingValue)) {
-			Encoding->Select(i);
-			found = true;
-			break;
-		}
-	}
-	if (!found) Encoding->Select(0);
-
-	// Style name sizer
-	NameSizer->Add(StyleName, 1, wxALL, 0);
-
-	// Font sizer
-	wxSizer *FontSizerTop = new wxBoxSizer(wxHORIZONTAL);
-	wxSizer *FontSizerBottom = new wxBoxSizer(wxHORIZONTAL);
-	FontSizerTop->Add(FontName, 1, wxALL, 0);
-	FontSizerTop->Add(FontSize, 0, wxLEFT, 5);
-	FontSizerBottom->AddStretchSpacer(1);
-	FontSizerBottom->Add(BoxBold, 0, 0, 0);
-	FontSizerBottom->Add(BoxItalic, 0, wxLEFT, 5);
-	FontSizerBottom->Add(BoxUnderline, 0, wxLEFT, 5);
-	FontSizerBottom->Add(BoxStrikeout, 0, wxLEFT, 5);
-	FontSizerBottom->AddStretchSpacer(1);
-	FontSizer->Add(FontSizerTop, 1, wxALL | wxEXPAND, 0);
-	FontSizer->Add(FontSizerBottom, 1, wxTOP | wxEXPAND, 5);
-
-	// Colors sizer
-	wxString colorLabels[] = { _("Primary"), _("Secondary"), _("Outline"), _("Shadow") };
-	ColorsSizer->AddStretchSpacer(1);
-	for (int i = 0; i < 4; ++i) {
-		auto sizer = new wxBoxSizer(wxVERTICAL);
-		sizer->Add(new wxStaticText(this, -1, colorLabels[i]), 0, wxBOTTOM | wxALIGN_CENTER, 5);
-		sizer->Add(colorButton[i], 0, wxBOTTOM | wxALIGN_CENTER, 5);
-		ColorsSizer->Add(sizer, 0, wxLEFT, i?5:0);
-	}
-	ColorsSizer->AddStretchSpacer(1);
-
-	// Margins
-	wxString marginLabels[] = { _("Left"), _("Right"), _("Vert") };
-	MarginSizer->AddStretchSpacer(1);
-	for (int i=0;i<3;i++) {
-		auto sizer = new wxBoxSizer(wxVERTICAL);
-		sizer->AddStretchSpacer(1);
-		sizer->Add(new wxStaticText(this, -1, marginLabels[i]), 0, wxCENTER, 0);
-		sizer->Add(margin[i], 0, wxTOP | wxCENTER, 5);
-		sizer->AddStretchSpacer(1);
-		MarginSizer->Add(sizer, 0, wxEXPAND | wxLEFT, i?5:0);
-	}
-	MarginSizer->AddStretchSpacer(1);
-
-	// Margins+Alignment
-	wxSizer *MarginAlign = new wxBoxSizer(wxHORIZONTAL);
-	MarginAlign->Add(MarginSizer, 1, wxLEFT | wxEXPAND, 0);
-	MarginAlign->Add(Alignment, 0, wxLEFT | wxEXPAND, 5);
-
-	// Outline
-	add_with_label(OutlineBox, _("Outline:"), Outline);
-	add_with_label(OutlineBox, _("Shadow:"), Shadow);
-	OutlineBox->Add(OutlineType, 0, wxLEFT | wxALIGN_CENTER, 5);
-
-	// Misc
-	auto MiscBoxTop = new wxFlexGridSizer(2, 4, 5, 5);
-	add_with_label(MiscBoxTop, _("Scale X%:"), ScaleX);
-	add_with_label(MiscBoxTop, _("Scale Y%:"), ScaleY);
-	add_with_label(MiscBoxTop, _("Rotation:"), Angle);
-	add_with_label(MiscBoxTop, _("Spacing:"), Spacing);
-
-	wxSizer *MiscBoxBottom = new wxBoxSizer(wxHORIZONTAL);
-	add_with_label(MiscBoxBottom, _("Encoding:"), Encoding);
-
-	MiscBox->Add(MiscBoxTop, wxSizerFlags().Expand());
-	MiscBox->Add(MiscBoxBottom, wxSizerFlags().Expand().Border(wxTOP));
-
-	// Preview
-	auto previewButton = new ColourButton(this, wxSize(45, 16), false, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
-	PreviewText = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Style Editor/Preview Text")->GetString()));
-	SubsPreview = new SubtitlesPreview(this, wxSize(100, 60), wxSUNKEN_BORDER, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
-
-	SubsPreview->SetToolTip(_("Preview of current style"));
-	SubsPreview->SetStyle(*style);
-	SubsPreview->SetText(from_wx(PreviewText->GetValue()));
-	PreviewText->SetToolTip(_("Text to be used for the preview"));
-	previewButton->SetToolTip(_("Color of preview background"));
-
-	wxSizer *PreviewBottomSizer = new wxBoxSizer(wxHORIZONTAL);
-	PreviewBottomSizer->Add(PreviewText, 1, wxEXPAND | wxRIGHT, 5);
-	PreviewBottomSizer->Add(previewButton, 0, wxEXPAND, 0);
-	PreviewBox->Add(SubsPreview, 1, wxEXPAND | wxBOTTOM, 5);
-	PreviewBox->Add(PreviewBottomSizer, 0, wxEXPAND | wxBOTTOM, 0);
-
-	// Buttons
-	auto ButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxAPPLY | wxHELP);
-
-	// Left side sizer
-	wxSizer *LeftSizer = new wxBoxSizer(wxVERTICAL);
-	LeftSizer->Add(NameSizer, 0, wxBOTTOM | wxEXPAND, 5);
-	LeftSizer->Add(FontSizer, 0, wxBOTTOM | wxEXPAND, 5);
-	LeftSizer->Add(ColorsSizer, 0, wxBOTTOM | wxEXPAND, 5);
-	LeftSizer->Add(MarginAlign, 0, wxBOTTOM | wxEXPAND, 0);
-
-	// Right side sizer
-	wxSizer *RightSizer = new wxBoxSizer(wxVERTICAL);
-	RightSizer->Add(OutlineBox, wxSizerFlags().Expand().Border(wxBOTTOM));
-	RightSizer->Add(MiscBox, wxSizerFlags().Expand().Border(wxBOTTOM));
-	RightSizer->Add(PreviewBox, wxSizerFlags(1).Expand());
-
-	// Controls Sizer
-	wxSizer *ControlSizer = new wxBoxSizer(wxHORIZONTAL);
-	ControlSizer->Add(LeftSizer, 0, wxEXPAND, 0);
-	ControlSizer->Add(RightSizer, 1, wxLEFT | wxEXPAND, 5);
-
-	// General Layout
-	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(ControlSizer, 1, wxALL | wxEXPAND, 5);
-	MainSizer->Add(ButtonSizer, 0, wxBOTTOM | wxEXPAND, 5);
-
-	SetSizerAndFit(MainSizer);
-
-	// Force the style name text field to scroll based on its final size, rather
-	// than its initial size
-	StyleName->SetInsertionPoint(0);
-	StyleName->SetInsertionPoint(-1);
-
-	persist = agi::make_unique<PersistLocation>(this, "Tool/Style Editor", true);
-
-	Bind(wxEVT_CHILD_FOCUS, &DialogStyleEditor::OnChildFocus, this);
-
-	Bind(wxEVT_CHECKBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
-	Bind(wxEVT_COMBOBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
-	Bind(wxEVT_SPINCTRL, &DialogStyleEditor::OnCommandPreviewUpdate, this);
-
-	previewButton->Bind(EVT_COLOR, &DialogStyleEditor::OnPreviewColourChange, this);
-	FontName->Bind(wxEVT_TEXT_ENTER, &DialogStyleEditor::OnCommandPreviewUpdate, this);
-	PreviewText->Bind(wxEVT_TEXT, &DialogStyleEditor::OnPreviewTextChange, this);
-
-	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, true), wxID_OK);
-	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, false), wxID_APPLY);
-	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, false, true), wxID_CANCEL);
-	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Style Editor"), wxID_HELP);
-
-	for (auto const& elem : colorButton)
-		elem->Bind(EVT_COLOR, &DialogStyleEditor::OnSetColor, this);
+    if (new_name.size()) {
+        is_new = true;
+        style = this->style = new AssStyle(*style);
+        style->name = new_name;
+    }
+    else if (!style) {
+        is_new = true;
+        style = this->style = new AssStyle;
+    }
+
+    work = agi::make_unique<AssStyle>(*style);
+
+    SetIcon(GETICON(style_toolbutton_16));
+
+    auto add_with_label = [&](wxSizer * sizer, wxString const & label, wxWindow * ctrl) {
+        sizer->Add(new wxStaticText(this, -1, label), wxSizerFlags().Center().Border(wxLEFT | wxRIGHT));
+        sizer->Add(ctrl, wxSizerFlags(1).Left().Expand());
+    };
+
+    auto num_text_ctrl = [&](double * value, double min, double max, double step) -> wxSpinCtrlDouble * {
+        auto scd = new wxSpinCtrlDouble(this, -1, "", wxDefaultPosition,
+                                        wxSize(75, -1), wxSP_ARROW_KEYS, min, max, *value, step);
+        scd->SetValidator(DoubleSpinValidator(value));
+        scd->Bind(wxEVT_SPINCTRLDOUBLE, [ = ](wxSpinDoubleEvent & evt)
+        {
+            evt.Skip();
+            if (updating) return;
+
+            bool old = updating;
+            updating = true;
+            scd->GetValidator()->TransferFromWindow();
+            updating = old;
+            SubsPreview->SetStyle(*work);
+        });
+        return scd;
+    };
+
+    // Prepare control values
+    wxString EncodingValue = std::to_wstring(style->encoding);
+    wxString alignValues[9] = { "7", "8", "9", "4", "5", "6", "1", "2", "3" };
+
+    // Encoding options
+    wxArrayString encodingStrings;
+    AssStyle::GetEncodings(encodingStrings);
+
+    // Create sizers
+    wxSizer *NameSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Style Name"));
+    wxSizer *FontSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Font"));
+    wxSizer *ColorsSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Colors"));
+    wxSizer *MarginSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Margins"));
+    wxSizer *OutlineBox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Outline"));
+    wxSizer *MiscBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Miscellaneous"));
+    wxSizer *PreviewBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Preview"));
+
+    // Create controls
+    StyleName = new wxTextCtrl(this, -1, to_wx(style->name));
+    FontName = new wxComboBox(this, -1, to_wx(style->font), wxDefaultPosition, wxSize(150, -1), 0, nullptr, wxCB_DROPDOWN);
+    auto FontSize = num_text_ctrl(&work->fontsize, 0, 10000.0, 1.0);
+    BoxBold = new wxCheckBox(this, -1, _("&Bold"));
+    BoxItalic = new wxCheckBox(this, -1, _("&Italic"));
+    BoxUnderline = new wxCheckBox(this, -1, _("&Underline"));
+    BoxStrikeout = new wxCheckBox(this, -1, _("&Strikeout"));
+    ColourButton *colorButton[] = {
+        new ColourButton(this, wxSize(55, 16), true, style->primary, ColorValidator(&work->primary)),
+        new ColourButton(this, wxSize(55, 16), true, style->secondary, ColorValidator(&work->secondary)),
+        new ColourButton(this, wxSize(55, 16), true, style->outline, ColorValidator(&work->outline)),
+        new ColourButton(this, wxSize(55, 16), true, style->shadow, ColorValidator(&work->shadow))
+    };
+    for (int i = 0; i < 3; i++)
+        margin[i] = new wxSpinCtrl(this, -1, std::to_wstring(style->Margin[i]),
+                                   wxDefaultPosition, wxSize(60, -1),
+                                   wxSP_ARROW_KEYS, 0, 9999, style->Margin[i]);
+
+    Alignment = new wxRadioBox(this, -1, _("Alignment"), wxDefaultPosition, wxDefaultSize, 9, alignValues, 3, wxRA_SPECIFY_COLS);
+    auto Outline = num_text_ctrl(&work->outline_w, 0.0, 1000.0, 0.1);
+    auto Shadow = num_text_ctrl(&work->shadow_w, 0.0, 1000.0, 0.1);
+    OutlineType = new wxCheckBox(this, -1, _("&Opaque box"));
+    auto ScaleX = num_text_ctrl(&work->scalex, 0.0, 10000.0, 1.0);
+    auto ScaleY = num_text_ctrl(&work->scaley, 0.0, 10000.0, 1.0);
+    auto Angle = num_text_ctrl(&work->angle, -360.0, 360.0, 1.0);
+    auto Spacing = num_text_ctrl(&work->spacing, 0.0, 1000.0, 0.1);
+    Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
+
+    // Set control tooltips
+    StyleName->SetToolTip(_("Style name"));
+    FontName->SetToolTip(_("Font face"));
+    FontSize->SetToolTip(_("Font size"));
+    colorButton[0]->SetToolTip(_("Choose primary color"));
+    colorButton[1]->SetToolTip(_("Choose secondary color"));
+    colorButton[2]->SetToolTip(_("Choose outline color"));
+    colorButton[3]->SetToolTip(_("Choose shadow color"));
+    margin[0]->SetToolTip(_("Distance from left edge, in pixels"));
+    margin[1]->SetToolTip(_("Distance from right edge, in pixels"));
+    margin[2]->SetToolTip(_("Distance from top/bottom edge, in pixels"));
+    OutlineType->SetToolTip(_("When selected, display an opaque box behind the subtitles instead of an outline around the text"));
+    Outline->SetToolTip(_("Outline width, in pixels"));
+    Shadow->SetToolTip(_("Shadow distance, in pixels"));
+    ScaleX->SetToolTip(_("Scale X, in percentage"));
+    ScaleY->SetToolTip(_("Scale Y, in percentage"));
+    Angle->SetToolTip(_("Angle to rotate in Z axis, in degrees"));
+    Encoding->SetToolTip(_("Encoding, only useful in unicode if the font doesn't have the proper unicode mapping"));
+    Spacing->SetToolTip(_("Character spacing, in pixels"));
+    Alignment->SetToolTip(_("Alignment in screen, in numpad style"));
+
+    // Set up controls
+    BoxBold->SetValue(style->bold);
+    BoxItalic->SetValue(style->italic);
+    BoxUnderline->SetValue(style->underline);
+    BoxStrikeout->SetValue(style->strikeout);
+    OutlineType->SetValue(style->borderstyle == 3);
+    Alignment->SetSelection(AlignToControl(style->alignment));
+    // Fill font face list box
+    FontName->Freeze();
+    FontName->Append(font_list);
+    FontName->SetValue(to_wx(style->font));
+    FontName->Thaw();
+
+    // Set encoding value
+    bool found = false;
+    for (size_t i = 0; i < encodingStrings.Count(); i++) {
+        if (encodingStrings[i].StartsWith(EncodingValue)) {
+            Encoding->Select(i);
+            found = true;
+            break;
+        }
+    }
+    if (!found) Encoding->Select(0);
+
+    // Style name sizer
+    NameSizer->Add(StyleName, 1, wxALL, 0);
+
+    // Font sizer
+    wxSizer *FontSizerTop = new wxBoxSizer(wxHORIZONTAL);
+    wxSizer *FontSizerBottom = new wxBoxSizer(wxHORIZONTAL);
+    FontSizerTop->Add(FontName, 1, wxALL, 0);
+    FontSizerTop->Add(FontSize, 0, wxLEFT, 5);
+    FontSizerBottom->AddStretchSpacer(1);
+    FontSizerBottom->Add(BoxBold, 0, 0, 0);
+    FontSizerBottom->Add(BoxItalic, 0, wxLEFT, 5);
+    FontSizerBottom->Add(BoxUnderline, 0, wxLEFT, 5);
+    FontSizerBottom->Add(BoxStrikeout, 0, wxLEFT, 5);
+    FontSizerBottom->AddStretchSpacer(1);
+    FontSizer->Add(FontSizerTop, 1, wxALL | wxEXPAND, 0);
+    FontSizer->Add(FontSizerBottom, 1, wxTOP | wxEXPAND, 5);
+
+    // Colors sizer
+    wxString colorLabels[] = { _("Primary"), _("Secondary"), _("Outline"), _("Shadow") };
+    ColorsSizer->AddStretchSpacer(1);
+    for (int i = 0; i < 4; ++i) {
+        auto sizer = new wxBoxSizer(wxVERTICAL);
+        sizer->Add(new wxStaticText(this, -1, colorLabels[i]), 0, wxBOTTOM | wxALIGN_CENTER, 5);
+        sizer->Add(colorButton[i], 0, wxBOTTOM | wxALIGN_CENTER, 5);
+        ColorsSizer->Add(sizer, 0, wxLEFT, i ? 5 : 0);
+    }
+    ColorsSizer->AddStretchSpacer(1);
+
+    // Margins
+    wxString marginLabels[] = { _("Left"), _("Right"), _("Vert") };
+    MarginSizer->AddStretchSpacer(1);
+    for (int i = 0; i < 3; i++) {
+        auto sizer = new wxBoxSizer(wxVERTICAL);
+        sizer->AddStretchSpacer(1);
+        sizer->Add(new wxStaticText(this, -1, marginLabels[i]), 0, wxCENTER, 0);
+        sizer->Add(margin[i], 0, wxTOP | wxCENTER, 5);
+        sizer->AddStretchSpacer(1);
+        MarginSizer->Add(sizer, 0, wxEXPAND | wxLEFT, i ? 5 : 0);
+    }
+    MarginSizer->AddStretchSpacer(1);
+
+    // Margins+Alignment
+    wxSizer *MarginAlign = new wxBoxSizer(wxHORIZONTAL);
+    MarginAlign->Add(MarginSizer, 1, wxLEFT | wxEXPAND, 0);
+    MarginAlign->Add(Alignment, 0, wxLEFT | wxEXPAND, 5);
+
+    // Outline
+    add_with_label(OutlineBox, _("Outline:"), Outline);
+    add_with_label(OutlineBox, _("Shadow:"), Shadow);
+    OutlineBox->Add(OutlineType, 0, wxLEFT | wxALIGN_CENTER, 5);
+
+    // Misc
+    auto MiscBoxTop = new wxFlexGridSizer(2, 4, 5, 5);
+    add_with_label(MiscBoxTop, _("Scale X%:"), ScaleX);
+    add_with_label(MiscBoxTop, _("Scale Y%:"), ScaleY);
+    add_with_label(MiscBoxTop, _("Rotation:"), Angle);
+    add_with_label(MiscBoxTop, _("Spacing:"), Spacing);
+
+    wxSizer *MiscBoxBottom = new wxBoxSizer(wxHORIZONTAL);
+    add_with_label(MiscBoxBottom, _("Encoding:"), Encoding);
+
+    MiscBox->Add(MiscBoxTop, wxSizerFlags().Expand());
+    MiscBox->Add(MiscBoxBottom, wxSizerFlags().Expand().Border(wxTOP));
+
+    // Preview
+    auto previewButton = new ColourButton(this, wxSize(45, 16), false, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
+    PreviewText = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Style Editor/Preview Text")->GetString()));
+    SubsPreview = new SubtitlesPreview(this, wxSize(100, 60), wxSUNKEN_BORDER, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
+
+    SubsPreview->SetToolTip(_("Preview of current style"));
+    SubsPreview->SetStyle(*style);
+    SubsPreview->SetText(from_wx(PreviewText->GetValue()));
+    PreviewText->SetToolTip(_("Text to be used for the preview"));
+    previewButton->SetToolTip(_("Color of preview background"));
+
+    wxSizer *PreviewBottomSizer = new wxBoxSizer(wxHORIZONTAL);
+    PreviewBottomSizer->Add(PreviewText, 1, wxEXPAND | wxRIGHT, 5);
+    PreviewBottomSizer->Add(previewButton, 0, wxEXPAND, 0);
+    PreviewBox->Add(SubsPreview, 1, wxEXPAND | wxBOTTOM, 5);
+    PreviewBox->Add(PreviewBottomSizer, 0, wxEXPAND | wxBOTTOM, 0);
+
+    // Buttons
+    auto ButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxAPPLY | wxHELP);
+
+    // Left side sizer
+    wxSizer *LeftSizer = new wxBoxSizer(wxVERTICAL);
+    LeftSizer->Add(NameSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    LeftSizer->Add(FontSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    LeftSizer->Add(ColorsSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    LeftSizer->Add(MarginAlign, 0, wxBOTTOM | wxEXPAND, 0);
+
+    // Right side sizer
+    wxSizer *RightSizer = new wxBoxSizer(wxVERTICAL);
+    RightSizer->Add(OutlineBox, wxSizerFlags().Expand().Border(wxBOTTOM));
+    RightSizer->Add(MiscBox, wxSizerFlags().Expand().Border(wxBOTTOM));
+    RightSizer->Add(PreviewBox, wxSizerFlags(1).Expand());
+
+    // Controls Sizer
+    wxSizer *ControlSizer = new wxBoxSizer(wxHORIZONTAL);
+    ControlSizer->Add(LeftSizer, 0, wxEXPAND, 0);
+    ControlSizer->Add(RightSizer, 1, wxLEFT | wxEXPAND, 5);
+
+    // General Layout
+    wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(ControlSizer, 1, wxALL | wxEXPAND, 5);
+    MainSizer->Add(ButtonSizer, 0, wxBOTTOM | wxEXPAND, 5);
+
+    SetSizerAndFit(MainSizer);
+
+    // Force the style name text field to scroll based on its final size, rather
+    // than its initial size
+    StyleName->SetInsertionPoint(0);
+    StyleName->SetInsertionPoint(-1);
+
+    persist = agi::make_unique<PersistLocation>(this, "Tool/Style Editor", true);
+
+    Bind(wxEVT_CHILD_FOCUS, &DialogStyleEditor::OnChildFocus, this);
+
+    Bind(wxEVT_CHECKBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
+    Bind(wxEVT_COMBOBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
+    Bind(wxEVT_SPINCTRL, &DialogStyleEditor::OnCommandPreviewUpdate, this);
+
+    previewButton->Bind(EVT_COLOR, &DialogStyleEditor::OnPreviewColourChange, this);
+    FontName->Bind(wxEVT_TEXT_ENTER, &DialogStyleEditor::OnCommandPreviewUpdate, this);
+    PreviewText->Bind(wxEVT_TEXT, &DialogStyleEditor::OnPreviewTextChange, this);
+
+    Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, true), wxID_OK);
+    Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, false), wxID_APPLY);
+    Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, false, true), wxID_CANCEL);
+    Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Style Editor"), wxID_HELP);
+
+    for (auto const &elem : colorButton)
+        elem->Bind(EVT_COLOR, &DialogStyleEditor::OnSetColor, this);
 }
 
-DialogStyleEditor::~DialogStyleEditor() {
-	if (is_new)
-		delete style;
+DialogStyleEditor::~DialogStyleEditor()
+{
+    if (is_new)
+        delete style;
 }
 
-std::string DialogStyleEditor::GetStyleName() const {
-	return style->name;
+std::string DialogStyleEditor::GetStyleName() const
+{
+    return style->name;
 }
 
-void DialogStyleEditor::Apply(bool apply, bool close) {
-	if (apply) {
-		std::string new_name = from_wx(StyleName->GetValue());
-
-		// Get list of existing styles
-		std::vector<std::string> styles = store ? store->GetNames() : c->ass->GetStyles();
-
-		// Check if style name is unique
-		AssStyle *existing = store ? store->GetStyle(new_name) : c->ass->GetStyle(new_name);
-		if (existing && existing != style) {
-			wxMessageBox(_("There is already a style with this name. Please choose another name."), _("Style name conflict"), wxOK | wxICON_ERROR | wxCENTER);
-			return;
-		}
-
-		// Style name change
-		bool did_rename = false;
-		if (work->name != new_name) {
-			if (!store && !is_new) {
-				StyleRenamer renamer(c, work->name, new_name);
-				if (renamer.NeedsReplace()) {
-					// See if user wants to update style name through script
-					int answer = wxMessageBox(
-						_("Do you want to change all instances of this style in the script to this new name?"),
-						_("Update script?"),
-						wxYES_NO | wxCANCEL);
-
-					if (answer == wxCANCEL) return;
-
-					if (answer == wxYES) {
-						did_rename = true;
-						renamer.Replace();
-					}
-				}
-			}
-
-			work->name = new_name;
-		}
-
-		UpdateWorkStyle();
-
-		*style = *work;
-		style->UpdateData();
-		if (is_new) {
-			if (store)
-				store->push_back(std::unique_ptr<AssStyle>(style));
-			else
-				c->ass->Styles.push_back(*style);
-			is_new = false;
-		}
-		if (!store)
-			c->ass->Commit(_("style change"), AssFile::COMMIT_STYLES | (did_rename ? AssFile::COMMIT_DIAG_FULL : 0));
-
-		// Update preview
-		if (!close) SubsPreview->SetStyle(*style);
-	}
-
-	if (close) {
-		EndModal(apply);
-		if (PreviewText)
-			OPT_SET("Tool/Style Editor/Preview Text")->SetString(from_wx(PreviewText->GetValue()));
-	}
+void DialogStyleEditor::Apply(bool apply, bool close)
+{
+    if (apply) {
+        std::string new_name = from_wx(StyleName->GetValue());
+
+        // Get list of existing styles
+        std::vector<std::string> styles = store ? store->GetNames() : c->ass->GetStyles();
+
+        // Check if style name is unique
+        AssStyle *existing = store ? store->GetStyle(new_name) : c->ass->GetStyle(new_name);
+        if (existing && existing != style) {
+            wxMessageBox(_("There is already a style with this name. Please choose another name."), _("Style name conflict"), wxOK | wxICON_ERROR | wxCENTER);
+            return;
+        }
+
+        // Style name change
+        bool did_rename = false;
+        if (work->name != new_name) {
+            if (!store && !is_new) {
+                StyleRenamer renamer(c, work->name, new_name);
+                if (renamer.NeedsReplace()) {
+                    // See if user wants to update style name through script
+                    int answer = wxMessageBox(
+                                     _("Do you want to change all instances of this style in the script to this new name?"),
+                                     _("Update script?"),
+                                     wxYES_NO | wxCANCEL);
+
+                    if (answer == wxCANCEL) return;
+
+                    if (answer == wxYES) {
+                        did_rename = true;
+                        renamer.Replace();
+                    }
+                }
+            }
+
+            work->name = new_name;
+        }
+
+        UpdateWorkStyle();
+
+        *style = *work;
+        style->UpdateData();
+        if (is_new) {
+            if (store)
+                store->push_back(std::unique_ptr<AssStyle>(style));
+            else
+                c->ass->Styles.push_back(*style);
+            is_new = false;
+        }
+        if (!store)
+            c->ass->Commit(_("style change"), AssFile::COMMIT_STYLES | (did_rename ? AssFile::COMMIT_DIAG_FULL : 0));
+
+        // Update preview
+        if (!close) SubsPreview->SetStyle(*style);
+    }
+
+    if (close) {
+        EndModal(apply);
+        if (PreviewText)
+            OPT_SET("Tool/Style Editor/Preview Text")->SetString(from_wx(PreviewText->GetValue()));
+    }
 }
 
-void DialogStyleEditor::UpdateWorkStyle() {
-	updating = true;
-	TransferDataFromWindow();
-	updating = false;
+void DialogStyleEditor::UpdateWorkStyle()
+{
+    updating = true;
+    TransferDataFromWindow();
+    updating = false;
 
-	work->font = from_wx(FontName->GetValue());
+    work->font = from_wx(FontName->GetValue());
 
-	long templ = 0;
-	Encoding->GetValue().BeforeFirst('-').ToLong(&templ);
-	work->encoding = templ;
+    long templ = 0;
+    Encoding->GetValue().BeforeFirst('-').ToLong(&templ);
+    work->encoding = templ;
 
-	work->borderstyle = OutlineType->IsChecked() ? 3 : 1;
+    work->borderstyle = OutlineType->IsChecked() ? 3 : 1;
 
-	work->alignment = ControlToAlign(Alignment->GetSelection());
+    work->alignment = ControlToAlign(Alignment->GetSelection());
 
-	for (size_t i = 0; i < 3; ++i)
-		work->Margin[i] = margin[i]->GetValue();
+    for (size_t i = 0; i < 3; ++i)
+        work->Margin[i] = margin[i]->GetValue();
 
-	work->bold = BoxBold->IsChecked();
-	work->italic = BoxItalic->IsChecked();
-	work->underline = BoxUnderline->IsChecked();
-	work->strikeout = BoxStrikeout->IsChecked();
+    work->bold = BoxBold->IsChecked();
+    work->italic = BoxItalic->IsChecked();
+    work->underline = BoxUnderline->IsChecked();
+    work->strikeout = BoxStrikeout->IsChecked();
 }
 
-void DialogStyleEditor::OnSetColor(ValueEvent<agi::Color>&) {
-	TransferDataFromWindow();
-	SubsPreview->SetStyle(*work);
+void DialogStyleEditor::OnSetColor(ValueEvent<agi::Color> &)
+{
+    TransferDataFromWindow();
+    SubsPreview->SetStyle(*work);
 }
 
-void DialogStyleEditor::OnChildFocus(wxChildFocusEvent &event) {
-	UpdateWorkStyle();
-	SubsPreview->SetStyle(*work);
-	event.Skip();
+void DialogStyleEditor::OnChildFocus(wxChildFocusEvent &event)
+{
+    UpdateWorkStyle();
+    SubsPreview->SetStyle(*work);
+    event.Skip();
 }
 
-void DialogStyleEditor::OnPreviewTextChange (wxCommandEvent &event) {
-	SubsPreview->SetText(from_wx(PreviewText->GetValue()));
-	event.Skip();
+void DialogStyleEditor::OnPreviewTextChange (wxCommandEvent &event)
+{
+    SubsPreview->SetText(from_wx(PreviewText->GetValue()));
+    event.Skip();
 }
 
-void DialogStyleEditor::OnPreviewColourChange(ValueEvent<agi::Color> &evt) {
-	SubsPreview->SetColour(evt.Get());
-	OPT_SET("Colour/Style Editor/Background/Preview")->SetColor(evt.Get());
+void DialogStyleEditor::OnPreviewColourChange(ValueEvent<agi::Color> &evt)
+{
+    SubsPreview->SetColour(evt.Get());
+    OPT_SET("Colour/Style Editor/Background/Preview")->SetColor(evt.Get());
 }
 
-void DialogStyleEditor::OnCommandPreviewUpdate(wxCommandEvent &event) {
-	UpdateWorkStyle();
-	SubsPreview->SetStyle(*work);
-	event.Skip();
+void DialogStyleEditor::OnCommandPreviewUpdate(wxCommandEvent &event)
+{
+    UpdateWorkStyle();
+    SubsPreview->SetStyle(*work);
+    event.Skip();
 }
 
-int DialogStyleEditor::ControlToAlign(int n) {
-	switch (n) {
-		case 0: return 7;
-		case 1: return 8;
-		case 2: return 9;
-		case 3: return 4;
-		case 4: return 5;
-		case 5: return 6;
-		case 6: return 1;
-		case 7: return 2;
-		case 8: return 3;
-		default: return 2;
-	}
+int DialogStyleEditor::ControlToAlign(int n)
+{
+    switch (n) {
+    case 0: return 7;
+    case 1: return 8;
+    case 2: return 9;
+    case 3: return 4;
+    case 4: return 5;
+    case 5: return 6;
+    case 6: return 1;
+    case 7: return 2;
+    case 8: return 3;
+    default: return 2;
+    }
 }
 
-int DialogStyleEditor::AlignToControl(int n) {
-	switch (n) {
-		case 7: return 0;
-		case 8: return 1;
-		case 9: return 2;
-		case 4: return 3;
-		case 5: return 4;
-		case 6: return 5;
-		case 1: return 6;
-		case 2: return 7;
-		case 3: return 8;
-		default: return 7;
-	}
+int DialogStyleEditor::AlignToControl(int n)
+{
+    switch (n) {
+    case 7: return 0;
+    case 8: return 1;
+    case 9: return 2;
+    case 4: return 3;
+    case 5: return 4;
+    case 6: return 5;
+    case 1: return 6;
+    case 2: return 7;
+    case 3: return 8;
+    default: return 7;
+    }
 }
diff --git a/src/dialog_style_editor.h b/src/dialog_style_editor.h
index 41ad606ed04fed2199a23dc74d1a26708ff81c48..ab1c7c6ccff621c00a21605d2f7c28485340d72d 100644
--- a/src/dialog_style_editor.h
+++ b/src/dialog_style_editor.h
@@ -49,60 +49,60 @@ namespace agi { struct Context; struct Color; }
 template<typename T> class ValueEvent;
 
 class DialogStyleEditor final : public wxDialog {
-	agi::Context *c;
-	std::unique_ptr<PersistLocation> persist;
+    agi::Context *c;
+    std::unique_ptr<PersistLocation> persist;
 
-	/// If true, the style was just created and so the user should not be
-	/// asked if they want to change any existing lines should they rename
-	/// the style
-	bool is_new = false;
+    /// If true, the style was just created and so the user should not be
+    /// asked if they want to change any existing lines should they rename
+    /// the style
+    bool is_new = false;
 
-	bool updating = false;
+    bool updating = false;
 
-	/// The style currently being edited
-	AssStyle *style;
+    /// The style currently being edited
+    AssStyle *style;
 
-	/// Copy of style passed to the subtitles preview to avoid making changes
-	/// before Apply is clicked
-	std::unique_ptr<AssStyle> work;
+    /// Copy of style passed to the subtitles preview to avoid making changes
+    /// before Apply is clicked
+    std::unique_ptr<AssStyle> work;
 
-	/// The style storage style is in, if applicable
-	AssStyleStorage *store;
+    /// The style storage style is in, if applicable
+    AssStyleStorage *store;
 
-	wxTextCtrl *StyleName;
-	wxComboBox *FontName;
-	wxCheckBox *BoxBold;
-	wxCheckBox *BoxItalic;
-	wxCheckBox *BoxUnderline;
-	wxCheckBox *BoxStrikeout;
-	wxSpinCtrl *margin[3];
-	wxRadioBox *Alignment;
-	wxCheckBox *OutlineType;
-	wxComboBox *Encoding;
-	wxTextCtrl *PreviewText;
-	SubtitlesPreview *SubsPreview;
+    wxTextCtrl *StyleName;
+    wxComboBox *FontName;
+    wxCheckBox *BoxBold;
+    wxCheckBox *BoxItalic;
+    wxCheckBox *BoxUnderline;
+    wxCheckBox *BoxStrikeout;
+    wxSpinCtrl *margin[3];
+    wxRadioBox *Alignment;
+    wxCheckBox *OutlineType;
+    wxComboBox *Encoding;
+    wxTextCtrl *PreviewText;
+    SubtitlesPreview *SubsPreview;
 
-	void SetBitmapColor(int n,wxColour color);
-	int AlignToControl(int n);
-	int ControlToAlign(int n);
-	void UpdateWorkStyle();
+    void SetBitmapColor(int n, wxColour color);
+    int AlignToControl(int n);
+    int ControlToAlign(int n);
+    void UpdateWorkStyle();
 
-	void OnChildFocus(wxChildFocusEvent &event);
-	void OnCommandPreviewUpdate(wxCommandEvent &event);
+    void OnChildFocus(wxChildFocusEvent &event);
+    void OnCommandPreviewUpdate(wxCommandEvent &event);
 
-	void OnPreviewTextChange(wxCommandEvent &event);
-	void OnPreviewColourChange(ValueEvent<agi::Color> &event);
+    void OnPreviewTextChange(wxCommandEvent &event);
+    void OnPreviewColourChange(ValueEvent<agi::Color> &event);
 
-	/// @brief Maybe apply changes and maybe close the dialog
-	/// @param apply Should changes be applied?
-	/// @param close Should the dialog be closed?
-	void Apply(bool apply,bool close);
-	/// @brief Sets color for one of the four color buttons
-	void OnSetColor(ValueEvent<agi::Color>& evt);
+    /// @brief Maybe apply changes and maybe close the dialog
+    /// @param apply Should changes be applied?
+    /// @param close Should the dialog be closed?
+    void Apply(bool apply, bool close);
+    /// @brief Sets color for one of the four color buttons
+    void OnSetColor(ValueEvent<agi::Color> &evt);
 
 public:
-	DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const& new_name, wxArrayString const& font_list);
-	~DialogStyleEditor();
+    DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const &new_name, wxArrayString const &font_list);
+    ~DialogStyleEditor();
 
-	std::string GetStyleName() const;
+    std::string GetStyleName() const;
 };
diff --git a/src/dialog_style_manager.cpp b/src/dialog_style_manager.cpp
index 706ea7146eebdbc3589bfa29b679f38995d00d60..5beba866065da241e9bef935ee1d28b4a8671c32 100644
--- a/src/dialog_style_manager.cpp
+++ b/src/dialog_style_manager.cpp
@@ -77,857 +77,892 @@
 
 namespace {
 class DialogStyleManager final : public wxDialog {
-	agi::Context *c; ///< Project context
-	std::unique_ptr<PersistLocation> persist;
-
-	agi::signal::Connection commit_connection;
-	agi::signal::Connection active_line_connection;
-
-	std::shared_future<wxArrayString> font_list;
-
-	/// Styles in the current subtitle file
-	std::vector<AssStyle*> styleMap;
-
-	/// Style storage manager
-	AssStyleStorage Store;
-
-	wxComboBox *CatalogList;
-	wxListBox *StorageList;
-	wxListBox *CurrentList;
-
-	wxButton *CatalogDelete;
-
-	wxButton *MoveToLocal;
-	wxButton *MoveToStorage;
-
-	wxButton *StorageNew;
-	wxButton *StorageEdit;
-	wxButton *StorageCopy;
-	wxButton *StorageDelete;
-	wxButton *StorageMoveUp;
-	wxButton *StorageMoveDown;
-	wxButton *StorageMoveTop;
-	wxButton *StorageMoveBottom;
-	wxButton *StorageSort;
-
-	wxButton *CurrentNew;
-	wxButton *CurrentEdit;
-	wxButton *CurrentCopy;
-	wxButton *CurrentDelete;
-	wxButton *CurrentMoveUp;
-	wxButton *CurrentMoveDown;
-	wxButton *CurrentMoveTop;
-	wxButton *CurrentMoveBottom;
-	wxButton *CurrentSort;
-
-	/// Load the list of available storages
-	void LoadCatalog();
-	/// Load the style list from the subtitles file
-	void LoadCurrentStyles(int commit_type);
-	/// Enable/disable all of the buttons as appropriate
-	void UpdateButtons();
-	/// Move styles up or down
-	/// @param storage Storage or current file styles
-	/// @param type 0: up; 1: top; 2: down; 3: bottom; 4: sort
-	void MoveStyles(bool storage, int type);
-
-	/// Open the style editor for the given style on the script
-	/// @param style Style to edit, or nullptr for new
-	/// @param new_name Default new name for copies
-	void ShowCurrentEditor(AssStyle *style, std::string const& new_name = "");
-
-	/// Open the style editor for the given style in the storage
-	/// @param style Style to edit, or nullptr for new
-	/// @param new_name Default new name for copies
-	void ShowStorageEditor(AssStyle *style, std::string const& new_name = "");
-
-	/// Save the storage and update the view after a change
-	void UpdateStorage();
-
-	void OnChangeCatalog();
-	void OnCatalogNew();
-	void OnCatalogDelete();
-
-	void OnCopyToCurrent();
-	void OnCopyToStorage();
-
-	void OnCurrentCopy();
-	void OnCurrentDelete();
-	void OnCurrentEdit();
-	void OnCurrentImport();
-	void OnCurrentNew();
-
-	void OnStorageCopy();
-	void OnStorageDelete();
-	void OnStorageEdit();
-	void OnStorageNew();
-
-	void OnKeyDown(wxKeyEvent &event);
-	void PasteToCurrent();
-	void PasteToStorage();
-
-	template<class T>
-	void CopyToClipboard(wxListBox *list, T const& v);
-
-	void OnActiveLineChanged(AssDialogue *new_line);
+    agi::Context *c; ///< Project context
+    std::unique_ptr<PersistLocation> persist;
+
+    agi::signal::Connection commit_connection;
+    agi::signal::Connection active_line_connection;
+
+    std::shared_future<wxArrayString> font_list;
+
+    /// Styles in the current subtitle file
+    std::vector<AssStyle *> styleMap;
+
+    /// Style storage manager
+    AssStyleStorage Store;
+
+    wxComboBox *CatalogList;
+    wxListBox *StorageList;
+    wxListBox *CurrentList;
+
+    wxButton *CatalogDelete;
+
+    wxButton *MoveToLocal;
+    wxButton *MoveToStorage;
+
+    wxButton *StorageNew;
+    wxButton *StorageEdit;
+    wxButton *StorageCopy;
+    wxButton *StorageDelete;
+    wxButton *StorageMoveUp;
+    wxButton *StorageMoveDown;
+    wxButton *StorageMoveTop;
+    wxButton *StorageMoveBottom;
+    wxButton *StorageSort;
+
+    wxButton *CurrentNew;
+    wxButton *CurrentEdit;
+    wxButton *CurrentCopy;
+    wxButton *CurrentDelete;
+    wxButton *CurrentMoveUp;
+    wxButton *CurrentMoveDown;
+    wxButton *CurrentMoveTop;
+    wxButton *CurrentMoveBottom;
+    wxButton *CurrentSort;
+
+    /// Load the list of available storages
+    void LoadCatalog();
+    /// Load the style list from the subtitles file
+    void LoadCurrentStyles(int commit_type);
+    /// Enable/disable all of the buttons as appropriate
+    void UpdateButtons();
+    /// Move styles up or down
+    /// @param storage Storage or current file styles
+    /// @param type 0: up; 1: top; 2: down; 3: bottom; 4: sort
+    void MoveStyles(bool storage, int type);
+
+    /// Open the style editor for the given style on the script
+    /// @param style Style to edit, or nullptr for new
+    /// @param new_name Default new name for copies
+    void ShowCurrentEditor(AssStyle *style, std::string const &new_name = "");
+
+    /// Open the style editor for the given style in the storage
+    /// @param style Style to edit, or nullptr for new
+    /// @param new_name Default new name for copies
+    void ShowStorageEditor(AssStyle *style, std::string const &new_name = "");
+
+    /// Save the storage and update the view after a change
+    void UpdateStorage();
+
+    void OnChangeCatalog();
+    void OnCatalogNew();
+    void OnCatalogDelete();
+
+    void OnCopyToCurrent();
+    void OnCopyToStorage();
+
+    void OnCurrentCopy();
+    void OnCurrentDelete();
+    void OnCurrentEdit();
+    void OnCurrentImport();
+    void OnCurrentNew();
+
+    void OnStorageCopy();
+    void OnStorageDelete();
+    void OnStorageEdit();
+    void OnStorageNew();
+
+    void OnKeyDown(wxKeyEvent &event);
+    void PasteToCurrent();
+    void PasteToStorage();
+
+    template<class T>
+    void CopyToClipboard(wxListBox *list, T const &v);
+
+    void OnActiveLineChanged(AssDialogue *new_line);
 
 public:
-	DialogStyleManager(agi::Context *context);
+    DialogStyleManager(agi::Context *context);
 };
 
-wxBitmapButton *add_bitmap_button(wxWindow *parent, wxSizer *sizer, wxBitmap const& img, wxString const& tooltip) {
-	wxBitmapButton *btn = new wxBitmapButton(parent, -1, img);
-	btn->SetToolTip(tooltip);
-	sizer->Add(btn, wxSizerFlags().Expand());
-	return btn;
+wxBitmapButton *add_bitmap_button(wxWindow *parent, wxSizer *sizer, wxBitmap const &img, wxString const &tooltip)
+{
+    wxBitmapButton *btn = new wxBitmapButton(parent, -1, img);
+    btn->SetToolTip(tooltip);
+    sizer->Add(btn, wxSizerFlags().Expand());
+    return btn;
 }
 
-wxSizer *make_move_buttons(wxWindow *parent, wxButton **up, wxButton **down, wxButton **top, wxButton **bottom, wxButton **sort) {
-	wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
-	sizer->AddStretchSpacer(1);
+wxSizer *make_move_buttons(wxWindow *parent, wxButton **up, wxButton **down, wxButton **top, wxButton **bottom, wxButton **sort)
+{
+    wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+    sizer->AddStretchSpacer(1);
 
-	*up     = add_bitmap_button(parent, sizer, GETIMAGE(arrow_up_24), _("Move style up"));
-	*down   = add_bitmap_button(parent, sizer, GETIMAGE(arrow_down_24), _("Move style down"));
-	*top    = add_bitmap_button(parent, sizer, GETIMAGE(arrow_up_stop_24), _("Move style to top"));
-	*bottom = add_bitmap_button(parent, sizer, GETIMAGE(arrow_down_stop_24), _("Move style to bottom"));
-	*sort   = add_bitmap_button(parent, sizer, GETIMAGE(arrow_sort_24), _("Sort styles alphabetically"));
+    *up     = add_bitmap_button(parent, sizer, GETIMAGE(arrow_up_24), _("Move style up"));
+    *down   = add_bitmap_button(parent, sizer, GETIMAGE(arrow_down_24), _("Move style down"));
+    *top    = add_bitmap_button(parent, sizer, GETIMAGE(arrow_up_stop_24), _("Move style to top"));
+    *bottom = add_bitmap_button(parent, sizer, GETIMAGE(arrow_down_stop_24), _("Move style to bottom"));
+    *sort   = add_bitmap_button(parent, sizer, GETIMAGE(arrow_sort_24), _("Sort styles alphabetically"));
 
-	sizer->AddStretchSpacer(1);
-	return sizer;
+    sizer->AddStretchSpacer(1);
+    return sizer;
 }
 
-wxSizer *make_edit_buttons(wxWindow *parent, wxString move_label, wxButton **move, wxButton **nw, wxButton **edit, wxButton **copy, wxButton **del) {
-	wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
+wxSizer *make_edit_buttons(wxWindow *parent, wxString move_label, wxButton **move, wxButton **nw, wxButton **edit, wxButton **copy, wxButton **del)
+{
+    wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
 
-	*move = new wxButton(parent, -1, move_label);
-	*nw = new wxButton(parent, -1, _("&New"));
-	*edit = new wxButton(parent, -1, _("&Edit"));
-	*copy = new wxButton(parent, -1, _("&Copy"));
-	*del = new wxButton(parent, -1, _("&Delete"));
+    *move = new wxButton(parent, -1, move_label);
+    *nw = new wxButton(parent, -1, _("&New"));
+    *edit = new wxButton(parent, -1, _("&Edit"));
+    *copy = new wxButton(parent, -1, _("&Copy"));
+    *del = new wxButton(parent, -1, _("&Delete"));
 
-	sizer->Add(*nw, wxSizerFlags(1).Expand().Border(wxRIGHT));
-	sizer->Add(*edit, wxSizerFlags(1).Expand().Border(wxRIGHT));
-	sizer->Add(*copy, wxSizerFlags(1).Expand().Border(wxRIGHT));
-	sizer->Add(*del, wxSizerFlags(1).Expand());
+    sizer->Add(*nw, wxSizerFlags(1).Expand().Border(wxRIGHT));
+    sizer->Add(*edit, wxSizerFlags(1).Expand().Border(wxRIGHT));
+    sizer->Add(*copy, wxSizerFlags(1).Expand().Border(wxRIGHT));
+    sizer->Add(*del, wxSizerFlags(1).Expand());
 
-	return sizer;
+    return sizer;
 }
 
 template<class Func>
-std::string unique_name(Func name_checker, std::string const& source_name) {
-	if (name_checker(source_name)) {
-		std::string name = agi::format(_("%s - Copy"), source_name);
-		for (int i = 2; name_checker(name); ++i)
-			name = agi::format(_("%s - Copy (%d)"), source_name, i);
-		return name;
-	}
-	return source_name;
+std::string unique_name(Func name_checker, std::string const &source_name)
+{
+    if (name_checker(source_name)) {
+        std::string name = agi::format(_("%s - Copy"), source_name);
+        for (int i = 2; name_checker(name); ++i)
+            name = agi::format(_("%s - Copy (%d)"), source_name, i);
+        return name;
+    }
+    return source_name;
 }
 
 template<class Func1, class Func2>
-void add_styles(Func1 name_checker, Func2 style_adder) {
-	auto cb = GetClipboard();
-	int failed_to_parse = 0;
-	for (auto tok : agi::Split(cb, '\n')) {
-		tok = boost::trim_copy(tok);
-		if (tok.empty()) continue;
-		try {
-			AssStyle *s = new AssStyle(agi::str(tok));
-			s->name = unique_name(name_checker, s->name);
-			style_adder(s);
-		}
-		catch (...) {
-			++failed_to_parse;
-		}
-	}
-	if (failed_to_parse)
-		wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION);
-}
-
-int confirm_delete(int n, wxWindow *parent, wxString const& title) {
-	return wxMessageBox(
-		fmt_plural(n, "Are you sure you want to delete this style?", "Are you sure you want to delete these %d styles?", n),
-		title, wxYES_NO | wxICON_EXCLAMATION, parent);
-}
-
-int get_single_sel(wxListBox *lb) {
-	wxArrayInt selections;
-	int n = lb->GetSelections(selections);
-	return n == 1 ? selections[0] : -1;
+void add_styles(Func1 name_checker, Func2 style_adder)
+{
+    auto cb = GetClipboard();
+    int failed_to_parse = 0;
+    for (auto tok : agi::Split(cb, '\n')) {
+        tok = boost::trim_copy(tok);
+        if (tok.empty()) continue;
+        try {
+            AssStyle *s = new AssStyle(agi::str(tok));
+            s->name = unique_name(name_checker, s->name);
+            style_adder(s);
+        }
+        catch (...) {
+            ++failed_to_parse;
+        }
+    }
+    if (failed_to_parse)
+        wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION);
+}
+
+int confirm_delete(int n, wxWindow *parent, wxString const &title)
+{
+    return wxMessageBox(
+               fmt_plural(n, "Are you sure you want to delete this style?", "Are you sure you want to delete these %d styles?", n),
+               title, wxYES_NO | wxICON_EXCLAMATION, parent);
+}
+
+int get_single_sel(wxListBox *lb)
+{
+    wxArrayInt selections;
+    int n = lb->GetSelections(selections);
+    return n == 1 ? selections[0] : -1;
 }
 
 DialogStyleManager::DialogStyleManager(agi::Context *context)
-: wxDialog(context->parent, -1, _("Styles Manager"))
-, c(context)
-, commit_connection(c->ass->AddCommitListener(&DialogStyleManager::LoadCurrentStyles, this))
-, active_line_connection(c->selectionController->AddActiveLineListener(&DialogStyleManager::OnActiveLineChanged, this))
-, font_list(std::async(std::launch::async, []() -> wxArrayString {
-	wxArrayString fontList = wxFontEnumerator::GetFacenames();
-	fontList.Sort();
-	return fontList;
+    : wxDialog(context->parent, -1, _("Styles Manager"))
+    , c(context)
+    , commit_connection(c->ass->AddCommitListener(&DialogStyleManager::LoadCurrentStyles, this))
+    , active_line_connection(c->selectionController->AddActiveLineListener(&DialogStyleManager::OnActiveLineChanged, this))
+    , font_list(std::async(std::launch::async, []() -> wxArrayString {
+    wxArrayString fontList = wxFontEnumerator::GetFacenames();
+    fontList.Sort();
+    return fontList;
 }))
 {
-	using std::bind;
-	SetIcon(GETICON(style_toolbutton_16));
-
-	// Catalog
-	wxSizer *CatalogBox = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Catalog of available storages"));
-	CatalogList = new wxComboBox(this,-1, "", wxDefaultPosition, wxSize(-1,-1), 0, nullptr, wxCB_READONLY);
-	wxButton *CatalogNew = new wxButton(this, -1, _("New"));
-	CatalogDelete = new wxButton(this, -1, _("Delete"));
-	CatalogBox->Add(CatalogList,1,wxEXPAND | wxRIGHT,5);
-	CatalogBox->Add(CatalogNew,0,wxRIGHT,5);
-	CatalogBox->Add(CatalogDelete,0,0,0);
-
-	// Storage styles list
-	wxSizer *StorageButtons = make_edit_buttons(this, _("Copy to &current script ->"), &MoveToLocal, &StorageNew, &StorageEdit, &StorageCopy, &StorageDelete);
-
-	wxSizer *StorageListSizer = new wxBoxSizer(wxHORIZONTAL);
-	StorageList = new wxListBox(this, -1, wxDefaultPosition, wxSize(240,250), 0, nullptr, wxLB_EXTENDED);
-	StorageListSizer->Add(StorageList,1,wxEXPAND | wxRIGHT,0);
-	StorageListSizer->Add(make_move_buttons(this, &StorageMoveUp, &StorageMoveDown, &StorageMoveTop, &StorageMoveBottom, &StorageSort), wxSizerFlags().Expand());
-
-	wxSizer *StorageBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Storage"));
-	StorageBox->Add(StorageListSizer,1,wxEXPAND | wxBOTTOM,5);
-	StorageBox->Add(MoveToLocal,0,wxEXPAND | wxBOTTOM,5);
-	StorageBox->Add(StorageButtons,0,wxEXPAND | wxBOTTOM,0);
-
-	// Local styles list
-	wxButton *CurrentImport = new wxButton(this, -1, _("&Import from script..."));
-	wxSizer *CurrentButtons = make_edit_buttons(this, _("<- Copy to &storage"), &MoveToStorage, &CurrentNew, &CurrentEdit, &CurrentCopy, &CurrentDelete);
-
-	wxSizer *MoveImportSizer = new wxBoxSizer(wxHORIZONTAL);
-	MoveImportSizer->Add(MoveToStorage,1,wxEXPAND | wxRIGHT,5);
-	MoveImportSizer->Add(CurrentImport,1,wxEXPAND,0);
-
-	wxSizer *CurrentListSizer = new wxBoxSizer(wxHORIZONTAL);
-	CurrentList = new wxListBox(this, -1, wxDefaultPosition, wxSize(240,250), 0, nullptr, wxLB_EXTENDED);
-	CurrentListSizer->Add(CurrentList,1,wxEXPAND | wxRIGHT,0);
-	CurrentListSizer->Add(make_move_buttons(this, &CurrentMoveUp, &CurrentMoveDown, &CurrentMoveTop, &CurrentMoveBottom, &CurrentSort), wxSizerFlags().Expand());
-
-	wxSizer *CurrentBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Current script"));
-	CurrentBox->Add(CurrentListSizer,1,wxEXPAND | wxBOTTOM,5);
-	CurrentBox->Add(MoveImportSizer,0,wxEXPAND | wxBOTTOM,5);
-	CurrentBox->Add(CurrentButtons,0,wxEXPAND | wxBOTTOM,0);
-
-	// Buttons
-	wxStdDialogButtonSizer *buttonSizer = CreateStdDialogButtonSizer(wxCANCEL | wxHELP);
-	buttonSizer->GetCancelButton()->SetLabel(_("Close"));
-	Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Styles Manager"), wxID_HELP);
-
-	// General layout
-	wxSizer *StylesSizer = new wxBoxSizer(wxHORIZONTAL);
-	StylesSizer->Add(StorageBox,0,wxRIGHT | wxEXPAND,5);
-	StylesSizer->Add(CurrentBox,0,wxLEFT | wxEXPAND,0);
-	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(CatalogBox,0,wxEXPAND | wxLEFT | wxRIGHT | wxTOP,5);
-	MainSizer->Add(StylesSizer,1,wxEXPAND | wxALL,5);
-	MainSizer->Add(buttonSizer,0,wxBOTTOM | wxEXPAND,5);
+    using std::bind;
+    SetIcon(GETICON(style_toolbutton_16));
+
+    // Catalog
+    wxSizer *CatalogBox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Catalog of available storages"));
+    CatalogList = new wxComboBox(this, -1, "", wxDefaultPosition, wxSize(-1, -1), 0, nullptr, wxCB_READONLY);
+    wxButton *CatalogNew = new wxButton(this, -1, _("New"));
+    CatalogDelete = new wxButton(this, -1, _("Delete"));
+    CatalogBox->Add(CatalogList, 1, wxEXPAND | wxRIGHT, 5);
+    CatalogBox->Add(CatalogNew, 0, wxRIGHT, 5);
+    CatalogBox->Add(CatalogDelete, 0, 0, 0);
+
+    // Storage styles list
+    wxSizer *StorageButtons = make_edit_buttons(this, _("Copy to &current script ->"), &MoveToLocal, &StorageNew, &StorageEdit, &StorageCopy, &StorageDelete);
+
+    wxSizer *StorageListSizer = new wxBoxSizer(wxHORIZONTAL);
+    StorageList = new wxListBox(this, -1, wxDefaultPosition, wxSize(240, 250), 0, nullptr, wxLB_EXTENDED);
+    StorageListSizer->Add(StorageList, 1, wxEXPAND | wxRIGHT, 0);
+    StorageListSizer->Add(make_move_buttons(this, &StorageMoveUp, &StorageMoveDown, &StorageMoveTop, &StorageMoveBottom, &StorageSort), wxSizerFlags().Expand());
+
+    wxSizer *StorageBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Storage"));
+    StorageBox->Add(StorageListSizer, 1, wxEXPAND | wxBOTTOM, 5);
+    StorageBox->Add(MoveToLocal, 0, wxEXPAND | wxBOTTOM, 5);
+    StorageBox->Add(StorageButtons, 0, wxEXPAND | wxBOTTOM, 0);
+
+    // Local styles list
+    wxButton *CurrentImport = new wxButton(this, -1, _("&Import from script..."));
+    wxSizer *CurrentButtons = make_edit_buttons(this, _("<- Copy to &storage"), &MoveToStorage, &CurrentNew, &CurrentEdit, &CurrentCopy, &CurrentDelete);
+
+    wxSizer *MoveImportSizer = new wxBoxSizer(wxHORIZONTAL);
+    MoveImportSizer->Add(MoveToStorage, 1, wxEXPAND | wxRIGHT, 5);
+    MoveImportSizer->Add(CurrentImport, 1, wxEXPAND, 0);
+
+    wxSizer *CurrentListSizer = new wxBoxSizer(wxHORIZONTAL);
+    CurrentList = new wxListBox(this, -1, wxDefaultPosition, wxSize(240, 250), 0, nullptr, wxLB_EXTENDED);
+    CurrentListSizer->Add(CurrentList, 1, wxEXPAND | wxRIGHT, 0);
+    CurrentListSizer->Add(make_move_buttons(this, &CurrentMoveUp, &CurrentMoveDown, &CurrentMoveTop, &CurrentMoveBottom, &CurrentSort), wxSizerFlags().Expand());
+
+    wxSizer *CurrentBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Current script"));
+    CurrentBox->Add(CurrentListSizer, 1, wxEXPAND | wxBOTTOM, 5);
+    CurrentBox->Add(MoveImportSizer, 0, wxEXPAND | wxBOTTOM, 5);
+    CurrentBox->Add(CurrentButtons, 0, wxEXPAND | wxBOTTOM, 0);
+
+    // Buttons
+    wxStdDialogButtonSizer *buttonSizer = CreateStdDialogButtonSizer(wxCANCEL | wxHELP);
+    buttonSizer->GetCancelButton()->SetLabel(_("Close"));
+    Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Styles Manager"), wxID_HELP);
+
+    // General layout
+    wxSizer *StylesSizer = new wxBoxSizer(wxHORIZONTAL);
+    StylesSizer->Add(StorageBox, 0, wxRIGHT | wxEXPAND, 5);
+    StylesSizer->Add(CurrentBox, 0, wxLEFT | wxEXPAND, 0);
+    wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(CatalogBox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5);
+    MainSizer->Add(StylesSizer, 1, wxEXPAND | wxALL, 5);
+    MainSizer->Add(buttonSizer, 0, wxBOTTOM | wxEXPAND, 5);
+
+    SetSizerAndFit(MainSizer);
+
+    // Position window
+    persist = agi::make_unique<PersistLocation>(this, "Tool/Style Manager");
+
+    // Populate lists
+    LoadCatalog();
+    LoadCurrentStyles(AssFile::COMMIT_STYLES | AssFile::COMMIT_DIAG_META);
+
+    //Set key handlers for lists
+    CatalogList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
+    StorageList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
+    CurrentList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
+
+    StorageMoveUp->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(true, 0); });
+    StorageMoveTop->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(true, 1); });
+    StorageMoveDown->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(true, 2); });
+    StorageMoveBottom->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(true, 3); });
+    StorageSort->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(true, 4); });
+
+    CurrentMoveUp->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(false, 0); });
+    CurrentMoveTop->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(false, 1); });
+    CurrentMoveDown->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(false, 2); });
+    CurrentMoveBottom->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(false, 3); });
+    CurrentSort->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { MoveStyles(false, 4); });
+
+    CatalogNew->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCatalogNew(); });
+    CatalogDelete->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCatalogDelete(); });
+
+    StorageNew->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnStorageNew(); });
+    StorageEdit->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnStorageEdit(); });
+    StorageCopy->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnStorageCopy(); });
+    StorageDelete->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnStorageDelete(); });
+
+    CurrentNew->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCurrentNew(); });
+    CurrentEdit->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCurrentEdit(); });
+    CurrentCopy->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCurrentCopy(); });
+    CurrentDelete->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCurrentDelete(); });
+
+    CurrentImport->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCurrentImport(); });
+
+    MoveToLocal->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCopyToCurrent(); });
+    MoveToStorage->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OnCopyToStorage(); });
+
+    CatalogList->Bind(wxEVT_COMBOBOX, [ = ](wxCommandEvent &) { OnChangeCatalog(); });
+
+    StorageList->Bind(wxEVT_LISTBOX, [ = ](wxCommandEvent &) { UpdateButtons(); });
+    StorageList->Bind(wxEVT_LISTBOX_DCLICK, [ = ](wxCommandEvent &) { OnStorageEdit(); });
+
+    CurrentList->Bind(wxEVT_LISTBOX, [ = ](wxCommandEvent &) { UpdateButtons(); });
+    CurrentList->Bind(wxEVT_LISTBOX_DCLICK, [ = ](wxCommandEvent &) { OnCurrentEdit(); });
+}
 
-	SetSizerAndFit(MainSizer);
+void DialogStyleManager::LoadCurrentStyles(int commit_type)
+{
+    if (commit_type & AssFile::COMMIT_STYLES || commit_type == AssFile::COMMIT_NEW) {
+        CurrentList->Clear();
+        styleMap.clear();
 
-	// Position window
-	persist = agi::make_unique<PersistLocation>(this, "Tool/Style Manager");
+        for (auto &style : c->ass->Styles) {
+            CurrentList->Append(to_wx(style.name));
+            styleMap.push_back(&style);
+        }
+    }
 
-	// Populate lists
-	LoadCatalog();
-	LoadCurrentStyles(AssFile::COMMIT_STYLES | AssFile::COMMIT_DIAG_META);
+    if (commit_type & AssFile::COMMIT_DIAG_META) {
+        AssDialogue *dia = c->selectionController->GetActiveLine();
+        CurrentList->DeselectAll();
+        if (dia && commit_type != AssFile::COMMIT_NEW)
+            CurrentList->SetStringSelection(to_wx(dia->Style));
+        else
+            CurrentList->SetSelection(0);
+    }
 
-	//Set key handlers for lists
-	CatalogList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
-	StorageList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
-	CurrentList->Bind(wxEVT_KEY_DOWN, &DialogStyleManager::OnKeyDown, this);
-
-	StorageMoveUp->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(true, 0); });
-	StorageMoveTop->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(true, 1); });
-	StorageMoveDown->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(true, 2); });
-	StorageMoveBottom->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(true, 3); });
-	StorageSort->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(true, 4); });
-
-	CurrentMoveUp->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(false, 0); });
-	CurrentMoveTop->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(false, 1); });
-	CurrentMoveDown->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(false, 2); });
-	CurrentMoveBottom->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(false, 3); });
-	CurrentSort->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { MoveStyles(false, 4); });
+    UpdateButtons();
+}
 
-	CatalogNew->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCatalogNew(); });
-	CatalogDelete->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCatalogDelete(); });
+void DialogStyleManager::OnActiveLineChanged(AssDialogue *new_line)
+{
+    if (new_line) {
+        CurrentList->DeselectAll();
+        CurrentList->SetStringSelection(to_wx(new_line->Style));
+        UpdateButtons();
+    }
+}
 
-	StorageNew->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnStorageNew(); });
-	StorageEdit->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnStorageEdit(); });
-	StorageCopy->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnStorageCopy(); });
-	StorageDelete->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnStorageDelete(); });
+void DialogStyleManager::UpdateStorage()
+{
+    Store.Save();
+
+    StorageList->Clear();
+    StorageList->Append(to_wx(Store.GetNames()));
 
-	CurrentNew->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCurrentNew(); });
-	CurrentEdit->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCurrentEdit(); });
-	CurrentCopy->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCurrentCopy(); });
-	CurrentDelete->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCurrentDelete(); });
-
-	CurrentImport->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCurrentImport(); });
+    UpdateButtons();
+}
 
-	MoveToLocal->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCopyToCurrent(); });
-	MoveToStorage->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OnCopyToStorage(); });
-
-	CatalogList->Bind(wxEVT_COMBOBOX, [=](wxCommandEvent&) { OnChangeCatalog(); });
-
-	StorageList->Bind(wxEVT_LISTBOX, [=](wxCommandEvent&) { UpdateButtons(); });
-	StorageList->Bind(wxEVT_LISTBOX_DCLICK, [=](wxCommandEvent&) { OnStorageEdit(); });
-
-	CurrentList->Bind(wxEVT_LISTBOX, [=](wxCommandEvent&) { UpdateButtons(); });
-	CurrentList->Bind(wxEVT_LISTBOX_DCLICK, [=](wxCommandEvent&) { OnCurrentEdit(); });
-}
-
-void DialogStyleManager::LoadCurrentStyles(int commit_type) {
-	if (commit_type & AssFile::COMMIT_STYLES || commit_type == AssFile::COMMIT_NEW) {
-		CurrentList->Clear();
-		styleMap.clear();
-
-		for (auto& style : c->ass->Styles) {
-			CurrentList->Append(to_wx(style.name));
-			styleMap.push_back(&style);
-		}
-	}
-
-	if (commit_type & AssFile::COMMIT_DIAG_META) {
-		AssDialogue *dia = c->selectionController->GetActiveLine();
-		CurrentList->DeselectAll();
-		if (dia && commit_type != AssFile::COMMIT_NEW)
-			CurrentList->SetStringSelection(to_wx(dia->Style));
-		else
-			CurrentList->SetSelection(0);
-	}
-
-	UpdateButtons();
-}
-
-void DialogStyleManager::OnActiveLineChanged(AssDialogue *new_line) {
-	if (new_line) {
-		CurrentList->DeselectAll();
-		CurrentList->SetStringSelection(to_wx(new_line->Style));
-		UpdateButtons();
-	}
-}
-
-void DialogStyleManager::UpdateStorage() {
-	Store.Save();
-
-	StorageList->Clear();
-	StorageList->Append(to_wx(Store.GetNames()));
-
-	UpdateButtons();
-}
-
-void DialogStyleManager::OnChangeCatalog() {
-	std::string catalog(from_wx(CatalogList->GetStringSelection()));
-	c->ass->Properties.style_storage = catalog;
-	Store.LoadCatalog(catalog);
-	UpdateStorage();
-}
-
-void DialogStyleManager::LoadCatalog() {
-	CatalogList->Clear();
-
-	// Get saved style catalogs
-	auto catalogs = AssStyleStorage::GetCatalogs();
-	for (auto const& c : catalogs)
-		CatalogList->Append(to_wx(c));
-
-	// Create a default storage if there are none
-	if (CatalogList->IsListEmpty()) {
-		Store.LoadCatalog("Default");
-		Store.push_back(agi::make_unique<AssStyle>());
-		Store.Save();
-		CatalogList->Append("Default");
-	}
-
-	// Set to default if available
-	std::string pickStyle = c->ass->Properties.style_storage;
-	if (pickStyle.empty())
-		pickStyle = "Default";
-
-	int opt = CatalogList->FindString(to_wx(pickStyle), false);
-	CatalogList->SetSelection(opt == wxNOT_FOUND ? 0 : opt);
-
-	OnChangeCatalog();
-}
-
-void DialogStyleManager::OnCatalogNew() {
-	wxString name = wxGetTextFromUser(_("New storage name:"), _("New catalog entry"), "", this);
-	if (!name) return;
-
-	// Remove bad characters from the name
-	wxString badchars = wxFileName::GetForbiddenChars(wxPATH_DOS);
-	int badchars_removed = 0;
-	for (wxUniCharRef chr : name) {
-		if (badchars.find(chr) != badchars.npos) {
-			chr = '_';
-			++badchars_removed;
-		}
-	}
-
-	// Make sure that there is no storage with the same name (case insensitive search since Windows filenames are case insensitive)
-	if (CatalogList->FindString(name, false) != wxNOT_FOUND) {
-		wxMessageBox(_("A catalog with that name already exists."), _("Catalog name conflict"), wxOK | wxICON_ERROR | wxCENTER);
-		return;
-	}
-
-	// Warn about bad characters
-	if (badchars_removed) {
-		wxMessageBox(
-			fmt_tl("The specified catalog name contains one or more illegal characters. They have been replaced with underscores instead.\nThe catalog has been renamed to \"%s\".", name),
-			_("Invalid characters"));
-	}
-
-	// Add to list of storages
-	CatalogList->Append(name);
-	CatalogList->SetStringSelection(name);
-	OnChangeCatalog();
-}
-
-void DialogStyleManager::OnCatalogDelete() {
-	if (CatalogList->GetCount() == 1) return;
-
-	wxString name = CatalogList->GetStringSelection();
-	wxString message = fmt_tl("Are you sure you want to delete the storage \"%s\" from the catalog?", name);
-	int option = wxMessageBox(message, _("Confirm delete"), wxYES_NO | wxICON_EXCLAMATION , this);
-	if (option == wxYES) {
-		agi::fs::Remove(config::path->Decode("?user/catalog/" + from_wx(name) + ".sty"));
-		CatalogList->Delete(CatalogList->GetSelection());
-		CatalogList->SetSelection(0);
-		OnChangeCatalog();
-	}
-}
-
-void DialogStyleManager::OnCopyToStorage() {
-	wxArrayInt selections;
-	int n = CurrentList->GetSelections(selections);
-	wxArrayString copied;
-	copied.reserve(n);
-	for (int i = 0; i < n; i++) {
-		wxString styleName = CurrentList->GetString(selections[i]);
-
-		if (AssStyle *style = Store.GetStyle(from_wx(styleName))) {
-			if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current storage. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
-				*style = *styleMap.at(selections[i]);
-				copied.push_back(styleName);
-			}
-		}
-		else {
-			Store.push_back(agi::make_unique<AssStyle>(*styleMap.at(selections[i])));
-			copied.push_back(styleName);
-		}
-	}
-
-	UpdateStorage();
-	for (auto const& style_name : copied)
-		StorageList->SetStringSelection(style_name, true);
-
-	UpdateButtons();
-}
-
-void DialogStyleManager::OnCopyToCurrent() {
-	wxArrayInt selections;
-	int n = StorageList->GetSelections(selections);
-	wxArrayString copied;
-	copied.reserve(n);
-	for (int i = 0; i < n; i++) {
-		wxString styleName = StorageList->GetString(selections[i]);
-
-		if (AssStyle *style = c->ass->GetStyle(from_wx(styleName))) {
-			if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
-				*style = *Store[selections[i]];
-				copied.push_back(styleName);
-			}
-		}
-		else {
-			c->ass->Styles.push_back(*new AssStyle(*Store[selections[i]]));
-			copied.push_back(styleName);
-		}
-	}
-
-	c->ass->Commit(_("style copy"), AssFile::COMMIT_STYLES);
-
-	CurrentList->DeselectAll();
-	for (auto const& style_name : copied)
-		CurrentList->SetStringSelection(style_name, true);
-	UpdateButtons();
+void DialogStyleManager::OnChangeCatalog()
+{
+    std::string catalog(from_wx(CatalogList->GetStringSelection()));
+    c->ass->Properties.style_storage = catalog;
+    Store.LoadCatalog(catalog);
+    UpdateStorage();
+}
+
+void DialogStyleManager::LoadCatalog()
+{
+    CatalogList->Clear();
+
+    // Get saved style catalogs
+    auto catalogs = AssStyleStorage::GetCatalogs();
+    for (auto const &c : catalogs)
+        CatalogList->Append(to_wx(c));
+
+    // Create a default storage if there are none
+    if (CatalogList->IsListEmpty()) {
+        Store.LoadCatalog("Default");
+        Store.push_back(agi::make_unique<AssStyle>());
+        Store.Save();
+        CatalogList->Append("Default");
+    }
+
+    // Set to default if available
+    std::string pickStyle = c->ass->Properties.style_storage;
+    if (pickStyle.empty())
+        pickStyle = "Default";
+
+    int opt = CatalogList->FindString(to_wx(pickStyle), false);
+    CatalogList->SetSelection(opt == wxNOT_FOUND ? 0 : opt);
+
+    OnChangeCatalog();
+}
+
+void DialogStyleManager::OnCatalogNew()
+{
+    wxString name = wxGetTextFromUser(_("New storage name:"), _("New catalog entry"), "", this);
+    if (!name) return;
+
+    // Remove bad characters from the name
+    wxString badchars = wxFileName::GetForbiddenChars(wxPATH_DOS);
+    int badchars_removed = 0;
+    for (wxUniCharRef chr : name) {
+        if (badchars.find(chr) != badchars.npos) {
+            chr = '_';
+            ++badchars_removed;
+        }
+    }
+
+    // Make sure that there is no storage with the same name (case insensitive search since Windows filenames are case insensitive)
+    if (CatalogList->FindString(name, false) != wxNOT_FOUND) {
+        wxMessageBox(_("A catalog with that name already exists."), _("Catalog name conflict"), wxOK | wxICON_ERROR | wxCENTER);
+        return;
+    }
+
+    // Warn about bad characters
+    if (badchars_removed) {
+        wxMessageBox(
+            fmt_tl("The specified catalog name contains one or more illegal characters. They have been replaced with underscores instead.\nThe catalog has been renamed to \"%s\".", name),
+            _("Invalid characters"));
+    }
+
+    // Add to list of storages
+    CatalogList->Append(name);
+    CatalogList->SetStringSelection(name);
+    OnChangeCatalog();
+}
+
+void DialogStyleManager::OnCatalogDelete()
+{
+    if (CatalogList->GetCount() == 1) return;
+
+    wxString name = CatalogList->GetStringSelection();
+    wxString message = fmt_tl("Are you sure you want to delete the storage \"%s\" from the catalog?", name);
+    int option = wxMessageBox(message, _("Confirm delete"), wxYES_NO | wxICON_EXCLAMATION, this);
+    if (option == wxYES) {
+        agi::fs::Remove(config::path->Decode("?user/catalog/" + from_wx(name) + ".sty"));
+        CatalogList->Delete(CatalogList->GetSelection());
+        CatalogList->SetSelection(0);
+        OnChangeCatalog();
+    }
+}
+
+void DialogStyleManager::OnCopyToStorage()
+{
+    wxArrayInt selections;
+    int n = CurrentList->GetSelections(selections);
+    wxArrayString copied;
+    copied.reserve(n);
+    for (int i = 0; i < n; i++) {
+        wxString styleName = CurrentList->GetString(selections[i]);
+
+        if (AssStyle *style = Store.GetStyle(from_wx(styleName))) {
+            if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current storage. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
+                *style = *styleMap.at(selections[i]);
+                copied.push_back(styleName);
+            }
+        }
+        else {
+            Store.push_back(agi::make_unique<AssStyle>(*styleMap.at(selections[i])));
+            copied.push_back(styleName);
+        }
+    }
+
+    UpdateStorage();
+    for (auto const &style_name : copied)
+        StorageList->SetStringSelection(style_name, true);
+
+    UpdateButtons();
+}
+
+void DialogStyleManager::OnCopyToCurrent()
+{
+    wxArrayInt selections;
+    int n = StorageList->GetSelections(selections);
+    wxArrayString copied;
+    copied.reserve(n);
+    for (int i = 0; i < n; i++) {
+        wxString styleName = StorageList->GetString(selections[i]);
+
+        if (AssStyle *style = c->ass->GetStyle(from_wx(styleName))) {
+            if (wxYES == wxMessageBox(fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styleName), _("Style name collision"), wxYES_NO)) {
+                *style = *Store[selections[i]];
+                copied.push_back(styleName);
+            }
+        }
+        else {
+            c->ass->Styles.push_back(*new AssStyle(*Store[selections[i]]));
+            copied.push_back(styleName);
+        }
+    }
+
+    c->ass->Commit(_("style copy"), AssFile::COMMIT_STYLES);
+
+    CurrentList->DeselectAll();
+    for (auto const &style_name : copied)
+        CurrentList->SetStringSelection(style_name, true);
+    UpdateButtons();
 }
 
 template<class T>
-void DialogStyleManager::CopyToClipboard(wxListBox *list, T const& v) {
-	wxArrayInt selections;
-	list->GetSelections(selections);
+void DialogStyleManager::CopyToClipboard(wxListBox *list, T const &v)
+{
+    wxArrayInt selections;
+    list->GetSelections(selections);
 
-	std::string data;
-	for(size_t i = 0; i < selections.size(); ++i) {
-		if (i) data += "\r\n";
-		AssStyle *s = v[selections[i]];
-		s->UpdateData();
-		data += s->GetEntryData();
-	}
+    std::string data;
+    for (size_t i = 0; i < selections.size(); ++i) {
+        if (i) data += "\r\n";
+        AssStyle *s = v[selections[i]];
+        s->UpdateData();
+        data += s->GetEntryData();
+    }
 
-	SetClipboard(data);
+    SetClipboard(data);
 }
 
-void DialogStyleManager::PasteToCurrent() {
-	add_styles(
-		[=](std::string const& str) { return c->ass->GetStyle(str); },
-		[=](AssStyle *s) { c->ass->Styles.push_back(*s); });
+void DialogStyleManager::PasteToCurrent()
+{
+    add_styles(
+    [ = ](std::string const & str) { return c->ass->GetStyle(str); },
+    [ = ](AssStyle * s) { c->ass->Styles.push_back(*s); });
 
-	c->ass->Commit(_("style paste"), AssFile::COMMIT_STYLES);
+    c->ass->Commit(_("style paste"), AssFile::COMMIT_STYLES);
 }
 
-void DialogStyleManager::PasteToStorage() {
-	add_styles(
-		[=](std::string const& str) { return Store.GetStyle(str); },
-		[=](AssStyle *s) { Store.push_back(std::unique_ptr<AssStyle>(s)); });
+void DialogStyleManager::PasteToStorage()
+{
+    add_styles(
+    [ = ](std::string const & str) { return Store.GetStyle(str); },
+    [ = ](AssStyle * s) { Store.push_back(std::unique_ptr<AssStyle>(s)); });
 
-	UpdateStorage();
-	StorageList->SetStringSelection(to_wx(Store.back()->name));
-	UpdateButtons();
+    UpdateStorage();
+    StorageList->SetStringSelection(to_wx(Store.back()->name));
+    UpdateButtons();
 }
 
-void DialogStyleManager::ShowStorageEditor(AssStyle *style, std::string const& new_name) {
-	DialogStyleEditor editor(this, style, c, &Store, new_name, font_list.get());
-	if (editor.ShowModal()) {
-		UpdateStorage();
-		StorageList->SetStringSelection(to_wx(editor.GetStyleName()));
-		UpdateButtons();
-	}
+void DialogStyleManager::ShowStorageEditor(AssStyle *style, std::string const &new_name)
+{
+    DialogStyleEditor editor(this, style, c, &Store, new_name, font_list.get());
+    if (editor.ShowModal()) {
+        UpdateStorage();
+        StorageList->SetStringSelection(to_wx(editor.GetStyleName()));
+        UpdateButtons();
+    }
 }
 
-void DialogStyleManager::OnStorageNew() {
-	ShowStorageEditor(nullptr);
+void DialogStyleManager::OnStorageNew()
+{
+    ShowStorageEditor(nullptr);
 }
 
-void DialogStyleManager::OnStorageEdit() {
-	int sel = get_single_sel(StorageList);
-	if (sel == -1) return;
-	ShowStorageEditor(Store[sel]);
+void DialogStyleManager::OnStorageEdit()
+{
+    int sel = get_single_sel(StorageList);
+    if (sel == -1) return;
+    ShowStorageEditor(Store[sel]);
+}
+
+void DialogStyleManager::OnStorageCopy()
+{
+    int sel = get_single_sel(StorageList);
+    if (sel == -1) return;
+
+    ShowStorageEditor(Store[sel], unique_name(
+    [ = ](std::string const & str) { return Store.GetStyle(str); }, Store[sel]->name));
 }
 
-void DialogStyleManager::OnStorageCopy() {
-	int sel = get_single_sel(StorageList);
-	if (sel == -1) return;
+void DialogStyleManager::OnStorageDelete()
+{
+    wxArrayInt selections;
+    int n = StorageList->GetSelections(selections);
 
-	ShowStorageEditor(Store[sel], unique_name(
-		[=](std::string const& str) { return Store.GetStyle(str); }, Store[sel]->name));
+    if (confirm_delete(n, this, _("Confirm delete from storage")) == wxYES) {
+        for (int i = 0; i < n; i++)
+            Store.Delete(selections[i] - i);
+        UpdateStorage();
+    }
 }
-
-void DialogStyleManager::OnStorageDelete() {
-	wxArrayInt selections;
-	int n = StorageList->GetSelections(selections);
-
-	if (confirm_delete(n, this, _("Confirm delete from storage")) == wxYES) {
-		for (int i = 0; i < n; i++)
-			Store.Delete(selections[i] - i);
-		UpdateStorage();
-	}
-}
-
-void DialogStyleManager::ShowCurrentEditor(AssStyle *style, std::string const& new_name) {
-	DialogStyleEditor editor(this, style, c, nullptr, new_name, font_list.get());
-	if (editor.ShowModal()) {
-		CurrentList->DeselectAll();
-		CurrentList->SetStringSelection(to_wx(editor.GetStyleName()));
-		UpdateButtons();
-	}
-}
-
-void DialogStyleManager::OnCurrentNew() {
-	ShowCurrentEditor(nullptr);
-}
-
-void DialogStyleManager::OnCurrentEdit() {
-	int sel = get_single_sel(CurrentList);
-	if (sel == -1) return;
-	ShowCurrentEditor(styleMap[sel]);
-}
-
-void DialogStyleManager::OnCurrentCopy() {
-	int sel = get_single_sel(CurrentList);
-	if (sel == -1) return;
-
-	ShowCurrentEditor(styleMap[sel], unique_name(
-		[=](std::string const& str) { return c->ass->GetStyle(str); },
-		styleMap[sel]->name));
-}
-
-void DialogStyleManager::OnCurrentDelete() {
-	wxArrayInt selections;
-	int n = CurrentList->GetSelections(selections);
-
-	if (confirm_delete(n, this, _("Confirm delete from current")) == wxYES) {
-		for (int i = 0; i < n; i++) {
-			delete styleMap.at(selections[i]);
-		}
-		c->ass->Commit(_("style delete"), AssFile::COMMIT_STYLES);
-	}
-}
-
-void DialogStyleManager::OnCurrentImport() {
-	auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "", "", SubtitleFormat::GetWildcards(0), this);
-	if (filename.empty()) return;
-
-	std::string charset;
-	try {
-		charset = CharSetDetect::GetEncoding(filename);
-	}
-	catch (agi::UserCancelException const&) {
-		return;
-	}
-
-	AssFile temp;
-	try {
-		auto reader = SubtitleFormat::GetReader(filename, charset);
-		if (!reader)
-			wxMessageBox("Unsupported subtitle format", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
-		else
-			reader->ReadFile(&temp, filename, 0, charset);
-	}
-	catch (agi::Exception const& err) {
-		wxMessageBox(to_wx(err.GetMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this);
-	}
-	catch (...) {
-		wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
-		return;
-	}
-
-	// Get styles
-	auto styles = temp.GetStyles();
-	if (styles.empty()) {
-		wxMessageBox(_("The selected file has no available styles."), _("Error Importing Styles"));
-		return;
-	}
-
-	// Get selection
-	wxArrayInt selections;
-	int res = GetSelectedChoices(this, selections, _("Choose styles to import:"), _("Import Styles"), to_wx(styles));
-	if (res == -1 || selections.empty()) return;
-	bool modified = false;
-
-	// Loop through selection
-	for (auto const& sel : selections) {
-		// Check if there is already a style with that name
-		if (AssStyle *existing = c->ass->GetStyle(styles[sel])) {
-			int answer = wxMessageBox(
-				fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styles[sel]),
-				_("Style name collision"),
-				wxYES_NO);
-			if (answer == wxYES) {
-				modified = true;
-				*existing = *temp.GetStyle(styles[sel]);
-			}
-			continue;
-		}
-
-		// Copy
-		modified = true;
-		c->ass->Styles.push_back(*new AssStyle(*temp.GetStyle(styles[sel])));
-	}
-
-	// Update
-	if (modified)
-		c->ass->Commit(_("style import"), AssFile::COMMIT_STYLES);
-}
-
-void DialogStyleManager::UpdateButtons() {
-	CatalogDelete->Enable(CatalogList->GetCount() > 1);
-
-	// Get storage selection
-	wxArrayInt sels;
-	int n = StorageList->GetSelections(sels);
-
-	StorageEdit->Enable(n == 1);
-	StorageCopy->Enable(n == 1);
-	StorageDelete->Enable(n > 0);
-	MoveToLocal->Enable(n > 0);
-
-	int firstStor = -1;
-	int lastStor = -1;
-	if (n) {
-		firstStor = sels[0];
-		lastStor = sels[n-1];
-	}
-
-	// Check if selection is continuous
-	bool contStor = true;
-	for (int i = 1; i < n; ++i) {
-		if (sels[i] != sels[i-1]+1) {
-			contStor = false;
-			break;
-		}
-	}
-
-	int itemsStor = StorageList->GetCount();
-	StorageMoveUp->Enable(contStor && firstStor > 0);
-	StorageMoveTop->Enable(contStor && firstStor > 0);
-	StorageMoveDown->Enable(contStor && lastStor != -1 && lastStor < itemsStor-1);
-	StorageMoveBottom->Enable(contStor && lastStor != -1 && lastStor < itemsStor-1);
-	StorageSort->Enable(itemsStor > 1);
-
-	// Get current selection
-	n = CurrentList->GetSelections(sels);
-
-	CurrentEdit->Enable(n == 1);
-	CurrentCopy->Enable(n == 1);
-	CurrentDelete->Enable(n > 0);
-	MoveToStorage->Enable(n > 0);
-
-	int firstCurr = -1;
-	int lastCurr = -1;
-	if (n) {
-		firstCurr = sels[0];
-		lastCurr = sels[n-1];
-	}
-
-	// Check if selection is continuous
-	bool contCurr = true;
-	for (int i = 1; i < n; ++i) {
-		if (sels[i] != sels[i-1]+1) {
-			contCurr = false;
-			break;
-		}
-	}
-
-	int itemsCurr = CurrentList->GetCount();
-	CurrentMoveUp->Enable(contCurr && firstCurr > 0);
-	CurrentMoveTop->Enable(contCurr && firstCurr > 0);
-	CurrentMoveDown->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr-1);
-	CurrentMoveBottom->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr-1);
-	CurrentSort->Enable(itemsCurr > 1);
+
+void DialogStyleManager::ShowCurrentEditor(AssStyle *style, std::string const &new_name)
+{
+    DialogStyleEditor editor(this, style, c, nullptr, new_name, font_list.get());
+    if (editor.ShowModal()) {
+        CurrentList->DeselectAll();
+        CurrentList->SetStringSelection(to_wx(editor.GetStyleName()));
+        UpdateButtons();
+    }
+}
+
+void DialogStyleManager::OnCurrentNew()
+{
+    ShowCurrentEditor(nullptr);
+}
+
+void DialogStyleManager::OnCurrentEdit()
+{
+    int sel = get_single_sel(CurrentList);
+    if (sel == -1) return;
+    ShowCurrentEditor(styleMap[sel]);
+}
+
+void DialogStyleManager::OnCurrentCopy()
+{
+    int sel = get_single_sel(CurrentList);
+    if (sel == -1) return;
+
+    ShowCurrentEditor(styleMap[sel], unique_name(
+    [ = ](std::string const & str) { return c->ass->GetStyle(str); },
+    styleMap[sel]->name));
+}
+
+void DialogStyleManager::OnCurrentDelete()
+{
+    wxArrayInt selections;
+    int n = CurrentList->GetSelections(selections);
+
+    if (confirm_delete(n, this, _("Confirm delete from current")) == wxYES) {
+        for (int i = 0; i < n; i++) {
+            delete styleMap.at(selections[i]);
+        }
+        c->ass->Commit(_("style delete"), AssFile::COMMIT_STYLES);
+    }
+}
+
+void DialogStyleManager::OnCurrentImport()
+{
+    auto filename = OpenFileSelector(_("Open subtitles file"), "Path/Last/Subtitles", "", "", SubtitleFormat::GetWildcards(0), this);
+    if (filename.empty()) return;
+
+    std::string charset;
+    try {
+        charset = CharSetDetect::GetEncoding(filename);
+    }
+    catch (agi::UserCancelException const &) {
+        return;
+    }
+
+    AssFile temp;
+    try {
+        auto reader = SubtitleFormat::GetReader(filename, charset);
+        if (!reader)
+            wxMessageBox("Unsupported subtitle format", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
+        else
+            reader->ReadFile(&temp, filename, 0, charset);
+    }
+    catch (agi::Exception const &err) {
+        wxMessageBox(to_wx(err.GetMessage()), "Error", wxOK | wxICON_ERROR | wxCENTER, this);
+    }
+    catch (...) {
+        wxMessageBox("Unknown error", "Error", wxOK | wxICON_ERROR | wxCENTER, this);
+        return;
+    }
+
+    // Get styles
+    auto styles = temp.GetStyles();
+    if (styles.empty()) {
+        wxMessageBox(_("The selected file has no available styles."), _("Error Importing Styles"));
+        return;
+    }
+
+    // Get selection
+    wxArrayInt selections;
+    int res = GetSelectedChoices(this, selections, _("Choose styles to import:"), _("Import Styles"), to_wx(styles));
+    if (res == -1 || selections.empty()) return;
+    bool modified = false;
+
+    // Loop through selection
+    for (auto const &sel : selections) {
+        // Check if there is already a style with that name
+        if (AssStyle *existing = c->ass->GetStyle(styles[sel])) {
+            int answer = wxMessageBox(
+                             fmt_tl("There is already a style with the name \"%s\" in the current script. Overwrite?", styles[sel]),
+                             _("Style name collision"),
+                             wxYES_NO);
+            if (answer == wxYES) {
+                modified = true;
+                *existing = *temp.GetStyle(styles[sel]);
+            }
+            continue;
+        }
+
+        // Copy
+        modified = true;
+        c->ass->Styles.push_back(*new AssStyle(*temp.GetStyle(styles[sel])));
+    }
+
+    // Update
+    if (modified)
+        c->ass->Commit(_("style import"), AssFile::COMMIT_STYLES);
+}
+
+void DialogStyleManager::UpdateButtons()
+{
+    CatalogDelete->Enable(CatalogList->GetCount() > 1);
+
+    // Get storage selection
+    wxArrayInt sels;
+    int n = StorageList->GetSelections(sels);
+
+    StorageEdit->Enable(n == 1);
+    StorageCopy->Enable(n == 1);
+    StorageDelete->Enable(n > 0);
+    MoveToLocal->Enable(n > 0);
+
+    int firstStor = -1;
+    int lastStor = -1;
+    if (n) {
+        firstStor = sels[0];
+        lastStor = sels[n - 1];
+    }
+
+    // Check if selection is continuous
+    bool contStor = true;
+    for (int i = 1; i < n; ++i) {
+        if (sels[i] != sels[i - 1] + 1) {
+            contStor = false;
+            break;
+        }
+    }
+
+    int itemsStor = StorageList->GetCount();
+    StorageMoveUp->Enable(contStor && firstStor > 0);
+    StorageMoveTop->Enable(contStor && firstStor > 0);
+    StorageMoveDown->Enable(contStor && lastStor != -1 && lastStor < itemsStor - 1);
+    StorageMoveBottom->Enable(contStor && lastStor != -1 && lastStor < itemsStor - 1);
+    StorageSort->Enable(itemsStor > 1);
+
+    // Get current selection
+    n = CurrentList->GetSelections(sels);
+
+    CurrentEdit->Enable(n == 1);
+    CurrentCopy->Enable(n == 1);
+    CurrentDelete->Enable(n > 0);
+    MoveToStorage->Enable(n > 0);
+
+    int firstCurr = -1;
+    int lastCurr = -1;
+    if (n) {
+        firstCurr = sels[0];
+        lastCurr = sels[n - 1];
+    }
+
+    // Check if selection is continuous
+    bool contCurr = true;
+    for (int i = 1; i < n; ++i) {
+        if (sels[i] != sels[i - 1] + 1) {
+            contCurr = false;
+            break;
+        }
+    }
+
+    int itemsCurr = CurrentList->GetCount();
+    CurrentMoveUp->Enable(contCurr && firstCurr > 0);
+    CurrentMoveTop->Enable(contCurr && firstCurr > 0);
+    CurrentMoveDown->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr - 1);
+    CurrentMoveBottom->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr - 1);
+    CurrentSort->Enable(itemsCurr > 1);
 }
 
 struct cmp_name {
-	template<typename T>
-	bool operator()(T const& lft, T const& rgt) const { return lft->name < rgt->name; }
+    template<typename T>
+    bool operator()(T const &lft, T const &rgt) const { return lft->name < rgt->name; }
 };
 
 template<class Cont>
-static void do_move(Cont& styls, int type, int& first, int& last, bool storage) {
-	auto begin = styls.begin();
-
-	// Move up
-	if (type == 0) {
-		if (first == 0) return;
-		rotate(begin + first - 1, begin + first, begin + last + 1);
-		first--;
-		last--;
-	}
-	// Move to top
-	else if (type == 1) {
-		rotate(begin, begin + first, begin + last + 1);
-		last = last - first;
-		first = 0;
-	}
-	// Move down
-	else if (type == 2) {
-		if (last + 1 == (int)styls.size()) return;
-		rotate(begin + first, begin + last + 1, begin + last + 2);
-		first++;
-		last++;
-	}
-	// Move to bottom
-	else if (type == 3) {
-		rotate(begin + first, begin + last + 1, styls.end());
-		first = styls.size() - (last - first + 1);
-		last = styls.size() - 1;
-	}
-	// Sort
-	else if (type == 4) {
-		// Get confirmation
-		if (storage) {
-			int res = wxMessageBox(_("Are you sure? This cannot be undone!"), _("Sort styles"), wxYES_NO | wxCENTER);
-			if (res == wxNO) return;
-		}
-
-		sort(styls.begin(), styls.end(), cmp_name());
-
-		first = 0;
-		last = 0;
-	}
-}
-
-void DialogStyleManager::MoveStyles(bool storage, int type) {
-	wxListBox *list = storage ? StorageList : CurrentList;
-
-	// Get selection
-	wxArrayInt sels;
-	int n = list->GetSelections(sels);
-	if (n == 0 && type != 4) return;
-
-	int first = 0, last = 0;
-	if (n) {
-		first = sels.front();
-		last = sels.back();
-	}
-
-	if (storage) {
-		do_move(Store, type, first, last, true);
-		UpdateStorage();
-	}
-	else {
-		do_move(styleMap, type, first, last, false);
-
-		// Replace styles
-		size_t curn = 0;
-		for (auto it = c->ass->Styles.begin(); it != c->ass->Styles.end(); ++it) {
-			auto new_style_at_pos = c->ass->Styles.iterator_to(*styleMap[curn]);
-			EntryList<AssStyle>::node_algorithms::swap_nodes(it.pointed_node(), new_style_at_pos.pointed_node());
-			if (++curn == styleMap.size()) break;
-			it = new_style_at_pos;
-		}
-
-		c->ass->Commit(_("style move"), AssFile::COMMIT_STYLES);
-	}
-
-	for (int i = 0 ; i < (int)list->GetCount(); ++i) {
-		if (i < first || i > last)
-			list->Deselect(i);
-		else
-			list->Select(i);
-	}
-
-	UpdateButtons();
-}
-
-void DialogStyleManager::OnKeyDown(wxKeyEvent &event) {
-	wxWindow *focus = wxWindow::FindFocus();
-
-	switch(event.GetKeyCode()) {
-		case WXK_DELETE :
-			if (focus == StorageList)
-				OnStorageDelete();
-			else if (focus == CurrentList)
-				OnCurrentDelete();
-			break;
-
-		case 'C' :
-		case 'c' :
-			if (event.CmdDown()) {
-				if (focus == StorageList)
-					CopyToClipboard(StorageList, Store);
-				else if (focus == CurrentList)
-					CopyToClipboard(CurrentList, styleMap);
-			}
-			break;
-
-		case 'V' :
-		case 'v' :
-			if (event.CmdDown()) {
-				if (focus == StorageList)
-					PasteToStorage();
-				else if (focus == CurrentList)
-					PasteToCurrent();
-			}
-			break;
-		default:
-			event.Skip();
-			break;
-	}
-}
-}
-
-void ShowStyleManagerDialog(agi::Context *c) {
-	c->dialog->Show<DialogStyleManager>(c);
+static void do_move(Cont &styls, int type, int &first, int &last, bool storage)
+{
+    auto begin = styls.begin();
+
+    // Move up
+    if (type == 0) {
+        if (first == 0) return;
+        rotate(begin + first - 1, begin + first, begin + last + 1);
+        first--;
+        last--;
+    }
+    // Move to top
+    else if (type == 1) {
+        rotate(begin, begin + first, begin + last + 1);
+        last = last - first;
+        first = 0;
+    }
+    // Move down
+    else if (type == 2) {
+        if (last + 1 == (int)styls.size()) return;
+        rotate(begin + first, begin + last + 1, begin + last + 2);
+        first++;
+        last++;
+    }
+    // Move to bottom
+    else if (type == 3) {
+        rotate(begin + first, begin + last + 1, styls.end());
+        first = styls.size() - (last - first + 1);
+        last = styls.size() - 1;
+    }
+    // Sort
+    else if (type == 4) {
+        // Get confirmation
+        if (storage) {
+            int res = wxMessageBox(_("Are you sure? This cannot be undone!"), _("Sort styles"), wxYES_NO | wxCENTER);
+            if (res == wxNO) return;
+        }
+
+        sort(styls.begin(), styls.end(), cmp_name());
+
+        first = 0;
+        last = 0;
+    }
+}
+
+void DialogStyleManager::MoveStyles(bool storage, int type)
+{
+    wxListBox *list = storage ? StorageList : CurrentList;
+
+    // Get selection
+    wxArrayInt sels;
+    int n = list->GetSelections(sels);
+    if (n == 0 && type != 4) return;
+
+    int first = 0, last = 0;
+    if (n) {
+        first = sels.front();
+        last = sels.back();
+    }
+
+    if (storage) {
+        do_move(Store, type, first, last, true);
+        UpdateStorage();
+    }
+    else {
+        do_move(styleMap, type, first, last, false);
+
+        // Replace styles
+        size_t curn = 0;
+        for (auto it = c->ass->Styles.begin(); it != c->ass->Styles.end(); ++it) {
+            auto new_style_at_pos = c->ass->Styles.iterator_to(*styleMap[curn]);
+            EntryList<AssStyle>::node_algorithms::swap_nodes(it.pointed_node(), new_style_at_pos.pointed_node());
+            if (++curn == styleMap.size()) break;
+            it = new_style_at_pos;
+        }
+
+        c->ass->Commit(_("style move"), AssFile::COMMIT_STYLES);
+    }
+
+    for (int i = 0 ; i < (int)list->GetCount(); ++i) {
+        if (i < first || i > last)
+            list->Deselect(i);
+        else
+            list->Select(i);
+    }
+
+    UpdateButtons();
+}
+
+void DialogStyleManager::OnKeyDown(wxKeyEvent &event)
+{
+    wxWindow *focus = wxWindow::FindFocus();
+
+    switch (event.GetKeyCode()) {
+    case WXK_DELETE :
+        if (focus == StorageList)
+            OnStorageDelete();
+        else if (focus == CurrentList)
+            OnCurrentDelete();
+        break;
+
+    case 'C' :
+    case 'c' :
+        if (event.CmdDown()) {
+            if (focus == StorageList)
+                CopyToClipboard(StorageList, Store);
+            else if (focus == CurrentList)
+                CopyToClipboard(CurrentList, styleMap);
+        }
+        break;
+
+    case 'V' :
+    case 'v' :
+        if (event.CmdDown()) {
+            if (focus == StorageList)
+                PasteToStorage();
+            else if (focus == CurrentList)
+                PasteToCurrent();
+        }
+        break;
+    default:
+        event.Skip();
+        break;
+    }
+}
+}
+
+void ShowStyleManagerDialog(agi::Context *c)
+{
+    c->dialog->Show<DialogStyleManager>(c);
 }
diff --git a/src/dialog_styling_assistant.cpp b/src/dialog_styling_assistant.cpp
index 77688cad98cb3ed19aeb11208d46377d43fcc458..8b5dbb2071fbbcf4bb21de99296772a8b160f0ea 100644
--- a/src/dialog_styling_assistant.cpp
+++ b/src/dialog_styling_assistant.cpp
@@ -46,213 +46,225 @@
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 
-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, to_wx(hotkey::get_hotkey_str_first("Styling Assistant", command))));
+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, to_wx(hotkey::get_hotkey_str_first("Styling Assistant", command))));
 }
 
 DialogStyling::DialogStyling(agi::Context *context)
-: wxDialog(context->parent, -1, _("Styling Assistant"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX)
-, c(context)
-, active_line_connection(context->selectionController->AddActiveLineListener(&DialogStyling::OnActiveLineChanged, this))
+    : wxDialog(context->parent, -1, _("Styling Assistant"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX)
+    , c(context)
+    , active_line_connection(context->selectionController->AddActiveLineListener(&DialogStyling::OnActiveLineChanged, this))
 {
-	SetIcon(GETICON(styling_toolbutton_16));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	wxSizer *bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
-
-	{
-		wxSizer *cur_line_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Current line"));
-		current_line_text = new wxTextCtrl(this, -1, _("Current line"), wxDefaultPosition, wxSize(300, 60), wxTE_MULTILINE | wxTE_READONLY);
-		cur_line_box->Add(current_line_text, 1, wxEXPAND, 0);
-		main_sizer->Add(cur_line_box, 0, wxEXPAND | wxALL, 5);
-	}
-
-	{
-		wxSizer *styles_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Styles available"));
-		style_list = new wxListBox(this, -1, wxDefaultPosition, wxSize(150, 180), to_wx(context->ass->GetStyles()));
-		styles_box->Add(style_list, 1, wxEXPAND, 0);
-		bottom_sizer->Add(styles_box, 1, wxEXPAND | wxRIGHT, 5);
-	}
-
-	wxSizer *right_sizer = new wxBoxSizer(wxVERTICAL);
-	{
-		wxSizer *style_text_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Set style"));
-		style_name = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(180, -1), wxTE_PROCESS_ENTER);
-		style_text_box->Add(style_name, 1, wxEXPAND);
-		right_sizer->Add(style_text_box, 0, wxEXPAND | wxBOTTOM, 5);
-	}
-
-	{
-		wxSizer *hotkey_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Keys"));
-
-		wxSizer *hotkey_grid = new wxGridSizer(2, 0, 5);
-		add_hotkey(hotkey_grid, this, "tool/styling_assistant/commit", _("Accept changes"));
-		add_hotkey(hotkey_grid, this, "tool/styling_assistant/preview", _("Preview changes"));
-		add_hotkey(hotkey_grid, this, "grid/line/prev", _("Previous line"));
-		add_hotkey(hotkey_grid, this, "grid/line/next", _("Next line"));
-		add_hotkey(hotkey_grid, this, "video/play/line", _("Play video"));
-		add_hotkey(hotkey_grid, this, "audio/play/selection", _("Play audio"));
-		hotkey_grid->Add(new wxStaticText(this, -1, _("Click on list")));
-		hotkey_grid->Add(new wxStaticText(this, -1, _("Select style")));
-
-		hotkey_box->Add(hotkey_grid, 0, wxEXPAND | wxBOTTOM, 5);
-
-		auto_seek = new wxCheckBox(this, -1, _("&Seek video to line start time"));
-		auto_seek->SetValue(true);
-		hotkey_box->Add(auto_seek, 0, 0, 0);
-		hotkey_box->AddStretchSpacer(1);
-
-		right_sizer->Add(hotkey_box, 0, wxEXPAND | wxBOTTOM, 5);
-	}
-
-	{
-		wxSizer *actions_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Actions"));
-		actions_box->AddStretchSpacer(1);
-
-		play_audio = new wxButton(this, -1, _("Play &Audio"));
-		play_audio->Enable(!!c->project->AudioProvider());
-		actions_box->Add(play_audio, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5);
-
-		play_video = new wxButton(this, -1, _("Play &Video"));
-		play_video->Enable(!!c->project->VideoProvider());
-		actions_box->Add(play_video, 0, wxBOTTOM | wxRIGHT, 5);
-
-		actions_box->AddStretchSpacer(1);
-		right_sizer->Add(actions_box, 0, wxEXPAND, 5);
-	}
-	bottom_sizer->Add(right_sizer);
-	main_sizer->Add(bottom_sizer, 1, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 5);
-
-	{
-		auto button_sizer = new wxStdDialogButtonSizer;
-		button_sizer->AddButton(new wxButton(this, wxID_CANCEL));
-		button_sizer->AddButton(new HelpButton(this, "Styling Assistant"));
-		button_sizer->Realize();
-
-		main_sizer->Add(button_sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 5);
-	}
-
-	SetSizerAndFit(main_sizer);
-
-	persist = agi::make_unique<PersistLocation>(this, "Tool/Styling Assistant");
-
-	Bind(wxEVT_ACTIVATE, &DialogStyling::OnActivate, this);
-	Bind(wxEVT_CHAR_HOOK, &DialogStyling::OnCharHook, this);
-	style_name->Bind(wxEVT_CHAR_HOOK, &DialogStyling::OnCharHook, this);
-	style_name->Bind(wxEVT_KEY_DOWN, &DialogStyling::OnKeyDown, this);
-	play_video->Bind(wxEVT_BUTTON, &DialogStyling::OnPlayVideoButton, this);
-	play_audio->Bind(wxEVT_BUTTON, &DialogStyling::OnPlayAudioButton, this);
-	style_list->Bind(wxEVT_LISTBOX, &DialogStyling::OnListClicked, this);
-	style_list->Bind(wxEVT_LISTBOX_DCLICK, &DialogStyling::OnListDoubleClicked, this);
-	style_name->Bind(wxEVT_TEXT, &DialogStyling::OnStyleBoxModified, this);
-
-	OnActiveLineChanged(c->selectionController->GetActiveLine());
+    SetIcon(GETICON(styling_toolbutton_16));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    wxSizer *bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+    {
+        wxSizer *cur_line_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Current line"));
+        current_line_text = new wxTextCtrl(this, -1, _("Current line"), wxDefaultPosition, wxSize(300, 60), wxTE_MULTILINE | wxTE_READONLY);
+        cur_line_box->Add(current_line_text, 1, wxEXPAND, 0);
+        main_sizer->Add(cur_line_box, 0, wxEXPAND | wxALL, 5);
+    }
+
+    {
+        wxSizer *styles_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Styles available"));
+        style_list = new wxListBox(this, -1, wxDefaultPosition, wxSize(150, 180), to_wx(context->ass->GetStyles()));
+        styles_box->Add(style_list, 1, wxEXPAND, 0);
+        bottom_sizer->Add(styles_box, 1, wxEXPAND | wxRIGHT, 5);
+    }
+
+    wxSizer *right_sizer = new wxBoxSizer(wxVERTICAL);
+    {
+        wxSizer *style_text_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Set style"));
+        style_name = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(180, -1), wxTE_PROCESS_ENTER);
+        style_text_box->Add(style_name, 1, wxEXPAND);
+        right_sizer->Add(style_text_box, 0, wxEXPAND | wxBOTTOM, 5);
+    }
+
+    {
+        wxSizer *hotkey_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Keys"));
+
+        wxSizer *hotkey_grid = new wxGridSizer(2, 0, 5);
+        add_hotkey(hotkey_grid, this, "tool/styling_assistant/commit", _("Accept changes"));
+        add_hotkey(hotkey_grid, this, "tool/styling_assistant/preview", _("Preview changes"));
+        add_hotkey(hotkey_grid, this, "grid/line/prev", _("Previous line"));
+        add_hotkey(hotkey_grid, this, "grid/line/next", _("Next line"));
+        add_hotkey(hotkey_grid, this, "video/play/line", _("Play video"));
+        add_hotkey(hotkey_grid, this, "audio/play/selection", _("Play audio"));
+        hotkey_grid->Add(new wxStaticText(this, -1, _("Click on list")));
+        hotkey_grid->Add(new wxStaticText(this, -1, _("Select style")));
+
+        hotkey_box->Add(hotkey_grid, 0, wxEXPAND | wxBOTTOM, 5);
+
+        auto_seek = new wxCheckBox(this, -1, _("&Seek video to line start time"));
+        auto_seek->SetValue(true);
+        hotkey_box->Add(auto_seek, 0, 0, 0);
+        hotkey_box->AddStretchSpacer(1);
+
+        right_sizer->Add(hotkey_box, 0, wxEXPAND | wxBOTTOM, 5);
+    }
+
+    {
+        wxSizer *actions_box = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Actions"));
+        actions_box->AddStretchSpacer(1);
+
+        play_audio = new wxButton(this, -1, _("Play &Audio"));
+        play_audio->Enable(!!c->project->AudioProvider());
+        actions_box->Add(play_audio, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5);
+
+        play_video = new wxButton(this, -1, _("Play &Video"));
+        play_video->Enable(!!c->project->VideoProvider());
+        actions_box->Add(play_video, 0, wxBOTTOM | wxRIGHT, 5);
+
+        actions_box->AddStretchSpacer(1);
+        right_sizer->Add(actions_box, 0, wxEXPAND, 5);
+    }
+    bottom_sizer->Add(right_sizer);
+    main_sizer->Add(bottom_sizer, 1, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 5);
+
+    {
+        auto button_sizer = new wxStdDialogButtonSizer;
+        button_sizer->AddButton(new wxButton(this, wxID_CANCEL));
+        button_sizer->AddButton(new HelpButton(this, "Styling Assistant"));
+        button_sizer->Realize();
+
+        main_sizer->Add(button_sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 5);
+    }
+
+    SetSizerAndFit(main_sizer);
+
+    persist = agi::make_unique<PersistLocation>(this, "Tool/Styling Assistant");
+
+    Bind(wxEVT_ACTIVATE, &DialogStyling::OnActivate, this);
+    Bind(wxEVT_CHAR_HOOK, &DialogStyling::OnCharHook, this);
+    style_name->Bind(wxEVT_CHAR_HOOK, &DialogStyling::OnCharHook, this);
+    style_name->Bind(wxEVT_KEY_DOWN, &DialogStyling::OnKeyDown, this);
+    play_video->Bind(wxEVT_BUTTON, &DialogStyling::OnPlayVideoButton, this);
+    play_audio->Bind(wxEVT_BUTTON, &DialogStyling::OnPlayAudioButton, this);
+    style_list->Bind(wxEVT_LISTBOX, &DialogStyling::OnListClicked, this);
+    style_list->Bind(wxEVT_LISTBOX_DCLICK, &DialogStyling::OnListDoubleClicked, this);
+    style_name->Bind(wxEVT_TEXT, &DialogStyling::OnStyleBoxModified, this);
+
+    OnActiveLineChanged(c->selectionController->GetActiveLine());
 }
 
-DialogStyling::~DialogStyling () {
+DialogStyling::~DialogStyling ()
+{
 }
 
-void DialogStyling::OnActiveLineChanged(AssDialogue *new_line) {
-	if (!new_line) return;
-	active_line = new_line;
+void DialogStyling::OnActiveLineChanged(AssDialogue *new_line)
+{
+    if (!new_line) return;
+    active_line = new_line;
 
-	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();
+    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(to_wx(active_line->Style));
+    style_list->SetStringSelection(to_wx(active_line->Style));
 
-	if (auto_seek->IsChecked() && IsActive())
-		c->videoController->JumpToTime(active_line->Start);
+    if (auto_seek->IsChecked() && IsActive())
+        c->videoController->JumpToTime(active_line->Start);
 }
 
-void DialogStyling::Commit(bool next) {
-	if (!c->ass->GetStyle(from_wx(style_name->GetValue()))) return;
+void DialogStyling::Commit(bool next)
+{
+    if (!c->ass->GetStyle(from_wx(style_name->GetValue()))) return;
 
-	active_line->Style = from_wx(style_name->GetValue());
-	c->ass->Commit(_("styling assistant"), AssFile::COMMIT_DIAG_META);
+    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);
+    if (next) cmd::call("grid/line/next", c);
 }
 
-void DialogStyling::OnActivate(wxActivateEvent &) {
-	if (!IsActive()) return;
+void DialogStyling::OnActivate(wxActivateEvent &)
+{
+    if (!IsActive()) return;
 
-	play_video->Enable(!!c->project->VideoProvider());
-	play_audio->Enable(!!c->project->AudioProvider());
+    play_video->Enable(!!c->project->VideoProvider());
+    play_audio->Enable(!!c->project->AudioProvider());
 
-	style_list->Set(to_wx(c->ass->GetStyles()));
+    style_list->Set(to_wx(c->ass->GetStyles()));
 
-	if (auto_seek->IsChecked())
-		c->videoController->JumpToTime(active_line->Start);
+    if (auto_seek->IsChecked())
+        c->videoController->JumpToTime(active_line->Start);
 
-	style_name->SetFocus();
+    style_name->SetFocus();
 }
 
-void DialogStyling::OnStyleBoxModified(wxCommandEvent &) {
-	long from, to;
-	style_name->GetSelection(&from, &to);
-	wxString prefix = style_name->GetValue().Left(from).Lower();
-
-	if (prefix.empty()) {
-		style_name->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-		return;
-	}
-
-	// Find the first style name which the contents of the box could be the
-	// beginning of
-	for (size_t i = 0; i < style_list->GetCount(); ++i) {
-		wxString style = style_list->GetString(i);
-		if (style.Lower().StartsWith(prefix)) {
-			style_name->ChangeValue(style);
-			style_name->SetSelection(prefix.size(), style.size());
-			style_name->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-			style_name->Refresh();
-			return;
-		}
-	}
-
-	style_name->SetBackgroundColour(wxColour(255, 108, 108));
-	style_name->Refresh();
+void DialogStyling::OnStyleBoxModified(wxCommandEvent &)
+{
+    long from, to;
+    style_name->GetSelection(&from, &to);
+    wxString prefix = style_name->GetValue().Left(from).Lower();
+
+    if (prefix.empty()) {
+        style_name->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+        return;
+    }
+
+    // Find the first style name which the contents of the box could be the
+    // beginning of
+    for (size_t i = 0; i < style_list->GetCount(); ++i) {
+        wxString style = style_list->GetString(i);
+        if (style.Lower().StartsWith(prefix)) {
+            style_name->ChangeValue(style);
+            style_name->SetSelection(prefix.size(), style.size());
+            style_name->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+            style_name->Refresh();
+            return;
+        }
+    }
+
+    style_name->SetBackgroundColour(wxColour(255, 108, 108));
+    style_name->Refresh();
 }
 
-void DialogStyling::OnListClicked(wxCommandEvent &evt) {
-	style_name->ChangeValue(style_list->GetString(evt.GetInt()));
-	Commit(false);
-	style_name->SetFocus();
+void DialogStyling::OnListClicked(wxCommandEvent &evt)
+{
+    style_name->ChangeValue(style_list->GetString(evt.GetInt()));
+    Commit(false);
+    style_name->SetFocus();
 }
 
-void DialogStyling::OnListDoubleClicked(wxCommandEvent &evt) {
-	style_name->ChangeValue(style_list->GetString(evt.GetInt()));
-	Commit(true);
-	style_name->SetFocus();
+void DialogStyling::OnListDoubleClicked(wxCommandEvent &evt)
+{
+    style_name->ChangeValue(style_list->GetString(evt.GetInt()));
+    Commit(true);
+    style_name->SetFocus();
 }
 
-void DialogStyling::OnPlayVideoButton(wxCommandEvent &) {
-	c->videoController->PlayLine();
-	style_name->SetFocus();
+void DialogStyling::OnPlayVideoButton(wxCommandEvent &)
+{
+    c->videoController->PlayLine();
+    style_name->SetFocus();
 }
 
-void DialogStyling::OnPlayAudioButton(wxCommandEvent &) {
-	cmd::call("audio/play/selection", c);
-	style_name->SetFocus();
+void DialogStyling::OnPlayAudioButton(wxCommandEvent &)
+{
+    cmd::call("audio/play/selection", c);
+    style_name->SetFocus();
 }
 
-void DialogStyling::OnCharHook(wxKeyEvent &evt) {
-	hotkey::check("Styling Assistant", c, evt);
+void DialogStyling::OnCharHook(wxKeyEvent &evt)
+{
+    hotkey::check("Styling Assistant", c, evt);
 }
 
-void DialogStyling::OnKeyDown(wxKeyEvent &evt) {
-	// Move the beginning of the selection back one character so that backspace
-	// actually does something
-	if (evt.GetKeyCode() == WXK_BACK && !evt.GetModifiers()) {
-		long from, to;
-		style_name->GetSelection(&from, &to);
-		if (from > 0)
-			style_name->SetSelection(from - 1, to);
-	}
-	else
-		evt.Skip();
+void DialogStyling::OnKeyDown(wxKeyEvent &evt)
+{
+    // Move the beginning of the selection back one character so that backspace
+    // actually does something
+    if (evt.GetKeyCode() == WXK_BACK && !evt.GetModifiers()) {
+        long from, to;
+        style_name->GetSelection(&from, &to);
+        if (from > 0)
+            style_name->SetSelection(from - 1, to);
+    }
+    else
+        evt.Skip();
 }
diff --git a/src/dialog_styling_assistant.h b/src/dialog_styling_assistant.h
index 1658bcf773f69a85347e6289d62c7f95836bda76..9d8c75d259c1871d99089b842fea188099559e7d 100644
--- a/src/dialog_styling_assistant.h
+++ b/src/dialog_styling_assistant.h
@@ -31,34 +31,34 @@ class wxTextCtrl;
 namespace agi { struct Context; }
 
 class DialogStyling final : public wxDialog {
-	agi::Context *c;
-	agi::signal::Connection active_line_connection;
+    agi::Context *c;
+    agi::signal::Connection active_line_connection;
 
-	wxButton *play_audio;
-	wxButton *play_video;
-	wxCheckBox *auto_seek;
-	wxListBox *style_list;
-	wxTextCtrl *current_line_text;
-	wxTextCtrl *style_name;
+    wxButton *play_audio;
+    wxButton *play_video;
+    wxCheckBox *auto_seek;
+    wxListBox *style_list;
+    wxTextCtrl *current_line_text;
+    wxTextCtrl *style_name;
 
-	void OnActivate(wxActivateEvent &evt);
-	void OnKeyDown(wxKeyEvent &evt);
-	void OnCharHook(wxKeyEvent &evt);
-	void OnListClicked(wxCommandEvent &evt);
-	void OnListDoubleClicked(wxCommandEvent &evt);
-	void OnPlayAudioButton(wxCommandEvent &evt);
-	void OnPlayVideoButton(wxCommandEvent &evt);
-	void OnStyleBoxModified(wxCommandEvent &evt);
+    void OnActivate(wxActivateEvent &evt);
+    void OnKeyDown(wxKeyEvent &evt);
+    void OnCharHook(wxKeyEvent &evt);
+    void OnListClicked(wxCommandEvent &evt);
+    void OnListDoubleClicked(wxCommandEvent &evt);
+    void OnPlayAudioButton(wxCommandEvent &evt);
+    void OnPlayVideoButton(wxCommandEvent &evt);
+    void OnStyleBoxModified(wxCommandEvent &evt);
 
-	void OnActiveLineChanged(AssDialogue *);
+    void OnActiveLineChanged(AssDialogue *);
 
-	AssDialogue *active_line = nullptr;
+    AssDialogue *active_line = nullptr;
 
-	std::unique_ptr<PersistLocation> persist;
+    std::unique_ptr<PersistLocation> persist;
 
 public:
-	void Commit(bool next);
+    void Commit(bool next);
 
-	DialogStyling(agi::Context *context);
-	~DialogStyling();
+    DialogStyling(agi::Context *context);
+    ~DialogStyling();
 };
diff --git a/src/dialog_text_import.cpp b/src/dialog_text_import.cpp
index 0da35b741ae41b216d581eb27b06274c667ba0ce..7dbb1875f10ec5028cbfa7796d2af34b1bbc1855 100644
--- a/src/dialog_text_import.cpp
+++ b/src/dialog_text_import.cpp
@@ -39,39 +39,40 @@
 
 /// A simple dialog to let the user select the format of a plain text file
 /// being imported into Aegisub
-bool ShowPlainTextImportDialog() {
-	auto seperator = OPT_GET("Tool/Import/Text/Actor Separator")->GetString();
-	auto comment = OPT_GET("Tool/Import/Text/Comment Starter")->GetString();
-	auto include_blank = OPT_GET("Tool/Import/Text/Include Blank")->GetBool();
+bool ShowPlainTextImportDialog()
+{
+    auto seperator = OPT_GET("Tool/Import/Text/Actor Separator")->GetString();
+    auto comment = OPT_GET("Tool/Import/Text/Comment Starter")->GetString();
+    auto include_blank = OPT_GET("Tool/Import/Text/Include Blank")->GetBool();
 
-	wxDialog d(nullptr, -1, _("Text import options"));
+    wxDialog d(nullptr, -1, _("Text import options"));
 
-	auto make_text_ctrl = [&](std::string *var) {
-		return new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxDefaultSize, 0, StringBinder(var));
-	};
+    auto make_text_ctrl = [&](std::string * var) {
+        return new wxTextCtrl(&d, -1, "", wxDefaultPosition, wxDefaultSize, 0, StringBinder(var));
+    };
 
-	auto fg = new wxFlexGridSizer(2, 5, 5);
-	fg->Add(new wxStaticText(&d, -1, _("Actor separator:")), 0, wxALIGN_CENTRE_VERTICAL);
-	fg->Add(make_text_ctrl(&seperator), 0, wxEXPAND);
-	fg->Add(new wxStaticText(&d, -1, _("Comment starter:")), 0, wxALIGN_CENTRE_VERTICAL);
-	fg->Add(make_text_ctrl(&comment), 0, wxEXPAND);
+    auto fg = new wxFlexGridSizer(2, 5, 5);
+    fg->Add(new wxStaticText(&d, -1, _("Actor separator:")), 0, wxALIGN_CENTRE_VERTICAL);
+    fg->Add(make_text_ctrl(&seperator), 0, wxEXPAND);
+    fg->Add(new wxStaticText(&d, -1, _("Comment starter:")), 0, wxALIGN_CENTRE_VERTICAL);
+    fg->Add(make_text_ctrl(&comment), 0, wxEXPAND);
 
-	auto main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(fg, 1, wxALL|wxEXPAND, 5);
-	main_sizer->Add(new wxCheckBox(&d, -1, _("Include blank lines"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&include_blank)), 0, wxLEFT|wxRIGHT|wxALIGN_RIGHT, 5);
-	main_sizer->Add(d.CreateSeparatedButtonSizer(wxOK|wxCANCEL), 0, wxALL|wxEXPAND, 5);
-	d.SetSizerAndFit(main_sizer);
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(fg, 1, wxALL | wxEXPAND, 5);
+    main_sizer->Add(new wxCheckBox(&d, -1, _("Include blank lines"), wxDefaultPosition, wxDefaultSize, 0, wxGenericValidator(&include_blank)), 0, wxLEFT | wxRIGHT | wxALIGN_RIGHT, 5);
+    main_sizer->Add(d.CreateSeparatedButtonSizer(wxOK | wxCANCEL), 0, wxALL | wxEXPAND, 5);
+    d.SetSizerAndFit(main_sizer);
 
-	d.Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
-		d.TransferDataFromWindow();
+    d.Bind(wxEVT_BUTTON, [&](wxCommandEvent &) {
+        d.TransferDataFromWindow();
 
-		OPT_SET("Tool/Import/Text/Actor Separator")->SetString(seperator);
-		OPT_SET("Tool/Import/Text/Comment Starter")->SetString(comment);
-		OPT_SET("Tool/Import/Text/Include Blank")->SetBool(include_blank);
+        OPT_SET("Tool/Import/Text/Actor Separator")->SetString(seperator);
+        OPT_SET("Tool/Import/Text/Comment Starter")->SetString(comment);
+        OPT_SET("Tool/Import/Text/Include Blank")->SetBool(include_blank);
 
-		d.EndModal(wxID_OK);
-	}, wxID_OK);
+        d.EndModal(wxID_OK);
+    }, wxID_OK);
 
 
-	return d.ShowModal() == wxID_OK;
+    return d.ShowModal() == wxID_OK;
 }
diff --git a/src/dialog_timing_processor.cpp b/src/dialog_timing_processor.cpp
index e0b7dc00b842fe1870b4dd1218e015e619bb3777..6c148e7e6b21121524cbde89503750059486c76f 100644
--- a/src/dialog_timing_processor.cpp
+++ b/src/dialog_timing_processor.cpp
@@ -67,385 +67,396 @@ namespace {
 /// @class DialogTimingProcessor
 /// @brief Automatic postprocessor for correcting common timing issues
 struct DialogTimingProcessor {
-	wxDialog d;
-	agi::Context *c; ///< Project context
-
-	int leadIn;      ///< Lead-in to add in milliseconds
-	int leadOut;     ///< Lead-out to add in milliseconds
-	int beforeStart; ///< Maximum time in milliseconds to move start time of line backwards to land on a keyframe
-	int afterStart;  ///< Maximum time in milliseconds to move start time of line forwards to land on a keyframe
-	int beforeEnd;   ///< Maximum time in milliseconds to move end time of line backwards to land on a keyframe
-	int afterEnd;    ///< Maximum time in milliseconds to move end time of line forwards to land on a keyframe
-	int adjGap;      ///< Maximum gap in milliseconds to snap adjacent lines to each other
-	int adjOverlap;  ///< Maximum overlap in milliseconds to snap adjacent lines to each other
-
-	wxCheckBox *onlySelection; ///< Only process selected lines of the selected styles
-	wxCheckBox *hasLeadIn;     ///< Enable adding lead-in
-	wxCheckBox *hasLeadOut;    ///< Enable adding lead-out
-	wxCheckBox *keysEnable;    ///< Enable snapping to keyframes
-	wxCheckBox *adjsEnable;    ///< Enable snapping adjacent lines to each other
-	wxSlider *adjacentBias;    ///< Bias between shifting start and end times when snapping adjacent lines
-	wxCheckListBox *StyleList; ///< List of styles to process
-	wxButton *ApplyButton;     ///< Button to apply the processing
-
-	void OnApply(wxCommandEvent &event);
-
-	/// Check or uncheck all styles
-	void CheckAll(bool check);
-
-	/// Enable and disable text boxes based on which checkboxes are checked
-	void UpdateControls();
-
-	/// Process the file
-	void Process();
-
-	/// Get a list of dialogue lines in the file sorted by start time
-	std::vector<AssDialogue*> SortDialogues();
-
-	DialogTimingProcessor(agi::Context *c);
+    wxDialog d;
+    agi::Context *c; ///< Project context
+
+    int leadIn;      ///< Lead-in to add in milliseconds
+    int leadOut;     ///< Lead-out to add in milliseconds
+    int beforeStart; ///< Maximum time in milliseconds to move start time of line backwards to land on a keyframe
+    int afterStart;  ///< Maximum time in milliseconds to move start time of line forwards to land on a keyframe
+    int beforeEnd;   ///< Maximum time in milliseconds to move end time of line backwards to land on a keyframe
+    int afterEnd;    ///< Maximum time in milliseconds to move end time of line forwards to land on a keyframe
+    int adjGap;      ///< Maximum gap in milliseconds to snap adjacent lines to each other
+    int adjOverlap;  ///< Maximum overlap in milliseconds to snap adjacent lines to each other
+
+    wxCheckBox *onlySelection; ///< Only process selected lines of the selected styles
+    wxCheckBox *hasLeadIn;     ///< Enable adding lead-in
+    wxCheckBox *hasLeadOut;    ///< Enable adding lead-out
+    wxCheckBox *keysEnable;    ///< Enable snapping to keyframes
+    wxCheckBox *adjsEnable;    ///< Enable snapping adjacent lines to each other
+    wxSlider *adjacentBias;    ///< Bias between shifting start and end times when snapping adjacent lines
+    wxCheckListBox *StyleList; ///< List of styles to process
+    wxButton *ApplyButton;     ///< Button to apply the processing
+
+    void OnApply(wxCommandEvent &event);
+
+    /// Check or uncheck all styles
+    void CheckAll(bool check);
+
+    /// Enable and disable text boxes based on which checkboxes are checked
+    void UpdateControls();
+
+    /// Process the file
+    void Process();
+
+    /// Get a list of dialogue lines in the file sorted by start time
+    std::vector<AssDialogue *> SortDialogues();
+
+    DialogTimingProcessor(agi::Context *c);
 };
 
-wxTextCtrl *make_ctrl(wxWindow *parent, wxSizer *sizer, wxString const& desc, int *value, wxCheckBox *cb, wxString const& tooltip) {
-	wxIntegerValidator<int> validator(value);
-	validator.SetMin(0);
-	wxTextCtrl *ctrl = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(60,-1), 0, validator);
-	ctrl->SetToolTip(tooltip);
-	if (!desc.empty())
-		sizer->Add(new wxStaticText(parent, -1, desc), wxSizerFlags().Center().Border(wxRIGHT));
-	sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
-
-	ctrl->Enable(cb->IsChecked());
-	cb->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent& evt) {
-		ctrl->Enable(cb->IsChecked());
-		evt.Skip();
-	});
-
-	return ctrl;
+wxTextCtrl *make_ctrl(wxWindow *parent, wxSizer *sizer, wxString const &desc, int *value, wxCheckBox *cb, wxString const &tooltip)
+{
+    wxIntegerValidator<int> validator(value);
+    validator.SetMin(0);
+    wxTextCtrl *ctrl = new wxTextCtrl(parent, -1, "", wxDefaultPosition, wxSize(60, -1), 0, validator);
+    ctrl->SetToolTip(tooltip);
+    if (!desc.empty())
+        sizer->Add(new wxStaticText(parent, -1, desc), wxSizerFlags().Center().Border(wxRIGHT));
+    sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
+
+    ctrl->Enable(cb->IsChecked());
+    cb->Bind(wxEVT_CHECKBOX, [ = ](wxCommandEvent & evt) {
+        ctrl->Enable(cb->IsChecked());
+        evt.Skip();
+    });
+
+    return ctrl;
 }
 
-inline wxTextCtrl *make_ctrl(wxStaticBoxSizer *sizer, wxString const& desc, int *value, wxCheckBox *cb, wxString const& tooltip) {
-	return make_ctrl(sizer->GetStaticBox()->GetParent(), sizer, desc, value, cb, tooltip);
+inline wxTextCtrl *make_ctrl(wxStaticBoxSizer *sizer, wxString const &desc, int *value, wxCheckBox *cb, wxString const &tooltip)
+{
+    return make_ctrl(sizer->GetStaticBox()->GetParent(), sizer, desc, value, cb, tooltip);
 }
 
-wxCheckBox *make_check(wxStaticBoxSizer *sizer, wxString const& desc, const char *opt, wxString const& tooltip) {
-	wxCheckBox *cb = new wxCheckBox(sizer->GetStaticBox()->GetParent(), -1, desc);
-	cb->SetToolTip(tooltip);
-	cb->SetValue(OPT_GET(opt)->GetBool());
-	sizer->Add(cb, wxSizerFlags().Border(wxRIGHT).Expand());
-	return cb;
+wxCheckBox *make_check(wxStaticBoxSizer *sizer, wxString const &desc, const char *opt, wxString const &tooltip)
+{
+    wxCheckBox *cb = new wxCheckBox(sizer->GetStaticBox()->GetParent(), -1, desc);
+    cb->SetToolTip(tooltip);
+    cb->SetValue(OPT_GET(opt)->GetBool());
+    sizer->Add(cb, wxSizerFlags().Border(wxRIGHT).Expand());
+    return cb;
 }
 
 DialogTimingProcessor::DialogTimingProcessor(agi::Context *c)
-: d(c->parent, -1, _("Timing Post-Processor"))
-, c(c)
+    : d(c->parent, -1, _("Timing Post-Processor"))
+    , c(c)
 {
-	using std::bind;
-
-	d.SetIcon(GETICON(timing_processor_toolbutton_16));
-
-	// Read options
-	leadIn = OPT_GET("Tool/Timing Post Processor/Lead/IN")->GetInt();
-	leadOut = OPT_GET("Tool/Timing Post Processor/Lead/OUT")->GetInt();
-	beforeStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start Before")->GetInt();
-	beforeEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End Before")->GetInt();
-	afterStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start After")->GetInt();
-	afterEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End After")->GetInt();
-	adjGap = OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent Gap")->GetInt();
-	adjOverlap = OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent Overlap")->GetInt();
-
-	// Styles box
-	auto LeftSizer = new wxStaticBoxSizer(wxVERTICAL,&d,_("Apply to styles"));
-	StyleList = new wxCheckListBox(&d, -1, wxDefaultPosition, wxSize(150,150), to_wx(c->ass->GetStyles()));
-	StyleList->SetToolTip(_("Select styles to process. Unchecked ones will be ignored."));
-
-	auto all = new wxButton(&d,-1,_("&All"));
-	all->SetToolTip(_("Select all styles"));
-
-	auto none = new wxButton(&d,-1,_("&None"));
-	none->SetToolTip(_("Deselect all styles"));
-
-	// Options box
-	auto optionsSizer = new wxStaticBoxSizer(wxHORIZONTAL,&d,_("Options"));
-	onlySelection = new wxCheckBox(&d,-1,_("Affect &selection only"));
-	onlySelection->SetValue(OPT_GET("Tool/Timing Post Processor/Only Selection")->GetBool());
-	optionsSizer->Add(onlySelection,1,wxALL,0);
-
-	// Lead-in/out box
-	auto LeadSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Lead-in/Lead-out"));
-
-	hasLeadIn = make_check(LeadSizer, _("Add lead &in:"),
-		"Tool/Timing Post Processor/Enable/Lead/IN",
-		_("Enable adding of lead-ins to lines"));
-	make_ctrl(LeadSizer, "", &leadIn, hasLeadIn, _("Lead in to be added, in milliseconds"));
-
-	hasLeadOut = make_check(LeadSizer, _("Add lead &out:"),
-		"Tool/Timing Post Processor/Enable/Lead/OUT",
-		_("Enable adding of lead-outs to lines"));
-	make_ctrl(LeadSizer, "", &leadOut, hasLeadOut, _("Lead out to be added, in milliseconds"));
-
-	LeadSizer->AddStretchSpacer(1);
-
-	// Adjacent subs sizer
-	auto AdjacentSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Make adjacent subtitles continuous"));
-	adjsEnable = make_check(AdjacentSizer, _("&Enable"),
-		"Tool/Timing Post Processor/Enable/Adjacent",
-		_("Enable snapping of subtitles together if they are within a certain distance of each other"));
-
-	auto adjBoxes = new wxBoxSizer(wxHORIZONTAL);
-	make_ctrl(&d, adjBoxes, _("Max gap:"), &adjGap, adjsEnable,
-		_("Maximum difference between start and end time for two subtitles to be made continuous, in milliseconds"));
-	make_ctrl(&d, adjBoxes, _("Max overlap:"), &adjOverlap, adjsEnable,
-		_("Maximum overlap between the end and start time for two subtitles to be made continuous, in milliseconds"));
-
-	adjacentBias = new wxSlider(&d, -1, mid<int>(0, OPT_GET("Tool/Timing Post Processor/Adjacent Bias")->GetDouble() * 100, 100), 0, 100, wxDefaultPosition, wxSize(-1,20));
-	adjacentBias->SetToolTip(_("Sets how to set the adjoining of lines. If set totally to left, it will extend or shrink start time of the second line; if totally to right, it will extend or shrink the end time of the first line."));
-
-	auto adjSliderSizer = new wxBoxSizer(wxHORIZONTAL);
-	adjSliderSizer->Add(new wxStaticText(&d, -1, _("Bias: Start <- ")), wxSizerFlags().Center());
-	adjSliderSizer->Add(adjacentBias, wxSizerFlags(1).Center());
-	adjSliderSizer->Add(new wxStaticText(&d, -1, _(" -> End")), wxSizerFlags().Center());
-
-	auto adjRightSizer = new wxBoxSizer(wxVERTICAL);
-	adjRightSizer->Add(adjBoxes, wxSizerFlags().Expand());
-	adjRightSizer->Add(adjSliderSizer, wxSizerFlags().Expand().Border(wxTOP));
-	AdjacentSizer->Add(adjRightSizer);
-
-	// Keyframes sizer
-	auto KeyframesSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Keyframe snapping"));
-	auto KeyframesFlexSizer = new wxFlexGridSizer(2,5,5,0);
-
-	keysEnable = new wxCheckBox(&d, -1, _("E&nable"));
-	keysEnable->SetToolTip(_("Enable snapping of subtitles to nearest keyframe, if distance is within threshold"));
-	keysEnable->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Keyframe")->GetBool());
-	KeyframesFlexSizer->Add(keysEnable,0,wxRIGHT|wxEXPAND,10);
-
-	// Keyframes are only available if timecodes are loaded
-	bool keysAvailable = !c->project->Keyframes().empty() && c->project->Timecodes().IsLoaded();
-	if (!keysAvailable) {
-		keysEnable->SetValue(false);
-		keysEnable->Enable(false);
-	}
-
-	make_ctrl(&d, KeyframesFlexSizer, _("Starts before thres.:"), &beforeStart, keysEnable,
-		_("Threshold for 'before start' distance, that is, how many milliseconds a subtitle must start before a keyframe to snap to it"));
-
-	make_ctrl(&d, KeyframesFlexSizer, _("Starts after thres.:"), &afterStart, keysEnable,
-		_("Threshold for 'after start' distance, that is, how many milliseconds a subtitle must start after a keyframe to snap to it"));
-
-	KeyframesFlexSizer->AddStretchSpacer(1);
-
-	make_ctrl(&d, KeyframesFlexSizer, _("Ends before thres.:"), &beforeEnd, keysEnable,
-		_("Threshold for 'before end' distance, that is, how many milliseconds a subtitle must end before a keyframe to snap to it"));
-
-	make_ctrl(&d, KeyframesFlexSizer, _("Ends after thres.:"), &afterEnd, keysEnable,
-		_("Threshold for 'after end' distance, that is, how many milliseconds a subtitle must end after a keyframe to snap to it"));
-
-	KeyframesSizer->Add(KeyframesFlexSizer,0,wxEXPAND);
-	KeyframesSizer->AddStretchSpacer(1);
-
-	// Button sizer
-	auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
-	ApplyButton = ButtonSizer->GetAffirmativeButton();
-	ButtonSizer->GetHelpButton()->Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Timing Processor"));
-
-	// Right Sizer
-	auto RightSizer = new wxBoxSizer(wxVERTICAL);
-	RightSizer->Add(optionsSizer,0,wxBOTTOM|wxEXPAND,5);
-	RightSizer->Add(LeadSizer,0,wxBOTTOM|wxEXPAND,5);
-	RightSizer->Add(AdjacentSizer,0,wxBOTTOM|wxEXPAND,5);
-	RightSizer->Add(KeyframesSizer,0,wxBOTTOM|wxEXPAND,5);
-	RightSizer->AddStretchSpacer(1);
-	RightSizer->Add(ButtonSizer,0,wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND,0);
-
-	// Style buttons sizer
-	auto StyleButtonsSizer = new wxBoxSizer(wxHORIZONTAL);
-	StyleButtonsSizer->Add(all,1,0,0);
-	StyleButtonsSizer->Add(none,1,0,0);
-
-	// Left sizer
-	LeftSizer->Add(StyleList, wxSizerFlags(1).Border(wxBOTTOM));
-	LeftSizer->Add(StyleButtonsSizer, wxSizerFlags().Expand());
-
-	// Top Sizer
-	auto TopSizer = new wxBoxSizer(wxHORIZONTAL);
-	TopSizer->Add(LeftSizer,0,wxRIGHT|wxEXPAND,5);
-	TopSizer->Add(RightSizer,1,wxALL|wxEXPAND,0);
-
-	// Main Sizer
-	auto MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(TopSizer,1,wxALL|wxEXPAND,5);
-	d.SetSizerAndFit(MainSizer);
-	d.CenterOnParent();
-
-	d.Bind(wxEVT_CHECKBOX, bind(&DialogTimingProcessor::UpdateControls, this));
-	d.Bind(wxEVT_CHECKLISTBOX, bind(&DialogTimingProcessor::UpdateControls, this));
-	d.Bind(wxEVT_BUTTON, &DialogTimingProcessor::OnApply, this, wxID_OK);
-	all->Bind(wxEVT_BUTTON, bind(&DialogTimingProcessor::CheckAll, this, true));
-	none->Bind(wxEVT_BUTTON, bind(&DialogTimingProcessor::CheckAll, this, false));
-
-	CheckAll(true);
+    using std::bind;
+
+    d.SetIcon(GETICON(timing_processor_toolbutton_16));
+
+    // Read options
+    leadIn = OPT_GET("Tool/Timing Post Processor/Lead/IN")->GetInt();
+    leadOut = OPT_GET("Tool/Timing Post Processor/Lead/OUT")->GetInt();
+    beforeStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start Before")->GetInt();
+    beforeEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End Before")->GetInt();
+    afterStart = OPT_GET("Tool/Timing Post Processor/Threshold/Key Start After")->GetInt();
+    afterEnd = OPT_GET("Tool/Timing Post Processor/Threshold/Key End After")->GetInt();
+    adjGap = OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent Gap")->GetInt();
+    adjOverlap = OPT_GET("Tool/Timing Post Processor/Threshold/Adjacent Overlap")->GetInt();
+
+    // Styles box
+    auto LeftSizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Apply to styles"));
+    StyleList = new wxCheckListBox(&d, -1, wxDefaultPosition, wxSize(150, 150), to_wx(c->ass->GetStyles()));
+    StyleList->SetToolTip(_("Select styles to process. Unchecked ones will be ignored."));
+
+    auto all = new wxButton(&d, -1, _("&All"));
+    all->SetToolTip(_("Select all styles"));
+
+    auto none = new wxButton(&d, -1, _("&None"));
+    none->SetToolTip(_("Deselect all styles"));
+
+    // Options box
+    auto optionsSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Options"));
+    onlySelection = new wxCheckBox(&d, -1, _("Affect &selection only"));
+    onlySelection->SetValue(OPT_GET("Tool/Timing Post Processor/Only Selection")->GetBool());
+    optionsSizer->Add(onlySelection, 1, wxALL, 0);
+
+    // Lead-in/out box
+    auto LeadSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Lead-in/Lead-out"));
+
+    hasLeadIn = make_check(LeadSizer, _("Add lead &in:"),
+                           "Tool/Timing Post Processor/Enable/Lead/IN",
+                           _("Enable adding of lead-ins to lines"));
+    make_ctrl(LeadSizer, "", &leadIn, hasLeadIn, _("Lead in to be added, in milliseconds"));
+
+    hasLeadOut = make_check(LeadSizer, _("Add lead &out:"),
+                            "Tool/Timing Post Processor/Enable/Lead/OUT",
+                            _("Enable adding of lead-outs to lines"));
+    make_ctrl(LeadSizer, "", &leadOut, hasLeadOut, _("Lead out to be added, in milliseconds"));
+
+    LeadSizer->AddStretchSpacer(1);
+
+    // Adjacent subs sizer
+    auto AdjacentSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Make adjacent subtitles continuous"));
+    adjsEnable = make_check(AdjacentSizer, _("&Enable"),
+                            "Tool/Timing Post Processor/Enable/Adjacent",
+                            _("Enable snapping of subtitles together if they are within a certain distance of each other"));
+
+    auto adjBoxes = new wxBoxSizer(wxHORIZONTAL);
+    make_ctrl(&d, adjBoxes, _("Max gap:"), &adjGap, adjsEnable,
+              _("Maximum difference between start and end time for two subtitles to be made continuous, in milliseconds"));
+    make_ctrl(&d, adjBoxes, _("Max overlap:"), &adjOverlap, adjsEnable,
+              _("Maximum overlap between the end and start time for two subtitles to be made continuous, in milliseconds"));
+
+    adjacentBias = new wxSlider(&d, -1, mid<int>(0, OPT_GET("Tool/Timing Post Processor/Adjacent Bias")->GetDouble() * 100, 100), 0, 100, wxDefaultPosition, wxSize(-1, 20));
+    adjacentBias->SetToolTip(_("Sets how to set the adjoining of lines. If set totally to left, it will extend or shrink start time of the second line; if totally to right, it will extend or shrink the end time of the first line."));
+
+    auto adjSliderSizer = new wxBoxSizer(wxHORIZONTAL);
+    adjSliderSizer->Add(new wxStaticText(&d, -1, _("Bias: Start <- ")), wxSizerFlags().Center());
+    adjSliderSizer->Add(adjacentBias, wxSizerFlags(1).Center());
+    adjSliderSizer->Add(new wxStaticText(&d, -1, _(" -> End")), wxSizerFlags().Center());
+
+    auto adjRightSizer = new wxBoxSizer(wxVERTICAL);
+    adjRightSizer->Add(adjBoxes, wxSizerFlags().Expand());
+    adjRightSizer->Add(adjSliderSizer, wxSizerFlags().Expand().Border(wxTOP));
+    AdjacentSizer->Add(adjRightSizer);
+
+    // Keyframes sizer
+    auto KeyframesSizer = new wxStaticBoxSizer(wxHORIZONTAL, &d, _("Keyframe snapping"));
+    auto KeyframesFlexSizer = new wxFlexGridSizer(2, 5, 5, 0);
+
+    keysEnable = new wxCheckBox(&d, -1, _("E&nable"));
+    keysEnable->SetToolTip(_("Enable snapping of subtitles to nearest keyframe, if distance is within threshold"));
+    keysEnable->SetValue(OPT_GET("Tool/Timing Post Processor/Enable/Keyframe")->GetBool());
+    KeyframesFlexSizer->Add(keysEnable, 0, wxRIGHT | wxEXPAND, 10);
+
+    // Keyframes are only available if timecodes are loaded
+    bool keysAvailable = !c->project->Keyframes().empty() && c->project->Timecodes().IsLoaded();
+    if (!keysAvailable) {
+        keysEnable->SetValue(false);
+        keysEnable->Enable(false);
+    }
+
+    make_ctrl(&d, KeyframesFlexSizer, _("Starts before thres.:"), &beforeStart, keysEnable,
+              _("Threshold for 'before start' distance, that is, how many milliseconds a subtitle must start before a keyframe to snap to it"));
+
+    make_ctrl(&d, KeyframesFlexSizer, _("Starts after thres.:"), &afterStart, keysEnable,
+              _("Threshold for 'after start' distance, that is, how many milliseconds a subtitle must start after a keyframe to snap to it"));
+
+    KeyframesFlexSizer->AddStretchSpacer(1);
+
+    make_ctrl(&d, KeyframesFlexSizer, _("Ends before thres.:"), &beforeEnd, keysEnable,
+              _("Threshold for 'before end' distance, that is, how many milliseconds a subtitle must end before a keyframe to snap to it"));
+
+    make_ctrl(&d, KeyframesFlexSizer, _("Ends after thres.:"), &afterEnd, keysEnable,
+              _("Threshold for 'after end' distance, that is, how many milliseconds a subtitle must end after a keyframe to snap to it"));
+
+    KeyframesSizer->Add(KeyframesFlexSizer, 0, wxEXPAND);
+    KeyframesSizer->AddStretchSpacer(1);
+
+    // Button sizer
+    auto ButtonSizer = d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP);
+    ApplyButton = ButtonSizer->GetAffirmativeButton();
+    ButtonSizer->GetHelpButton()->Bind(wxEVT_BUTTON, bind(&HelpButton::OpenPage, "Timing Processor"));
+
+    // Right Sizer
+    auto RightSizer = new wxBoxSizer(wxVERTICAL);
+    RightSizer->Add(optionsSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    RightSizer->Add(LeadSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    RightSizer->Add(AdjacentSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    RightSizer->Add(KeyframesSizer, 0, wxBOTTOM | wxEXPAND, 5);
+    RightSizer->AddStretchSpacer(1);
+    RightSizer->Add(ButtonSizer, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 0);
+
+    // Style buttons sizer
+    auto StyleButtonsSizer = new wxBoxSizer(wxHORIZONTAL);
+    StyleButtonsSizer->Add(all, 1, 0, 0);
+    StyleButtonsSizer->Add(none, 1, 0, 0);
+
+    // Left sizer
+    LeftSizer->Add(StyleList, wxSizerFlags(1).Border(wxBOTTOM));
+    LeftSizer->Add(StyleButtonsSizer, wxSizerFlags().Expand());
+
+    // Top Sizer
+    auto TopSizer = new wxBoxSizer(wxHORIZONTAL);
+    TopSizer->Add(LeftSizer, 0, wxRIGHT | wxEXPAND, 5);
+    TopSizer->Add(RightSizer, 1, wxALL | wxEXPAND, 0);
+
+    // Main Sizer
+    auto MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(TopSizer, 1, wxALL | wxEXPAND, 5);
+    d.SetSizerAndFit(MainSizer);
+    d.CenterOnParent();
+
+    d.Bind(wxEVT_CHECKBOX, bind(&DialogTimingProcessor::UpdateControls, this));
+    d.Bind(wxEVT_CHECKLISTBOX, bind(&DialogTimingProcessor::UpdateControls, this));
+    d.Bind(wxEVT_BUTTON, &DialogTimingProcessor::OnApply, this, wxID_OK);
+    all->Bind(wxEVT_BUTTON, bind(&DialogTimingProcessor::CheckAll, this, true));
+    none->Bind(wxEVT_BUTTON, bind(&DialogTimingProcessor::CheckAll, this, false));
+
+    CheckAll(true);
 }
 
-void DialogTimingProcessor::CheckAll(bool value) {
-	size_t count = StyleList->GetCount();
-	for (size_t i = 0; i < count; ++i)
-		StyleList->Check(i, value);
-	UpdateControls();
+void DialogTimingProcessor::CheckAll(bool value)
+{
+    size_t count = StyleList->GetCount();
+    for (size_t i = 0; i < count; ++i)
+        StyleList->Check(i, value);
+    UpdateControls();
 }
 
-void DialogTimingProcessor::UpdateControls() {
-	// Only enable the OK button if it'll actually do something
-	bool any_checked = false;
-	size_t len = StyleList->GetCount();
-	for (size_t i = 0; !any_checked && i < len; ++i)
-		any_checked = StyleList->IsChecked(i);
-	ApplyButton->Enable(any_checked && (hasLeadIn->IsChecked() || hasLeadOut->IsChecked() || keysEnable->IsChecked() || adjsEnable->IsChecked()));
+void DialogTimingProcessor::UpdateControls()
+{
+    // Only enable the OK button if it'll actually do something
+    bool any_checked = false;
+    size_t len = StyleList->GetCount();
+    for (size_t i = 0; !any_checked && i < len; ++i)
+        any_checked = StyleList->IsChecked(i);
+    ApplyButton->Enable(any_checked && (hasLeadIn->IsChecked() || hasLeadOut->IsChecked() || keysEnable->IsChecked() || adjsEnable->IsChecked()));
 }
 
-void DialogTimingProcessor::OnApply(wxCommandEvent &) {
-	d.TransferDataFromWindow();
-	// Save settings
-	OPT_SET("Tool/Timing Post Processor/Lead/IN")->SetInt(leadIn);
-	OPT_SET("Tool/Timing Post Processor/Lead/OUT")->SetInt(leadOut);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Key Start Before")->SetInt(beforeStart);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Key Start After")->SetInt(afterStart);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Key End Before")->SetInt(beforeEnd);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Key End After")->SetInt(afterEnd);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent Gap")->SetInt(adjGap);
-	OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent Overlap")->SetInt(adjOverlap);
-	OPT_SET("Tool/Timing Post Processor/Adjacent Bias")->SetDouble(adjacentBias->GetValue() / 100.0);
-	OPT_SET("Tool/Timing Post Processor/Enable/Lead/IN")->SetBool(hasLeadIn->IsChecked());
-	OPT_SET("Tool/Timing Post Processor/Enable/Lead/OUT")->SetBool(hasLeadOut->IsChecked());
-	if (keysEnable->IsEnabled()) OPT_SET("Tool/Timing Post Processor/Enable/Keyframe")->SetBool(keysEnable->IsChecked());
-	OPT_SET("Tool/Timing Post Processor/Enable/Adjacent")->SetBool(adjsEnable->IsChecked());
-	OPT_SET("Tool/Timing Post Processor/Only Selection")->SetBool(onlySelection->IsChecked());
-
-	Process();
-	d.EndModal(0);
+void DialogTimingProcessor::OnApply(wxCommandEvent &)
+{
+    d.TransferDataFromWindow();
+    // Save settings
+    OPT_SET("Tool/Timing Post Processor/Lead/IN")->SetInt(leadIn);
+    OPT_SET("Tool/Timing Post Processor/Lead/OUT")->SetInt(leadOut);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Key Start Before")->SetInt(beforeStart);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Key Start After")->SetInt(afterStart);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Key End Before")->SetInt(beforeEnd);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Key End After")->SetInt(afterEnd);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent Gap")->SetInt(adjGap);
+    OPT_SET("Tool/Timing Post Processor/Threshold/Adjacent Overlap")->SetInt(adjOverlap);
+    OPT_SET("Tool/Timing Post Processor/Adjacent Bias")->SetDouble(adjacentBias->GetValue() / 100.0);
+    OPT_SET("Tool/Timing Post Processor/Enable/Lead/IN")->SetBool(hasLeadIn->IsChecked());
+    OPT_SET("Tool/Timing Post Processor/Enable/Lead/OUT")->SetBool(hasLeadOut->IsChecked());
+    if (keysEnable->IsEnabled()) OPT_SET("Tool/Timing Post Processor/Enable/Keyframe")->SetBool(keysEnable->IsChecked());
+    OPT_SET("Tool/Timing Post Processor/Enable/Adjacent")->SetBool(adjsEnable->IsChecked());
+    OPT_SET("Tool/Timing Post Processor/Only Selection")->SetBool(onlySelection->IsChecked());
+
+    Process();
+    d.EndModal(0);
 }
 
-std::vector<AssDialogue*> DialogTimingProcessor::SortDialogues() {
-	std::set<boost::flyweight<std::string>> styles;
-	for (size_t i = 0; i < StyleList->GetCount(); ++i) {
-		if (StyleList->IsChecked(i))
-			styles.insert(boost::flyweight<std::string>(from_wx(StyleList->GetString(i))));
-	}
-
-	std::vector<AssDialogue*> sorted;
-
-	auto valid_line = [&](const AssDialogue *d) { return !d->Comment && styles.count(d->Style); };
-	if (onlySelection->IsChecked())
-		boost::copy(c->selectionController->GetSelectedSet() | filtered(valid_line),
-		    back_inserter(sorted));
-	else {
-		sorted.reserve(c->ass->Events.size());
-		boost::push_back(sorted, c->ass->Events | agi::address_of | filtered(valid_line));
-	}
-
-	// Check if rows are valid
-	for (auto diag : sorted) {
-		if (diag->Start > diag->End) {
-			wxMessageBox(
-				fmt_tl("One of the lines in the file (%i) has negative duration. Aborting.", diag->Row),
-				_("Invalid script"),
-				wxOK | wxICON_ERROR | wxCENTER);
-			sorted.clear();
-			break;
-		}
-	}
-
-	boost::sort(sorted, [](const AssDialogue *a, const AssDialogue *b) {
-		return a->Start < b->Start;
-	});
-	return sorted;
+std::vector<AssDialogue *> DialogTimingProcessor::SortDialogues()
+{
+    std::set<boost::flyweight<std::string>> styles;
+    for (size_t i = 0; i < StyleList->GetCount(); ++i) {
+        if (StyleList->IsChecked(i))
+            styles.insert(boost::flyweight<std::string>(from_wx(StyleList->GetString(i))));
+    }
+
+    std::vector<AssDialogue *> sorted;
+
+    auto valid_line = [&](const AssDialogue * d) { return !d->Comment && styles.count(d->Style); };
+    if (onlySelection->IsChecked())
+        boost::copy(c->selectionController->GetSelectedSet() | filtered(valid_line),
+                    back_inserter(sorted));
+    else {
+        sorted.reserve(c->ass->Events.size());
+        boost::push_back(sorted, c->ass->Events | agi::address_of | filtered(valid_line));
+    }
+
+    // Check if rows are valid
+    for (auto diag : sorted) {
+        if (diag->Start > diag->End) {
+            wxMessageBox(
+                fmt_tl("One of the lines in the file (%i) has negative duration. Aborting.", diag->Row),
+                _("Invalid script"),
+                wxOK | wxICON_ERROR | wxCENTER);
+            sorted.clear();
+            break;
+        }
+    }
+
+    boost::sort(sorted, [](const AssDialogue * a, const AssDialogue * b) {
+        return a->Start < b->Start;
+    });
+    return sorted;
 }
 
-static int get_closest_kf(std::vector<int> const& kf, int frame) {
-	const auto pos = boost::upper_bound(kf, frame);
-	// Return last keyframe if this is after the last one
-	if (pos == end(kf)) return kf.back();
-	// *pos is greater than frame, and *(pos - 1) is less than or equal to frame
-	return (pos == begin(kf) || *pos - frame < frame - *(pos - 1)) ? *pos : *(pos - 1);
+static int get_closest_kf(std::vector<int> const &kf, int frame)
+{
+    const auto pos = boost::upper_bound(kf, frame);
+    // Return last keyframe if this is after the last one
+    if (pos == end(kf)) return kf.back();
+    // *pos is greater than frame, and *(pos - 1) is less than or equal to frame
+    return (pos == begin(kf) || *pos - frame < frame - * (pos - 1)) ? *pos : *(pos - 1);
 }
 
 template<class Iter, class Field>
-static int safe_time(Iter begin, Iter end, AssDialogue *comp, int initial, Field field, int const& (*cmp)(int const&, int const&)) {
-	// Compare to every previous line (yay for O(n^2)!) to see if it's OK to add lead-in
-	for (; begin != end; ++begin) {
-		// If the line doesn't already collide with this line, extend it only
-		// to the edge of the line
-		if (!comp->CollidesWith(*begin))
-			initial = cmp(initial, (*begin)->*field);
-	}
-	return initial;
+static int safe_time(Iter begin, Iter end, AssDialogue *comp, int initial, Field field, int const & (*cmp)(int const &, int const &))
+{
+    // Compare to every previous line (yay for O(n^2)!) to see if it's OK to add lead-in
+    for (; begin != end; ++begin) {
+        // If the line doesn't already collide with this line, extend it only
+        // to the edge of the line
+        if (!comp->CollidesWith(*begin))
+            initial = cmp(initial, (*begin)->*field);
+    }
+    return initial;
 }
 
-void DialogTimingProcessor::Process() {
-	std::vector<AssDialogue*> sorted = SortDialogues();
-	if (sorted.empty()) return;
-
-	// Add lead-in/out
-	if (hasLeadIn->IsChecked() && leadIn) {
-		for (size_t i = 0; i < sorted.size(); ++i)
-			sorted[i]->Start = safe_time(sorted.rend() - i, sorted.rend(),
-				sorted[i], sorted[i]->Start - leadIn,
-				&AssDialogue::End, &std::max<int>);
-	}
-
-	if (hasLeadOut->IsChecked() && leadOut) {
-		for (size_t i = 0; i < sorted.size(); ++i)
-			sorted[i]->End = safe_time(sorted.begin() + i + 1, sorted.end(),
-				sorted[i], sorted[i]->End + leadOut,
-				&AssDialogue::Start, &std::min<int>);
-	}
-
-	// Make adjacent
-	if (adjsEnable->IsChecked()) {
-		double bias = adjacentBias->GetValue() / 100.0;
-
-		for (size_t i = 1; i < sorted.size(); ++i) {
-			AssDialogue *prev = sorted[i - 1];
-			AssDialogue *cur = sorted[i];
-
-			int dist = cur->Start - prev->End;
-			if ((dist < 0 && -dist <= adjOverlap) || (dist > 0 && dist <= adjGap)) {
-				int setPos = prev->End + int(dist * bias);
-				cur->Start = setPos;
-				prev->End = setPos;
-			}
-		}
-	}
-
-	// Keyframe snapping
-	if (keysEnable->IsChecked()) {
-		std::vector<int> kf = c->project->Keyframes();
-		auto fps = c->project->Timecodes();
-		if (auto provider = c->project->VideoProvider())
-			kf.push_back(provider->GetFrameCount() - 1);
-
-		for (AssDialogue *cur : sorted) {
-			// Get start/end frames
-			int startF = fps.FrameAtTime(cur->Start, agi::vfr::START);
-			int endF = fps.FrameAtTime(cur->End, agi::vfr::END);
-
-			// Get closest for start
-			int closest = get_closest_kf(kf, startF);
-			int time = fps.TimeAtFrame(closest, agi::vfr::START);
-			if ((closest > startF && time - cur->Start <= beforeStart) || (closest < startF && cur->Start - time <= afterStart))
-				cur->Start = time;
-
-			// Get closest for end
-			closest = get_closest_kf(kf, endF) - 1;
-			time = fps.TimeAtFrame(closest, agi::vfr::END);
-			if ((closest > endF && time - cur->End <= beforeEnd) || (closest < endF && cur->End - time <= afterEnd))
-				cur->End = time;
-		}
-	}
-
-	c->ass->Commit(_("timing processor"), AssFile::COMMIT_DIAG_TIME);
+void DialogTimingProcessor::Process()
+{
+    std::vector<AssDialogue *> sorted = SortDialogues();
+    if (sorted.empty()) return;
+
+    // Add lead-in/out
+    if (hasLeadIn->IsChecked() && leadIn) {
+        for (size_t i = 0; i < sorted.size(); ++i)
+            sorted[i]->Start = safe_time(sorted.rend() - i, sorted.rend(),
+                                         sorted[i], sorted[i]->Start - leadIn,
+                                         &AssDialogue::End, &std::max<int>);
+    }
+
+    if (hasLeadOut->IsChecked() && leadOut) {
+        for (size_t i = 0; i < sorted.size(); ++i)
+            sorted[i]->End = safe_time(sorted.begin() + i + 1, sorted.end(),
+                                       sorted[i], sorted[i]->End + leadOut,
+                                       &AssDialogue::Start, &std::min<int>);
+    }
+
+    // Make adjacent
+    if (adjsEnable->IsChecked()) {
+        double bias = adjacentBias->GetValue() / 100.0;
+
+        for (size_t i = 1; i < sorted.size(); ++i) {
+            AssDialogue *prev = sorted[i - 1];
+            AssDialogue *cur = sorted[i];
+
+            int dist = cur->Start - prev->End;
+            if ((dist < 0 && -dist <= adjOverlap) || (dist > 0 && dist <= adjGap)) {
+                int setPos = prev->End + int(dist * bias);
+                cur->Start = setPos;
+                prev->End = setPos;
+            }
+        }
+    }
+
+    // Keyframe snapping
+    if (keysEnable->IsChecked()) {
+        std::vector<int> kf = c->project->Keyframes();
+        auto fps = c->project->Timecodes();
+        if (auto provider = c->project->VideoProvider())
+            kf.push_back(provider->GetFrameCount() - 1);
+
+        for (AssDialogue *cur : sorted) {
+            // Get start/end frames
+            int startF = fps.FrameAtTime(cur->Start, agi::vfr::START);
+            int endF = fps.FrameAtTime(cur->End, agi::vfr::END);
+
+            // Get closest for start
+            int closest = get_closest_kf(kf, startF);
+            int time = fps.TimeAtFrame(closest, agi::vfr::START);
+            if ((closest > startF && time - cur->Start <= beforeStart) || (closest < startF && cur->Start - time <= afterStart))
+                cur->Start = time;
+
+            // Get closest for end
+            closest = get_closest_kf(kf, endF) - 1;
+            time = fps.TimeAtFrame(closest, agi::vfr::END);
+            if ((closest > endF && time - cur->End <= beforeEnd) || (closest < endF && cur->End - time <= afterEnd))
+                cur->End = time;
+        }
+    }
+
+    c->ass->Commit(_("timing processor"), AssFile::COMMIT_DIAG_TIME);
 }
 }
 
-void ShowTimingProcessorDialog(agi::Context *c) {
-	DialogTimingProcessor(c).d.ShowModal();
+void ShowTimingProcessorDialog(agi::Context *c)
+{
+    DialogTimingProcessor(c).d.ShowModal();
 }
diff --git a/src/dialog_translation.cpp b/src/dialog_translation.cpp
index c953b68749b8b6c07532f4ba126eb7d79c8cb95e..a9c152a36418b5583fdbd8dcc66e728906ca1687 100644
--- a/src/dialog_translation.cpp
+++ b/src/dialog_translation.cpp
@@ -51,256 +51,268 @@
 #include <wx/stattext.h>
 #include <wx/stc/stc.h>
 
-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, to_wx(hotkey::get_hotkey_str_first("Translation Assistant", command))));
+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, to_wx(hotkey::get_hotkey_str_first("Translation Assistant", command))));
 }
 
 // Skip over override blocks, comments, and whitespace between blocks
-static bool bad_block(std::unique_ptr<AssDialogueBlock> &block) {
-	bool is_whitespace = boost::all(block->GetText(), boost::is_space());
-	return block->GetType() != AssBlockType::PLAIN || (is_whitespace && OPT_GET("Tool/Translation Assistant/Skip Whitespace")->GetBool());
+static bool bad_block(std::unique_ptr<AssDialogueBlock> &block)
+{
+    bool is_whitespace = boost::all(block->GetText(), boost::is_space());
+    return block->GetType() != AssBlockType::PLAIN || (is_whitespace && OPT_GET("Tool/Translation Assistant/Skip Whitespace")->GetBool());
 }
 
 DialogTranslation::DialogTranslation(agi::Context *c)
-: wxDialog(c->parent, -1, _("Translation Assistant"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX)
-, c(c)
-, file_change_connection(c->ass->AddCommitListener(&DialogTranslation::OnExternalCommit, this))
-, active_line_connection(c->selectionController->AddActiveLineListener(&DialogTranslation::OnActiveLineChanged, this))
-, active_line(c->selectionController->GetActiveLine())
-, line_count(c->ass->Events.size())
+    : wxDialog(c->parent, -1, _("Translation Assistant"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX)
+    , c(c)
+    , file_change_connection(c->ass->AddCommitListener(&DialogTranslation::OnExternalCommit, this))
+    , active_line_connection(c->selectionController->AddActiveLineListener(&DialogTranslation::OnActiveLineChanged, this))
+    , active_line(c->selectionController->GetActiveLine())
+    , line_count(c->ass->Events.size())
 {
-	SetIcon(GETICON(translation_toolbutton_16));
-
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-
-	wxSizer *translation_sizer = new wxBoxSizer(wxVERTICAL);
-	{
-		wxSizer *original_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Original"));
-
-		line_number_display = new wxStaticText(this, -1, "");
-		original_box->Add(line_number_display, 0, wxBOTTOM, 5);
-
-		original_text = new wxStyledTextCtrl(this, -1, wxDefaultPosition, wxSize(320, 80));
-		original_text->SetWrapMode(wxSTC_WRAP_WORD);
-		original_text->SetMarginWidth(1, 0);
-		original_text->StyleSetForeground(1, wxColour(10, 60, 200));
-		original_text->SetReadOnly(true);
-		original_box->Add(original_text, 1, wxEXPAND, 0);
-
-		translation_sizer->Add(original_box, 1, wxEXPAND, 0);
-	}
-
-	{
-		translated_text = new SubsTextEditCtrl(this, wxSize(320, 80), 0, nullptr);
-		translated_text->SetWrapMode(wxSTC_WRAP_WORD);
-		translated_text->SetMarginWidth(1, 0);
-		translated_text->SetFocus();
-		translated_text->Bind(wxEVT_CHAR_HOOK, &DialogTranslation::OnKeyDown, this);
-		translated_text->CmdKeyAssign(wxSTC_KEY_RETURN, wxSTC_SCMOD_SHIFT, wxSTC_CMD_NEWLINE);
-
-		wxSizer *translated_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Translation"));
-		translated_box->Add(translated_text, 1, wxEXPAND, 0);
-		translation_sizer->Add(translated_box, 1, wxTOP|wxEXPAND, 5);
-	}
-	main_sizer->Add(translation_sizer, 1, wxALL | wxEXPAND, 5);
-
-	wxSizer *right_box = new wxBoxSizer(wxHORIZONTAL);
-	{
-		wxSizer *hotkey_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Keys"));
-
-		wxSizer *hotkey_grid = new wxGridSizer(2, 0, 5);
-		add_hotkey(hotkey_grid, this, "tool/translation_assistant/commit", _("Accept changes"));
-		add_hotkey(hotkey_grid, this, "tool/translation_assistant/preview", _("Preview changes"));
-		add_hotkey(hotkey_grid, this, "tool/translation_assistant/prev", _("Previous line"));
-		add_hotkey(hotkey_grid, this, "tool/translation_assistant/next", _("Next line"));
-		add_hotkey(hotkey_grid, this, "tool/translation_assistant/insert_original", _("Insert original"));
-		add_hotkey(hotkey_grid, this, "video/play/line", _("Play video"));
-		add_hotkey(hotkey_grid, this, "audio/play/selection", _("Play audio"));
-		add_hotkey(hotkey_grid, this, "edit/line/delete", _("Delete line"));
-		hotkey_box->Add(hotkey_grid, 0, wxEXPAND, 0);
-
-		seek_video = new wxCheckBox(this, -1, _("Enable &preview"));
-		seek_video->SetValue(true);
-		hotkey_box->Add(seek_video, 0, wxTOP, 5);
-
-		right_box->Add(hotkey_box, 1, wxRIGHT | wxEXPAND, 5);
-	}
-
-	{
-		wxStaticBoxSizer *actions_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Actions"));
-
-		wxButton *play_audio = new wxButton(this, -1, _("Play &Audio"));
-		play_audio->Enable(!!c->project->AudioProvider());
-		play_audio->Bind(wxEVT_BUTTON, &DialogTranslation::OnPlayAudioButton, this);
-		actions_box->Add(play_audio, 0, wxALL, 5);
-
-		wxButton *play_video = new wxButton(this, -1, _("Play &Video"));
-		play_video->Enable(!!c->project->VideoProvider());
-		play_video->Bind(wxEVT_BUTTON, &DialogTranslation::OnPlayVideoButton, this);
-		actions_box->Add(play_video, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5);
-
-		right_box->Add(actions_box, 0);
-	}
-	main_sizer->Add(right_box, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
-
-	{
-		auto standard_buttons = new wxStdDialogButtonSizer();
-		standard_buttons->AddButton(new wxButton(this, wxID_CANCEL));
-		standard_buttons->AddButton(new HelpButton(this, "Translation Assistant"));
-		standard_buttons->Realize();
-		main_sizer->Add(standard_buttons, 0, wxALIGN_RIGHT | wxLEFT | wxBOTTOM | wxRIGHT, 5);
-	}
-
-	SetSizerAndFit(main_sizer);
-
-	persist = agi::make_unique<PersistLocation>(this, "Tool/Translation Assistant");
-
-	Bind(wxEVT_KEY_DOWN, &DialogTranslation::OnKeyDown, this);
-
-	blocks = active_line->ParseTags();
-	if (bad_block(blocks[0])) {
-		if (!NextBlock())
-			throw NothingToTranslate();
-	}
-	else
-		UpdateDisplay();
+    SetIcon(GETICON(translation_toolbutton_16));
+
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+
+    wxSizer *translation_sizer = new wxBoxSizer(wxVERTICAL);
+    {
+        wxSizer *original_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Original"));
+
+        line_number_display = new wxStaticText(this, -1, "");
+        original_box->Add(line_number_display, 0, wxBOTTOM, 5);
+
+        original_text = new wxStyledTextCtrl(this, -1, wxDefaultPosition, wxSize(320, 80));
+        original_text->SetWrapMode(wxSTC_WRAP_WORD);
+        original_text->SetMarginWidth(1, 0);
+        original_text->StyleSetForeground(1, wxColour(10, 60, 200));
+        original_text->SetReadOnly(true);
+        original_box->Add(original_text, 1, wxEXPAND, 0);
+
+        translation_sizer->Add(original_box, 1, wxEXPAND, 0);
+    }
+
+    {
+        translated_text = new SubsTextEditCtrl(this, wxSize(320, 80), 0, nullptr);
+        translated_text->SetWrapMode(wxSTC_WRAP_WORD);
+        translated_text->SetMarginWidth(1, 0);
+        translated_text->SetFocus();
+        translated_text->Bind(wxEVT_CHAR_HOOK, &DialogTranslation::OnKeyDown, this);
+        translated_text->CmdKeyAssign(wxSTC_KEY_RETURN, wxSTC_SCMOD_SHIFT, wxSTC_CMD_NEWLINE);
+
+        wxSizer *translated_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Translation"));
+        translated_box->Add(translated_text, 1, wxEXPAND, 0);
+        translation_sizer->Add(translated_box, 1, wxTOP | wxEXPAND, 5);
+    }
+    main_sizer->Add(translation_sizer, 1, wxALL | wxEXPAND, 5);
+
+    wxSizer *right_box = new wxBoxSizer(wxHORIZONTAL);
+    {
+        wxSizer *hotkey_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Keys"));
+
+        wxSizer *hotkey_grid = new wxGridSizer(2, 0, 5);
+        add_hotkey(hotkey_grid, this, "tool/translation_assistant/commit", _("Accept changes"));
+        add_hotkey(hotkey_grid, this, "tool/translation_assistant/preview", _("Preview changes"));
+        add_hotkey(hotkey_grid, this, "tool/translation_assistant/prev", _("Previous line"));
+        add_hotkey(hotkey_grid, this, "tool/translation_assistant/next", _("Next line"));
+        add_hotkey(hotkey_grid, this, "tool/translation_assistant/insert_original", _("Insert original"));
+        add_hotkey(hotkey_grid, this, "video/play/line", _("Play video"));
+        add_hotkey(hotkey_grid, this, "audio/play/selection", _("Play audio"));
+        add_hotkey(hotkey_grid, this, "edit/line/delete", _("Delete line"));
+        hotkey_box->Add(hotkey_grid, 0, wxEXPAND, 0);
+
+        seek_video = new wxCheckBox(this, -1, _("Enable &preview"));
+        seek_video->SetValue(true);
+        hotkey_box->Add(seek_video, 0, wxTOP, 5);
+
+        right_box->Add(hotkey_box, 1, wxRIGHT | wxEXPAND, 5);
+    }
+
+    {
+        wxStaticBoxSizer *actions_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Actions"));
+
+        wxButton *play_audio = new wxButton(this, -1, _("Play &Audio"));
+        play_audio->Enable(!!c->project->AudioProvider());
+        play_audio->Bind(wxEVT_BUTTON, &DialogTranslation::OnPlayAudioButton, this);
+        actions_box->Add(play_audio, 0, wxALL, 5);
+
+        wxButton *play_video = new wxButton(this, -1, _("Play &Video"));
+        play_video->Enable(!!c->project->VideoProvider());
+        play_video->Bind(wxEVT_BUTTON, &DialogTranslation::OnPlayVideoButton, this);
+        actions_box->Add(play_video, 0, wxLEFT | wxRIGHT | wxBOTTOM, 5);
+
+        right_box->Add(actions_box, 0);
+    }
+    main_sizer->Add(right_box, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);
+
+    {
+        auto standard_buttons = new wxStdDialogButtonSizer();
+        standard_buttons->AddButton(new wxButton(this, wxID_CANCEL));
+        standard_buttons->AddButton(new HelpButton(this, "Translation Assistant"));
+        standard_buttons->Realize();
+        main_sizer->Add(standard_buttons, 0, wxALIGN_RIGHT | wxLEFT | wxBOTTOM | wxRIGHT, 5);
+    }
+
+    SetSizerAndFit(main_sizer);
+
+    persist = agi::make_unique<PersistLocation>(this, "Tool/Translation Assistant");
+
+    Bind(wxEVT_KEY_DOWN, &DialogTranslation::OnKeyDown, this);
+
+    blocks = active_line->ParseTags();
+    if (bad_block(blocks[0])) {
+        if (!NextBlock())
+            throw NothingToTranslate();
+    }
+    else
+        UpdateDisplay();
 }
 
 DialogTranslation::~DialogTranslation() { }
 
-void DialogTranslation::OnActiveLineChanged(AssDialogue *new_line) {
-	if (switching_lines) return;
+void DialogTranslation::OnActiveLineChanged(AssDialogue *new_line)
+{
+    if (switching_lines) return;
 
-	active_line = new_line;
-	blocks = active_line->ParseTags();
-	cur_block = 0;
+    active_line = new_line;
+    blocks = active_line->ParseTags();
+    cur_block = 0;
 
-	if (bad_block(blocks[cur_block]) && !NextBlock()) {
-		wxMessageBox(_("No more lines to translate."));
-		EndModal(1);
-	}
+    if (bad_block(blocks[cur_block]) && !NextBlock()) {
+        wxMessageBox(_("No more lines to translate."));
+        EndModal(1);
+    }
 }
 
-void DialogTranslation::OnExternalCommit(int commit_type) {
-	if (commit_type == AssFile::COMMIT_NEW || commit_type & AssFile::COMMIT_DIAG_ADDREM) {
-		line_count = c->ass->Events.size();
-		line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row + 1, line_count));
-	}
+void DialogTranslation::OnExternalCommit(int commit_type)
+{
+    if (commit_type == AssFile::COMMIT_NEW || commit_type & AssFile::COMMIT_DIAG_ADDREM) {
+        line_count = c->ass->Events.size();
+        line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row + 1, line_count));
+    }
 
-	if (commit_type & AssFile::COMMIT_DIAG_TEXT)
-		OnActiveLineChanged(active_line);
+    if (commit_type & AssFile::COMMIT_DIAG_TEXT)
+        OnActiveLineChanged(active_line);
 }
 
-bool DialogTranslation::NextBlock() {
-	switching_lines = true;
-	do {
-		if (cur_block == blocks.size() - 1) {
-			c->selectionController->NextLine();
-			AssDialogue *new_line = c->selectionController->GetActiveLine();
-			if (active_line == new_line || !new_line) return false;
-
-			active_line = new_line;
-			blocks = active_line->ParseTags();
-			cur_block = 0;
-		}
-		else
-			++cur_block;
-	} while (bad_block(blocks[cur_block]));
-	switching_lines = false;
-
-	UpdateDisplay();
-	return true;
+bool DialogTranslation::NextBlock()
+{
+    switching_lines = true;
+    do {
+        if (cur_block == blocks.size() - 1) {
+            c->selectionController->NextLine();
+            AssDialogue *new_line = c->selectionController->GetActiveLine();
+            if (active_line == new_line || !new_line) return false;
+
+            active_line = new_line;
+            blocks = active_line->ParseTags();
+            cur_block = 0;
+        }
+        else
+            ++cur_block;
+    } while (bad_block(blocks[cur_block]));
+    switching_lines = false;
+
+    UpdateDisplay();
+    return true;
 }
 
-bool DialogTranslation::PrevBlock() {
-	switching_lines = true;
-	do {
-		if (cur_block == 0) {
-			c->selectionController->PrevLine();
-			AssDialogue *new_line = c->selectionController->GetActiveLine();
-			if (active_line == new_line || !new_line) return false;
-
-			active_line = new_line;
-			blocks = active_line->ParseTags();
-			cur_block = blocks.size() - 1;
-		}
-		else
-			--cur_block;
-	} while (bad_block(blocks[cur_block]));
-	switching_lines = false;
-
-	UpdateDisplay();
-	return true;
+bool DialogTranslation::PrevBlock()
+{
+    switching_lines = true;
+    do {
+        if (cur_block == 0) {
+            c->selectionController->PrevLine();
+            AssDialogue *new_line = c->selectionController->GetActiveLine();
+            if (active_line == new_line || !new_line) return false;
+
+            active_line = new_line;
+            blocks = active_line->ParseTags();
+            cur_block = blocks.size() - 1;
+        }
+        else
+            --cur_block;
+    } while (bad_block(blocks[cur_block]));
+    switching_lines = false;
+
+    UpdateDisplay();
+    return true;
 }
 
-void DialogTranslation::UpdateDisplay() {
-	line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row, line_count));
-
-	original_text->SetReadOnly(false);
-	original_text->ClearAll();
-
-	size_t i = 0;
-	for (auto& block : blocks) {
-		if (block->GetType() == AssBlockType::PLAIN) {
-			int initial_pos = original_text->GetLength();
-			original_text->AppendTextRaw(block->GetText().c_str());
-			if (i == cur_block) {
-				original_text->StartStyling(initial_pos, 31);
-				original_text->SetStyling(block->GetText().size(), 1);
-			}
-		}
-		else
-			original_text->AppendTextRaw(block->GetText().c_str());
-		++i;
-	}
-
-	original_text->SetReadOnly(true);
-
-	if (seek_video->IsChecked()) c->videoController->JumpToTime(active_line->Start);
-
-	translated_text->ClearAll();
-	translated_text->SetFocus();
+void DialogTranslation::UpdateDisplay()
+{
+    line_number_display->SetLabel(fmt_tl("Current line: %d/%d", active_line->Row, line_count));
+
+    original_text->SetReadOnly(false);
+    original_text->ClearAll();
+
+    size_t i = 0;
+    for (auto &block : blocks) {
+        if (block->GetType() == AssBlockType::PLAIN) {
+            int initial_pos = original_text->GetLength();
+            original_text->AppendTextRaw(block->GetText().c_str());
+            if (i == cur_block) {
+                original_text->StartStyling(initial_pos, 31);
+                original_text->SetStyling(block->GetText().size(), 1);
+            }
+        }
+        else
+            original_text->AppendTextRaw(block->GetText().c_str());
+        ++i;
+    }
+
+    original_text->SetReadOnly(true);
+
+    if (seek_video->IsChecked()) c->videoController->JumpToTime(active_line->Start);
+
+    translated_text->ClearAll();
+    translated_text->SetFocus();
 }
 
-void DialogTranslation::Commit(bool next) {
-	std::string new_value = translated_text->GetTextRaw().data();
-	boost::replace_all(new_value, "\r\n", "\\N");
-	boost::replace_all(new_value, "\r", "\\N");
-	boost::replace_all(new_value, "\n", "\\N");
-	*blocks[cur_block] = AssDialogueBlockPlain(new_value);
-	active_line->UpdateText(blocks);
-
-	file_change_connection.Block();
-	c->ass->Commit(_("translation assistant"), AssFile::COMMIT_DIAG_TEXT);
-	file_change_connection.Unblock();
-
-	if (next) {
-		if (!NextBlock()) {
-			wxMessageBox(_("No more lines to translate."));
-			EndModal(1);
-		}
-	}
-	else {
-		UpdateDisplay();
-	}
+void DialogTranslation::Commit(bool next)
+{
+    std::string new_value = translated_text->GetTextRaw().data();
+    boost::replace_all(new_value, "\r\n", "\\N");
+    boost::replace_all(new_value, "\r", "\\N");
+    boost::replace_all(new_value, "\n", "\\N");
+    *blocks[cur_block] = AssDialogueBlockPlain(new_value);
+    active_line->UpdateText(blocks);
+
+    file_change_connection.Block();
+    c->ass->Commit(_("translation assistant"), AssFile::COMMIT_DIAG_TEXT);
+    file_change_connection.Unblock();
+
+    if (next) {
+        if (!NextBlock()) {
+            wxMessageBox(_("No more lines to translate."));
+            EndModal(1);
+        }
+    }
+    else {
+        UpdateDisplay();
+    }
 }
 
-void DialogTranslation::InsertOriginal() {
-	auto const& text = blocks[cur_block]->GetText();
-	translated_text->AddTextRaw(text.data(), text.size());
+void DialogTranslation::InsertOriginal()
+{
+    auto const &text = blocks[cur_block]->GetText();
+    translated_text->AddTextRaw(text.data(), text.size());
 }
 
-void DialogTranslation::OnKeyDown(wxKeyEvent &evt) {
-	hotkey::check("Translation Assistant", c, evt);
+void DialogTranslation::OnKeyDown(wxKeyEvent &evt)
+{
+    hotkey::check("Translation Assistant", c, evt);
 }
 
-void DialogTranslation::OnPlayVideoButton(wxCommandEvent &) {
-	c->videoController->PlayLine();
-	translated_text->SetFocus();
+void DialogTranslation::OnPlayVideoButton(wxCommandEvent &)
+{
+    c->videoController->PlayLine();
+    translated_text->SetFocus();
 }
 
-void DialogTranslation::OnPlayAudioButton(wxCommandEvent &) {
-	cmd::call("audio/play/selection", c);
-	translated_text->SetFocus();
+void DialogTranslation::OnPlayAudioButton(wxCommandEvent &)
+{
+    cmd::call("audio/play/selection", c);
+    translated_text->SetFocus();
 }
diff --git a/src/dialog_translation.h b/src/dialog_translation.h
index 295a0cbba7e3d1ab1ce6e315114eb346abb9869c..672d0a6094b5d89c5b76b3e379bdaecec93b3a13 100644
--- a/src/dialog_translation.h
+++ b/src/dialog_translation.h
@@ -32,48 +32,48 @@ class wxStyledTextCtrl;
 
 /// Assistant for translating subtitles in one language to another language
 class DialogTranslation final : public wxDialog {
-	agi::Context *c;
+    agi::Context *c;
 
-	agi::signal::Connection file_change_connection;
-	agi::signal::Connection active_line_connection;
+    agi::signal::Connection file_change_connection;
+    agi::signal::Connection active_line_connection;
 
-	/// The active line
-	AssDialogue *active_line;
-	/// The parsed dialogue blocks for the active line
-	std::vector<std::unique_ptr<AssDialogueBlock>> blocks;
-	/// Which dialogue block in the active line is currently being translated
-	size_t cur_block = 0;
+    /// The active line
+    AssDialogue *active_line;
+    /// The parsed dialogue blocks for the active line
+    std::vector<std::unique_ptr<AssDialogueBlock>> blocks;
+    /// Which dialogue block in the active line is currently being translated
+    size_t cur_block = 0;
 
-	/// Total number of dialogue lines in the file
-	size_t line_count;
+    /// Total number of dialogue lines in the file
+    size_t line_count;
 
-	/// Should active line change announcements be ignored?
-	bool switching_lines = false;
+    /// Should active line change announcements be ignored?
+    bool switching_lines = false;
 
-	wxStaticText *line_number_display;
-	wxStyledTextCtrl *original_text;
-	SubsTextEditCtrl *translated_text;
-	wxCheckBox *seek_video;
+    wxStaticText *line_number_display;
+    wxStyledTextCtrl *original_text;
+    SubsTextEditCtrl *translated_text;
+    wxCheckBox *seek_video;
 
-	std::unique_ptr<PersistLocation> persist;
+    std::unique_ptr<PersistLocation> persist;
 
-	void OnPlayAudioButton(wxCommandEvent &);
-	void OnPlayVideoButton(wxCommandEvent &);
-	void OnKeyDown(wxKeyEvent &evt);
-	void OnExternalCommit(int commit_type);
+    void OnPlayAudioButton(wxCommandEvent &);
+    void OnPlayVideoButton(wxCommandEvent &);
+    void OnKeyDown(wxKeyEvent &evt);
+    void OnExternalCommit(int commit_type);
 
-	void UpdateDisplay();
+    void UpdateDisplay();
 
-	void OnActiveLineChanged(AssDialogue *new_line);
+    void OnActiveLineChanged(AssDialogue *new_line);
 
 public:
-	DialogTranslation(agi::Context *context);
-	~DialogTranslation();
+    DialogTranslation(agi::Context *context);
+    ~DialogTranslation();
 
-	bool NextBlock();
-	bool PrevBlock();
-	void Commit(bool next);
-	void InsertOriginal();
+    bool NextBlock();
+    bool PrevBlock();
+    void Commit(bool next);
+    void InsertOriginal();
 
-	struct NothingToTranslate { };
+    struct NothingToTranslate { };
 };
diff --git a/src/dialog_version_check.cpp b/src/dialog_version_check.cpp
index eaf2f06668a9b4768af95b731b2bfbd4a4ea0f5c..f8aa7a07c103be4c26fbf6e78d49523ac368c99a 100644
--- a/src/dialog_version_check.cpp
+++ b/src/dialog_version_check.cpp
@@ -81,380 +81,398 @@ namespace ssl = boost::asio::ssl;
 namespace http = boost::beast::http;
 
 struct AegisubUpdateDescription {
-	int major;
-	int minor;
-	int patch;
-	std::string extra;
-	std::string description;
+    int major;
+    int minor;
+    int patch;
+    std::string extra;
+    std::string description;
 };
 
-AegisubUpdateDescription ParseVersionString(std::string version_string) {
-	std::vector<std::string> maj_min;
-	std::vector<std::string> patch;
-	agi::Split(maj_min, version_string, '.');
-	agi::Split(patch, maj_min[2], '-');
-
-	std::string extra = "";
-	if (patch.size() > 1) {
-		extra = patch[1];
-	}
-
-	return AegisubUpdateDescription{
-		atoi(maj_min[0].c_str()),
-		atoi(maj_min[1].c_str()),
-		atoi(patch[0].c_str()),
-		extra,
-		""
-	};
+AegisubUpdateDescription ParseVersionString(std::string version_string)
+{
+    std::vector<std::string> maj_min;
+    std::vector<std::string> patch;
+    agi::Split(maj_min, version_string, '.');
+    agi::Split(patch, maj_min[2], '-');
+
+    std::string extra = "";
+    if (patch.size() > 1) {
+        extra = patch[1];
+    }
+
+    return AegisubUpdateDescription{
+        atoi(maj_min[0].c_str()),
+        atoi(maj_min[1].c_str()),
+        atoi(patch[0].c_str()),
+        extra,
+        ""
+    };
 }
 
-bool IsNewer(AegisubUpdateDescription update) {
-	AegisubUpdateDescription current = ParseVersionString(GetReleaseVersion());
+bool IsNewer(AegisubUpdateDescription update)
+{
+    AegisubUpdateDescription current = ParseVersionString(GetReleaseVersion());
 
-	if (update.major != current.major)
-		return update.major > current.major;
+    if (update.major != current.major)
+        return update.major > current.major;
 
-	if (update.minor != current.minor)
-		return update.minor > current.minor;
+    if (update.minor != current.minor)
+        return update.minor > current.minor;
 
-	if (update.patch != current.patch)
-		return update.patch > current.patch;
+    if (update.patch != current.patch)
+        return update.patch > current.patch;
 
-	return update.extra.compare(current.extra) > 0;
+    return update.extra.compare(current.extra) > 0;
 }
 
-std::string AegisubVersion(AegisubUpdateDescription update) {
-	std::ostringstream s;
-	s << update.major << "." << update.minor << "." << update.patch;
-	if (!update.extra.empty())
-		s << "-" << update.extra;
+std::string AegisubVersion(AegisubUpdateDescription update)
+{
+    std::ostringstream s;
+    s << update.major << "." << update.minor << "." << update.patch;
+    if (!update.extra.empty())
+        s << "-" << update.extra;
 
-	return s.str();
+    return s.str();
 }
 
 class VersionCheckerResultDialog final : public wxDialog {
-	void OnCloseButton(wxCommandEvent &evt);
-	void OnRemindMeLater(wxCommandEvent &evt);
-	void OnClose(wxCloseEvent &evt);
+    void OnCloseButton(wxCommandEvent &evt);
+    void OnRemindMeLater(wxCommandEvent &evt);
+    void OnClose(wxCloseEvent &evt);
 
-	wxCheckBox *automatic_check_checkbox;
+    wxCheckBox *automatic_check_checkbox;
 
 public:
-	VersionCheckerResultDialog(wxString const& main_text, const AegisubUpdateDescription update);
+    VersionCheckerResultDialog(wxString const &main_text, const AegisubUpdateDescription update);
 
-	bool ShouldPreventAppExit() const override { return false; }
+    bool ShouldPreventAppExit() const override { return false; }
 };
 
-VersionCheckerResultDialog::VersionCheckerResultDialog(wxString const& main_text, const AegisubUpdateDescription update)
-: wxDialog(nullptr, -1, _("Version Checker"))
+VersionCheckerResultDialog::VersionCheckerResultDialog(wxString const &main_text, const AegisubUpdateDescription update)
+    : wxDialog(nullptr, -1, _("Version Checker"))
 {
-	const int controls_width = 500;
+    const int controls_width = 500;
 
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
 
-	wxStaticText *text = new wxStaticText(this, -1, main_text);
-	text->Wrap(controls_width);
-	main_sizer->Add(text, 0, wxBOTTOM|wxEXPAND, 6);
+    wxStaticText *text = new wxStaticText(this, -1, main_text);
+    text->Wrap(controls_width);
+    main_sizer->Add(text, 0, wxBOTTOM | wxEXPAND, 6);
 
-	main_sizer->Add(new wxStaticLine(this), 0, wxEXPAND|wxALL, 6);
+    main_sizer->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 6);
 
-	if (IsNewer(update)) {
-		text = new wxStaticText(this, -1, to_wx("Aegisub-Japan7"));
-		wxFont boldfont = text->GetFont();
-		boldfont.SetWeight(wxFONTWEIGHT_BOLD);
-		text->SetFont(boldfont);
-		main_sizer->Add(text, 0, wxEXPAND|wxBOTTOM, 6);
+    if (IsNewer(update)) {
+        text = new wxStaticText(this, -1, to_wx("Aegisub-Japan7"));
+        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, to_wx(update.description), wxDefaultPosition, wxSize(controls_width,60), wxTE_MULTILINE|wxTE_READONLY);
-		main_sizer->Add(descbox, 0, wxEXPAND|wxBOTTOM, 6);
+        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);
 
-		std::ostringstream surl;
-		surl << "https://" << UPDATE_CHECKER_SERVER << UPDATE_CHECKER_BASE_URL << "/Aegisub-Japan7-" << AegisubVersion(update) << "-x64.exe";
-		std::string url = surl.str();
+        std::ostringstream surl;
+        surl << "https://" << UPDATE_CHECKER_SERVER << UPDATE_CHECKER_BASE_URL << "/Aegisub-Japan7-" << AegisubVersion(update) << "-x64.exe";
+        std::string url = surl.str();
 
-		main_sizer->Add(new wxHyperlinkCtrl(this, -1, to_wx(url), to_wx(url)), 0, wxALIGN_LEFT|wxBOTTOM, 6);
-	}
+        main_sizer->Add(new wxHyperlinkCtrl(this, -1, to_wx(url), to_wx(url)), 0, wxALIGN_LEFT | wxBOTTOM, 6);
+    }
 
-	automatic_check_checkbox = new wxCheckBox(this, -1, _("&Auto Check for Updates"));
-	automatic_check_checkbox->SetValue(OPT_GET("App/Auto/Check For Updates")->GetBool());
+    automatic_check_checkbox = new wxCheckBox(this, -1, _("&Auto Check for Updates"));
+    automatic_check_checkbox->SetValue(OPT_GET("App/Auto/Check For Updates")->GetBool());
 
-	wxButton *remind_later_button = nullptr;
-	if (IsNewer(update))
-		remind_later_button = new wxButton(this, wxID_NO, _("Remind me again in a &week"));
+    wxButton *remind_later_button = nullptr;
+    if (IsNewer(update))
+        remind_later_button = new wxButton(this, wxID_NO, _("Remind me again in a &week"));
 
-	wxButton *close_button = new wxButton(this, wxID_OK, _("&Close"));
-	SetAffirmativeId(wxID_OK);
-	SetEscapeId(wxID_OK);
+    wxButton *close_button = new wxButton(this, wxID_OK, _("&Close"));
+    SetAffirmativeId(wxID_OK);
+    SetEscapeId(wxID_OK);
 
-	if (IsNewer(update))
-		main_sizer->Add(new wxStaticLine(this), 0, wxEXPAND|wxALL, 6);
-	main_sizer->Add(automatic_check_checkbox, 0, wxEXPAND|wxBOTTOM, 6);
+    if (IsNewer(update))
+        main_sizer->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 6);
+    main_sizer->Add(automatic_check_checkbox, 0, wxEXPAND | wxBOTTOM, 6);
 
-	auto button_sizer = new wxStdDialogButtonSizer();
-	button_sizer->AddButton(close_button);
-	if (remind_later_button)
-		button_sizer->AddButton(remind_later_button);
-	button_sizer->Realize();
-	main_sizer->Add(button_sizer, 0, wxEXPAND, 0);
+    auto button_sizer = new wxStdDialogButtonSizer();
+    button_sizer->AddButton(close_button);
+    if (remind_later_button)
+        button_sizer->AddButton(remind_later_button);
+    button_sizer->Realize();
+    main_sizer->Add(button_sizer, 0, wxEXPAND, 0);
 
-	wxSizer *outer_sizer = new wxBoxSizer(wxVERTICAL);
-	outer_sizer->Add(main_sizer, 0, wxALL|wxEXPAND, 12);
+    wxSizer *outer_sizer = new wxBoxSizer(wxVERTICAL);
+    outer_sizer->Add(main_sizer, 0, wxALL | wxEXPAND, 12);
 
-	SetSizerAndFit(outer_sizer);
-	Centre();
-	Show();
+    SetSizerAndFit(outer_sizer);
+    Centre();
+    Show();
 
-	Bind(wxEVT_BUTTON, std::bind(&VersionCheckerResultDialog::Close, this, false), wxID_OK);
-	Bind(wxEVT_BUTTON, &VersionCheckerResultDialog::OnRemindMeLater, this, wxID_NO);
-	Bind(wxEVT_CLOSE_WINDOW, &VersionCheckerResultDialog::OnClose, this);
+    Bind(wxEVT_BUTTON, std::bind(&VersionCheckerResultDialog::Close, this, false), wxID_OK);
+    Bind(wxEVT_BUTTON, &VersionCheckerResultDialog::OnRemindMeLater, this, wxID_NO);
+    Bind(wxEVT_CLOSE_WINDOW, &VersionCheckerResultDialog::OnClose, this);
 }
 
-void VersionCheckerResultDialog::OnRemindMeLater(wxCommandEvent &) {
-	// In one week
-	time_t new_next_check_time = time(nullptr) + 7*24*60*60;
-	OPT_SET("Version/Next Check")->SetInt(new_next_check_time);
+void VersionCheckerResultDialog::OnRemindMeLater(wxCommandEvent &)
+{
+    // In one week
+    time_t new_next_check_time = time(nullptr) + 7 * 24 * 60 * 60;
+    OPT_SET("Version/Next Check")->SetInt(new_next_check_time);
 
-	Close();
+    Close();
 }
 
-void VersionCheckerResultDialog::OnClose(wxCloseEvent &) {
-	OPT_SET("App/Auto/Check For Updates")->SetBool(automatic_check_checkbox->GetValue());
-	Destroy();
+void VersionCheckerResultDialog::OnClose(wxCloseEvent &)
+{
+    OPT_SET("App/Auto/Check For Updates")->SetBool(automatic_check_checkbox->GetValue());
+    Destroy();
 }
 
 DEFINE_EXCEPTION(VersionCheckError, agi::Exception);
 
-void PostErrorEvent(bool interactive, wxString const& error_text) {
-	if (interactive) {
-		agi::dispatch::Main().Async([=]{
-			new VersionCheckerResultDialog(error_text, {});
-		});
-	}
+void PostErrorEvent(bool interactive, wxString const &error_text)
+{
+    if (interactive) {
+        agi::dispatch::Main().Async([ = ] {
+            new VersionCheckerResultDialog(error_text, {});
+        });
+    }
 }
 
-__attribute__((unused)) 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";
+__attribute__((unused)) 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);
+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<HMODULE, BOOL (__stdcall *)(HMODULE)> kernel32(LoadLibraryW(L"kernel32.dll"), FreeLibrary);
-	if (!kernel32) return "";
+static wxString GetUILanguage()
+{
+    agi::scoped_holder<HMODULE, BOOL (__stdcall *)(HMODULE)> kernel32(LoadLibraryW(L"kernel32.dll"), FreeLibrary);
+    if (!kernel32) return "";
 
-	PGetUserPreferredUILanguages gupuil = (PGetUserPreferredUILanguages)GetProcAddress(kernel32, "GetUserPreferredUILanguages");
-	if (!gupuil) 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 "";
+    ULONG numlang = 0, output_len = 0;
+    if (gupuil(MUI_LANGUAGE_NAME, &numlang, 0, &output_len) != TRUE || !output_len)
+        return "";
 
-	std::vector<wchar_t> output(output_len);
-	if (!gupuil(MUI_LANGUAGE_NAME, &numlang, &output[0], &output_len) || numlang < 1)
-		return "";
+    std::vector<wchar_t> 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];
+    // We got at least one language, just treat it as the only, and a null-terminated string
+    return &output[0];
 }
 
-__attribute__((unused)) static wxString GetSystemLanguage() {
-	wxString res = GetUILanguage();
-	if (!res)
-		// On an old version of Windows, let's just return the LANGID as a string
-		res = fmt_wx("x-win%04x", GetUserDefaultUILanguage());
+__attribute__((unused)) static wxString GetSystemLanguage()
+{
+    wxString res = GetUILanguage();
+    if (!res)
+        // On an old version of Windows, let's just return the LANGID as a string
+        res = fmt_wx("x-win%04x", GetUserDefaultUILanguage());
 
-	return res;
+    return res;
 }
 #elif __APPLE__
-static wxString GetSystemLanguage() {
-	CFLocaleRef locale = CFLocaleCopyCurrent();
-	CFStringRef localeName = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleIdentifier);
+static wxString GetSystemLanguage()
+{
+    CFLocaleRef locale = CFLocaleCopyCurrent();
+    CFStringRef localeName = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleIdentifier);
 
-	char buf[128] = { 0 };
-	CFStringGetCString(localeName, buf, sizeof buf, kCFStringEncodingUTF8);
-	CFRelease(locale);
+    char buf[128] = { 0 };
+    CFStringGetCString(localeName, buf, sizeof buf, kCFStringEncodingUTF8);
+    CFRelease(locale);
 
-	return wxString::FromUTF8(buf);
+    return wxString::FromUTF8(buf);
 
 }
 #else
-__attribute__((unused)) static wxString GetSystemLanguage() {
-	return wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage())->CanonicalName;
+__attribute__((unused)) static wxString GetSystemLanguage()
+{
+    return wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage())->CanonicalName;
 }
 #endif
 
-__attribute__((unused)) static wxString GetAegisubLanguage() {
-	return to_wx(OPT_GET("App/Language")->GetString());
+__attribute__((unused)) static wxString GetAegisubLanguage()
+{
+    return to_wx(OPT_GET("App/Language")->GetString());
 }
 
-AegisubUpdateDescription GetLatestVersion() {
+AegisubUpdateDescription GetLatestVersion()
+{
 
-	boost::asio::io_context ioc;
-	boost::asio::ssl::context ctx(ssl::context::method::sslv23_client);
+    boost::asio::io_context ioc;
+    boost::asio::ssl::context ctx(ssl::context::method::sslv23_client);
 
-	boost::asio::ip::tcp::resolver resolver(ioc);
-	ssl::stream<boost::asio::ip::tcp::socket> stream(ioc, ctx);
+    boost::asio::ip::tcp::resolver resolver(ioc);
+    ssl::stream<boost::asio::ip::tcp::socket> stream(ioc, ctx);
 
-	if(! SSL_set_tlsext_host_name(stream.native_handle(), UPDATE_CHECKER_SERVER)) {
-            boost::system::error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
-            throw boost::system::system_error{ec};
-        }
+    if (! SSL_set_tlsext_host_name(stream.native_handle(), UPDATE_CHECKER_SERVER)) {
+        boost::system::error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
+        throw boost::system::system_error{ec};
+    }
 
-	auto const results = resolver.resolve(UPDATE_CHECKER_SERVER, "443");
+    auto const results = resolver.resolve(UPDATE_CHECKER_SERVER, "443");
 
-	boost::asio::connect(stream.next_layer(), results.begin(), results.end());
-	stream.handshake(boost::asio::ssl::stream_base::handshake_type::client);
+    boost::asio::connect(stream.next_layer(), results.begin(), results.end());
+    stream.handshake(boost::asio::ssl::stream_base::handshake_type::client);
 
-	std::ostringstream s;
-	s << UPDATE_CHECKER_BASE_URL;
-	s << "/latest";
-	std::string target = s.str();
-	http::request<http::string_body> req(http::verb::get, target, 11);
-	req.set(http::field::host, UPDATE_CHECKER_SERVER);
-	req.set(http::field::user_agent, "Aegisub-Japan7");
+    std::ostringstream s;
+    s << UPDATE_CHECKER_BASE_URL;
+    s << "/latest";
+    std::string target = s.str();
+    http::request<http::string_body> req(http::verb::get, target, 11);
+    req.set(http::field::host, UPDATE_CHECKER_SERVER);
+    req.set(http::field::user_agent, "Aegisub-Japan7");
 
-	http::write(stream, req);
+    http::write(stream, req);
 
-	boost::beast::flat_buffer buffer;
+    boost::beast::flat_buffer buffer;
 
-	http::response<http::string_body> res;
+    http::response<http::string_body> res;
 
-	http::read(stream, buffer, res);
+    http::read(stream, buffer, res);
 
-	// Gracefully close the stream
-        boost::system::error_code ec;
-        stream.shutdown(ec);
-        if(ec == boost::asio::error::eof)
-        {
-            // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
-            ec.assign(0, ec.category());
-        }
-        if(ec)
-            throw boost::system::system_error{ec};
+    // Gracefully close the stream
+    boost::system::error_code ec;
+    stream.shutdown(ec);
+    if (ec == boost::asio::error::eof) {
+        // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
+        ec.assign(0, ec.category());
+    }
+    if (ec)
+        throw boost::system::system_error{ec};
 
-	std::string line;
-	std::stringstream body(res.body().data());
+    std::string line;
+    std::stringstream body(res.body().data());
 
-	std::getline(body, line, '\n');
+    std::getline(body, line, '\n');
 
-	AegisubUpdateDescription version = ParseVersionString(line);
+    AegisubUpdateDescription version = ParseVersionString(line);
 
-	std::ostringstream desc;
-	while (std::getline(body, line, '\n')) {
-		desc << line;
-	}
+    std::ostringstream desc;
+    while (std::getline(body, line, '\n')) {
+        desc << line;
+    }
 
-	version.description = desc.str();
-	return version;
+    version.description = desc.str();
+    return version;
 
-	throw VersionCheckError(from_wx(_("Could not get update from updates server.")));
+    throw VersionCheckError(from_wx(_("Could not get update from updates server.")));
 }
 
 
-void DoCheck(bool interactive) {
-	AegisubUpdateDescription update = GetLatestVersion();
-
-	if (IsNewer(update) || interactive) {
-		agi::dispatch::Main().Async([=]{
-			wxString text;
-			if (IsNewer(update))
-				text = _("An update to Aegisub was found.");
-			else
-				text = _("There are no updates to Aegisub.");
-
-			new VersionCheckerResultDialog(text, update);
-		});
-	}
+void DoCheck(bool interactive)
+{
+    AegisubUpdateDescription update = GetLatestVersion();
+
+    if (IsNewer(update) || interactive) {
+        agi::dispatch::Main().Async([ = ] {
+            wxString text;
+            if (IsNewer(update))
+                text = _("An update to Aegisub was found.");
+            else
+                text = _("There are no updates to Aegisub.");
+
+            new VersionCheckerResultDialog(text, update);
+        });
+    }
 }
 }
 
-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, fmt_tl(
-				"There was an error checking for updates to Aegisub:\n%s\n\nIf other applications can access the Internet fine, this is probably a temporary server problem on our end.",
-				e.GetMessage()));
-		}
-		catch (...) {
-			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);
-		});
-	});
+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, fmt_tl(
+                               "There was an error checking for updates to Aegisub:\n%s\n\nIf other applications can access the Internet fine, this is probably a temporary server problem on our end.",
+                               e.GetMessage()));
+        }
+        catch (...)
+        {
+            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/src/dialog_video_details.cpp b/src/dialog_video_details.cpp
index 84dab54623e2fc617a1074f3cfbe0ea5fb70011c..594302a72c255a198553217e4e73dda9295e2cfa 100644
--- a/src/dialog_video_details.cpp
+++ b/src/dialog_video_details.cpp
@@ -41,36 +41,37 @@
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 
-void ShowVideoDetailsDialog(agi::Context *c) {
-	wxDialog d(c->parent, -1, _("Video Details"));
+void ShowVideoDetailsDialog(agi::Context *c)
+{
+    wxDialog d(c->parent, -1, _("Video Details"));
 
-	auto provider = c->project->VideoProvider();
-	auto width = provider->GetWidth();
-	auto height = provider->GetHeight();
-	auto framecount = provider->GetFrameCount();
-	auto fps = provider->GetFPS();
-	boost::rational<int> ar(width, height);
+    auto provider = c->project->VideoProvider();
+    auto width = provider->GetWidth();
+    auto height = provider->GetHeight();
+    auto framecount = provider->GetFrameCount();
+    auto fps = provider->GetFPS();
+    boost::rational<int> ar(width, height);
 
-	auto fg = new wxFlexGridSizer(2, 5, 10);
-	auto make_field = [&](wxString const& name, wxString const& value) {
-		fg->Add(new wxStaticText(&d, -1, name), 0, wxALIGN_CENTRE_VERTICAL);
-		fg->Add(new wxTextCtrl(&d, -1, value, wxDefaultPosition, wxSize(300,-1), wxTE_READONLY), 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
-	};
-	make_field(_("File name:"), c->project->VideoName().wstring());
-	make_field(_("FPS:"), fmt_wx("%.3f", fps.FPS()));
-	make_field(_("Resolution:"), fmt_wx("%dx%d (%d:%d)", width, height, ar.numerator(), ar.denominator()));
-	make_field(_("Length:"), fmt_plural(framecount, "1 frame", "%d frames (%s)",
-		framecount, agi::Time(fps.TimeAtFrame(framecount - 1)).GetAssFormatted(true)));
-	make_field(_("Decoder:"), to_wx(provider->GetDecoderName()));
+    auto fg = new wxFlexGridSizer(2, 5, 10);
+    auto make_field = [&](wxString const & name, wxString const & value) {
+        fg->Add(new wxStaticText(&d, -1, name), 0, wxALIGN_CENTRE_VERTICAL);
+        fg->Add(new wxTextCtrl(&d, -1, value, wxDefaultPosition, wxSize(300, -1), wxTE_READONLY), 0, wxALIGN_CENTRE_VERTICAL | wxEXPAND);
+    };
+    make_field(_("File name:"), c->project->VideoName().wstring());
+    make_field(_("FPS:"), fmt_wx("%.3f", fps.FPS()));
+    make_field(_("Resolution:"), fmt_wx("%dx%d (%d:%d)", width, height, ar.numerator(), ar.denominator()));
+    make_field(_("Length:"), fmt_plural(framecount, "1 frame", "%d frames (%s)",
+                                        framecount, agi::Time(fps.TimeAtFrame(framecount - 1)).GetAssFormatted(true)));
+    make_field(_("Decoder:"), to_wx(provider->GetDecoderName()));
 
-	auto video_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Video"));
-	video_sizer->Add(fg);
+    auto video_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Video"));
+    video_sizer->Add(fg);
 
-	auto main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(video_sizer, 1, wxALL|wxEXPAND, 5);
-	main_sizer->Add(d.CreateSeparatedButtonSizer(wxOK), 0, wxALL|wxEXPAND, 5);
-	d.SetSizerAndFit(main_sizer);
+    auto main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(video_sizer, 1, wxALL | wxEXPAND, 5);
+    main_sizer->Add(d.CreateSeparatedButtonSizer(wxOK), 0, wxALL | wxEXPAND, 5);
+    d.SetSizerAndFit(main_sizer);
 
-	d.CenterOnParent();
-	d.ShowModal();
+    d.CenterOnParent();
+    d.ShowModal();
 }
diff --git a/src/dialog_video_properties.cpp b/src/dialog_video_properties.cpp
index 90c407844cda55370f275fa41b6399b7bffa536b..917e9564127956c531645af8dc33901abf4a3a1f 100644
--- a/src/dialog_video_properties.cpp
+++ b/src/dialog_video_properties.cpp
@@ -29,136 +29,139 @@
 
 namespace {
 enum {
-	MISMATCH_IGNORE,
-	MISMATCH_PROMPT,
-	MISMATCH_RESAMPLE,
-	MISMATCH_SET
+    MISMATCH_IGNORE,
+    MISMATCH_PROMPT,
+    MISMATCH_RESAMPLE,
+    MISMATCH_SET
 };
 enum {
-	FIX_IGNORE,
-	FIX_SET,
-	FIX_RESAMPLE
+    FIX_IGNORE,
+    FIX_SET,
+    FIX_RESAMPLE
 };
 
-int prompt(wxWindow *parent, bool ar_changed, int sx, int sy, int vx, int vy) {
-	wxDialog d(parent, -1, _("Resolution mismatch"));
-
-	auto label_text = fmt_tl("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?", vx, vy, sx, sy);
-
-	auto sizer = new wxBoxSizer(wxVERTICAL);
-	sizer->Add(new wxStaticText(&d, -1, label_text), wxSizerFlags().Border());
-
-	wxRadioBox *rb;
-	if (ar_changed) {
-		wxString choices[] = {
-			_("Set to video resolution"),
-			_("Resample script (stretch to new aspect ratio)"),
-			_("Resample script (add borders)"),
-			_("Resample script (remove borders)")
-		};
-		rb = new wxRadioBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 4, choices, 1);
-	}
-	else {
-		wxString choices[] = {
-			_("Set to video resolution"),
-			_("Resample script"),
-		};
-		rb = new wxRadioBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 2, choices, 1);
-	}
-	sizer->Add(rb, wxSizerFlags().Border(wxALL & ~wxTOP).Expand());
-	sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Border().Expand());
-
-	unsigned int sel = OPT_GET("Video/Last Script Resolution Mismatch Choice")->GetInt();
-	rb->SetSelection(std::min(sel - 1, rb->GetCount()));
-
-	d.SetSizerAndFit(sizer);
-	d.CenterOnParent();
-
-	d.Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { d.EndModal(rb->GetSelection() + 1); }, wxID_OK);
-	d.Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { d.EndModal(0); }, wxID_CANCEL);
-	d.Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { HelpButton::OpenPage("Resolution mismatch"); }, wxID_HELP);
-
-	return d.ShowModal();
+int prompt(wxWindow *parent, bool ar_changed, int sx, int sy, int vx, int vy)
+{
+    wxDialog d(parent, -1, _("Resolution mismatch"));
+
+    auto label_text = fmt_tl("The resolution of the loaded video and the resolution specified for the subtitles don't match.\n\nVideo resolution:\t%d x %d\nScript resolution:\t%d x %d\n\nChange subtitles resolution to match video?", vx, vy, sx, sy);
+
+    auto sizer = new wxBoxSizer(wxVERTICAL);
+    sizer->Add(new wxStaticText(&d, -1, label_text), wxSizerFlags().Border());
+
+    wxRadioBox *rb;
+    if (ar_changed) {
+        wxString choices[] = {
+            _("Set to video resolution"),
+            _("Resample script (stretch to new aspect ratio)"),
+            _("Resample script (add borders)"),
+            _("Resample script (remove borders)")
+        };
+        rb = new wxRadioBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 4, choices, 1);
+    }
+    else {
+        wxString choices[] = {
+            _("Set to video resolution"),
+            _("Resample script"),
+        };
+        rb = new wxRadioBox(&d, -1, "", wxDefaultPosition, wxDefaultSize, 2, choices, 1);
+    }
+    sizer->Add(rb, wxSizerFlags().Border(wxALL & ~wxTOP).Expand());
+    sizer->Add(d.CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Border().Expand());
+
+    unsigned int sel = OPT_GET("Video/Last Script Resolution Mismatch Choice")->GetInt();
+    rb->SetSelection(std::min(sel - 1, rb->GetCount()));
+
+    d.SetSizerAndFit(sizer);
+    d.CenterOnParent();
+
+    d.Bind(wxEVT_BUTTON, [&](wxCommandEvent &) { d.EndModal(rb->GetSelection() + 1); }, wxID_OK);
+    d.Bind(wxEVT_BUTTON, [&](wxCommandEvent &) { d.EndModal(0); }, wxID_CANCEL);
+    d.Bind(wxEVT_BUTTON, [&](wxCommandEvent &) { HelpButton::OpenPage("Resolution mismatch"); }, wxID_HELP);
+
+    return d.ShowModal();
 }
 
-bool update_video_properties(AssFile *file, const AsyncVideoProvider *new_provider, wxWindow *parent) {
-	bool commit_subs = false;
-
-	// When opening dummy video only want to set the script properties if
-	// they were previously unset
-	bool set_properties = new_provider->ShouldSetVideoProperties();
-
-	auto matrix = new_provider->GetColorSpace();
-	if (set_properties && matrix != file->GetScriptInfo("YCbCr Matrix")) {
-		file->SetScriptInfo("YCbCr Matrix", matrix);
-		commit_subs = true;
-	}
-
-	// Check that the script resolution matches the video resolution
-	int sx = file->GetScriptInfoAsInt("PlayResX");
-	int sy = file->GetScriptInfoAsInt("PlayResY");
-	int vx = new_provider->GetWidth();
-	int vy = new_provider->GetHeight();
-
-	// If the script resolution hasn't been set at all just force it to the
-	// video resolution
-	if (sx == 0 && sy == 0) {
-		file->SetScriptInfo("PlayResX", std::to_string(vx));
-		file->SetScriptInfo("PlayResY", std::to_string(vy));
-		return true;
-	}
-
-	if (!set_properties)
-		return false;
-
-	// Treat exact multiples of the video resolution as equaling the resolution
-	// for the people who use that for subpixel precision (which is mostly
-	// pointless these days due to decimals being supported almost everywhere)
-	if (sx % vx == 0 && sy % vy == 0)
-		return commit_subs;
-
-	auto sar = double(sx) / sy;
-	auto var = double(vx) / vy;
-	bool ar_changed = std::abs(sar - var) / var > .01;
-
-	switch (OPT_GET("Video/Script Resolution Mismatch")->GetInt()) {
-	case MISMATCH_IGNORE: default:
-		return commit_subs;
-
-	case MISMATCH_SET:
-		file->SetScriptInfo("PlayResX", std::to_string(vx));
-		file->SetScriptInfo("PlayResY", std::to_string(vy));
-		return true;
-
-	case MISMATCH_RESAMPLE:
-		// Fallthrough to prompt if the AR changed
-		if (!ar_changed) {
-			ResampleResolution(file, {
-				{0, 0, 0, 0},
-				sx, sy, vx, vy,
-				ResampleARMode::Stretch,
-				YCbCrMatrix::rgb, YCbCrMatrix::rgb
-			});
-			return true;
-		}
-
-	case MISMATCH_PROMPT:
-		int res = prompt(parent, ar_changed, sx, sy, vx, vy);
-		if (res == FIX_IGNORE) return commit_subs;
-		OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res);
-
-		ResampleResolution(file, {
-			{0, 0, 0, 0},
-			sx, sy, vx, vy,
-			static_cast<ResampleARMode>(res - FIX_RESAMPLE),
-			YCbCrMatrix::rgb, YCbCrMatrix::rgb
-		});
-		return true;
-	}
+bool update_video_properties(AssFile *file, const AsyncVideoProvider *new_provider, wxWindow *parent)
+{
+    bool commit_subs = false;
+
+    // When opening dummy video only want to set the script properties if
+    // they were previously unset
+    bool set_properties = new_provider->ShouldSetVideoProperties();
+
+    auto matrix = new_provider->GetColorSpace();
+    if (set_properties && matrix != file->GetScriptInfo("YCbCr Matrix")) {
+        file->SetScriptInfo("YCbCr Matrix", matrix);
+        commit_subs = true;
+    }
+
+    // Check that the script resolution matches the video resolution
+    int sx = file->GetScriptInfoAsInt("PlayResX");
+    int sy = file->GetScriptInfoAsInt("PlayResY");
+    int vx = new_provider->GetWidth();
+    int vy = new_provider->GetHeight();
+
+    // If the script resolution hasn't been set at all just force it to the
+    // video resolution
+    if (sx == 0 && sy == 0) {
+        file->SetScriptInfo("PlayResX", std::to_string(vx));
+        file->SetScriptInfo("PlayResY", std::to_string(vy));
+        return true;
+    }
+
+    if (!set_properties)
+        return false;
+
+    // Treat exact multiples of the video resolution as equaling the resolution
+    // for the people who use that for subpixel precision (which is mostly
+    // pointless these days due to decimals being supported almost everywhere)
+    if (sx % vx == 0 && sy % vy == 0)
+        return commit_subs;
+
+    auto sar = double(sx) / sy;
+    auto var = double(vx) / vy;
+    bool ar_changed = std::abs(sar - var) / var > .01;
+
+    switch (OPT_GET("Video/Script Resolution Mismatch")->GetInt()) {
+case MISMATCH_IGNORE: default:
+        return commit_subs;
+
+    case MISMATCH_SET:
+        file->SetScriptInfo("PlayResX", std::to_string(vx));
+        file->SetScriptInfo("PlayResY", std::to_string(vy));
+        return true;
+
+    case MISMATCH_RESAMPLE:
+        // Fallthrough to prompt if the AR changed
+        if (!ar_changed) {
+            ResampleResolution(file, {
+                {0, 0, 0, 0},
+                sx, sy, vx, vy,
+                ResampleARMode::Stretch,
+                YCbCrMatrix::rgb, YCbCrMatrix::rgb
+            });
+            return true;
+        }
+
+    case MISMATCH_PROMPT:
+        int res = prompt(parent, ar_changed, sx, sy, vx, vy);
+        if (res == FIX_IGNORE) return commit_subs;
+        OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res);
+
+        ResampleResolution(file, {
+            {0, 0, 0, 0},
+            sx, sy, vx, vy,
+            static_cast<ResampleARMode>(res - FIX_RESAMPLE),
+            YCbCrMatrix::rgb, YCbCrMatrix::rgb
+        });
+        return true;
+    }
 }
 }
 
-void UpdateVideoProperties(AssFile *file, const AsyncVideoProvider *new_provider, wxWindow *parent) {
-	if (update_video_properties(file, new_provider, parent))
-		file->Commit(_("change script resolution"), AssFile::COMMIT_SCRIPTINFO);
+void UpdateVideoProperties(AssFile *file, const AsyncVideoProvider *new_provider, wxWindow *parent)
+{
+    if (update_video_properties(file, new_provider, parent))
+        file->Commit(_("change script resolution"), AssFile::COMMIT_SCRIPTINFO);
 }
diff --git a/src/dialogs.h b/src/dialogs.h
index 2e03d09e9e62c03d7b28b8afaf426687118d3278..18417c8293f3337c2d5312eefc2483099c3b5fcb 100644
--- a/src/dialogs.h
+++ b/src/dialogs.h
@@ -52,7 +52,7 @@ bool PromptForResampleSettings(agi::Context *c, ResampleSettings &settings);
 /// Update the video properties for a newly opened video, possibly prompting the user about what to do
 void UpdateVideoProperties(AssFile *file, const AsyncVideoProvider *new_provider, wxWindow *parent);
 
-int GetSelectedChoices(wxWindow *parent, wxArrayInt& selections, wxString const& message, wxString const& caption, wxArrayString const& choices);
+int GetSelectedChoices(wxWindow *parent, wxArrayInt &selections, wxString const &message, wxString const &caption, wxArrayString const &choices);
 
 std::string CreateDummyVideo(wxWindow *parent);
 
diff --git a/src/export_fixstyle.cpp b/src/export_fixstyle.cpp
index b68111b716afbf4406a1bd035f7ab6a8fff7dc19..e05719a64180d2070be6014bfa30202cb7e3f999 100644
--- a/src/export_fixstyle.cpp
+++ b/src/export_fixstyle.cpp
@@ -43,17 +43,18 @@
 #include <wx/intl.h>
 
 AssFixStylesFilter::AssFixStylesFilter()
-: AssExportFilter(from_wx(_("Fix Styles")), from_wx(_("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)
 {
 }
 
-void AssFixStylesFilter::ProcessSubs(AssFile *subs) {
-	auto styles = subs->GetStyles();
-	for (auto& str : styles) boost::to_lower(str);
-	sort(begin(styles), end(styles));
+void AssFixStylesFilter::ProcessSubs(AssFile *subs)
+{
+    auto styles = subs->GetStyles();
+    for (auto &str : styles) boost::to_lower(str);
+    sort(begin(styles), end(styles));
 
-	for (auto& diag : subs->Events) {
-		if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag.Style.get())))
-			diag.Style = "Default";
-	}
+    for (auto &diag : subs->Events) {
+        if (!binary_search(begin(styles), end(styles), boost::to_lower_copy(diag.Style.get())))
+            diag.Style = "Default";
+    }
 }
diff --git a/src/export_fixstyle.h b/src/export_fixstyle.h
index 76bb18975a1e9424886b254d63bf298c419ab540..20bb4a6c85fed57b45c769db026484b3fa6e5920 100644
--- a/src/export_fixstyle.h
+++ b/src/export_fixstyle.h
@@ -38,7 +38,7 @@
 /// @brief Fixes styles by replacing any style that isn't available on file with Default
 class AssFixStylesFilter final : public AssExportFilter {
 public:
-	static void ProcessSubs(AssFile *subs);
-	void ProcessSubs(AssFile *subs, wxWindow *) override { ProcessSubs(subs); }
-	AssFixStylesFilter();
+    static void ProcessSubs(AssFile *subs);
+    void ProcessSubs(AssFile *subs, wxWindow *) override { ProcessSubs(subs); }
+    AssFixStylesFilter();
 };
diff --git a/src/export_framerate.cpp b/src/export_framerate.cpp
index 024489f64ccafd19985383b4997d605fdbcab735..efa530c933d26e2f6a90babb3bbed6b66208d93f 100644
--- a/src/export_framerate.cpp
+++ b/src/export_framerate.cpp
@@ -49,176 +49,183 @@
 #include <wx/textctrl.h>
 
 AssTransformFramerateFilter::AssTransformFramerateFilter()
-: 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)
+    : 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)
 {
 }
 
-void AssTransformFramerateFilter::ProcessSubs(AssFile *subs, wxWindow *) {
-	TransformFrameRate(subs);
+void AssTransformFramerateFilter::ProcessSubs(AssFile *subs, wxWindow *)
+{
+    TransformFrameRate(subs);
 }
 
-wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c) {
-	LoadSettings(true, c);
-
-	wxWindow *base = new wxPanel(parent, -1);
-
-	// Input sizer
-	auto InputSizer = new wxBoxSizer(wxHORIZONTAL);
-	wxString initialInput;
-	auto FromVideo = new wxButton(base,-1,_("From &video"));
-	if (Input.IsLoaded()) {
-		initialInput = fmt_wx("%2.3f", Input.FPS());
-		FromVideo->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-			InputFramerate->SetValue(fmt_wx("%g", c->project->Timecodes().FPS()));
-		});
-	}
-	else {
-		initialInput = "23.976";
-		FromVideo->Enable(false);
-	}
-	InputFramerate = new wxTextCtrl(base,-1,initialInput,wxDefaultPosition,wxSize(60,20));
-	InputSizer->Add(InputFramerate,0,wxEXPAND | wxLEFT,5);
-	InputSizer->Add(FromVideo,0,wxEXPAND | wxLEFT,5);
-	InputSizer->AddStretchSpacer(1);
-
-	// Output sizers
-	auto OutputSizerTop = new wxBoxSizer(wxHORIZONTAL);
-	auto OutputSizerBottom = new wxBoxSizer(wxHORIZONTAL);
-	auto OutputSizer = new wxBoxSizer(wxVERTICAL);
-
-	// Output top line
-	RadioOutputVFR = new wxRadioButton(base,-1,_("V&ariable"),wxDefaultPosition,wxDefaultSize,wxRB_GROUP);
-	OutputSizerTop->Add(RadioOutputVFR,0,wxEXPAND,0);
-
-	// Output bottom line
-	RadioOutputCFR = new wxRadioButton(base,-1,_("&Constant: "));
-	wxString initialOutput = initialInput;
-	if (!Output.IsVFR()) {
-		RadioOutputVFR->Enable(false);
-		RadioOutputCFR->SetValue(true);
-	}
-	OutputFramerate = new wxTextCtrl(base,-1,initialOutput,wxDefaultPosition,wxSize(60,20));
-	OutputSizerBottom->Add(RadioOutputCFR,0,wxEXPAND,0);
-	OutputSizerBottom->Add(OutputFramerate,0,wxEXPAND | wxLEFT,5);
-	OutputSizerBottom->AddStretchSpacer(1);
-
-	// Reverse checkbox
-	Reverse = new wxCheckBox(base,-1,_("&Reverse transformation"));
-
-	// Output final
-	OutputSizer->Add(OutputSizerTop,0,wxLEFT,5);
-	OutputSizer->Add(OutputSizerBottom,0,wxLEFT,5);
-
-	// Main window
-	auto MainSizer = new wxFlexGridSizer(3,2,5,10);
-	MainSizer->Add(new wxStaticText(base,-1,_("Input framerate: ")),0,wxEXPAND | wxALIGN_CENTER_VERTICAL,0);
-	MainSizer->Add(InputSizer,0,wxEXPAND,0);
-	MainSizer->Add(new wxStaticText(base,-1,_("Output: ")),0,wxALIGN_CENTER_VERTICAL,0);
-	MainSizer->Add(OutputSizer,0,wxEXPAND,0);
-	MainSizer->Add(Reverse,0,wxTOP|wxEXPAND,5);
-
-	// Window
-	base->SetSizerAndFit(MainSizer);
-	return base;
+wxWindow *AssTransformFramerateFilter::GetConfigDialogWindow(wxWindow *parent, agi::Context *c)
+{
+    LoadSettings(true, c);
+
+    wxWindow *base = new wxPanel(parent, -1);
+
+    // Input sizer
+    auto InputSizer = new wxBoxSizer(wxHORIZONTAL);
+    wxString initialInput;
+    auto FromVideo = new wxButton(base, -1, _("From &video"));
+    if (Input.IsLoaded()) {
+        initialInput = fmt_wx("%2.3f", Input.FPS());
+        FromVideo->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+            InputFramerate->SetValue(fmt_wx("%g", c->project->Timecodes().FPS()));
+        });
+    }
+    else {
+        initialInput = "23.976";
+        FromVideo->Enable(false);
+    }
+    InputFramerate = new wxTextCtrl(base, -1, initialInput, wxDefaultPosition, wxSize(60, 20));
+    InputSizer->Add(InputFramerate, 0, wxEXPAND | wxLEFT, 5);
+    InputSizer->Add(FromVideo, 0, wxEXPAND | wxLEFT, 5);
+    InputSizer->AddStretchSpacer(1);
+
+    // Output sizers
+    auto OutputSizerTop = new wxBoxSizer(wxHORIZONTAL);
+    auto OutputSizerBottom = new wxBoxSizer(wxHORIZONTAL);
+    auto OutputSizer = new wxBoxSizer(wxVERTICAL);
+
+    // Output top line
+    RadioOutputVFR = new wxRadioButton(base, -1, _("V&ariable"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
+    OutputSizerTop->Add(RadioOutputVFR, 0, wxEXPAND, 0);
+
+    // Output bottom line
+    RadioOutputCFR = new wxRadioButton(base, -1, _("&Constant: "));
+    wxString initialOutput = initialInput;
+    if (!Output.IsVFR()) {
+        RadioOutputVFR->Enable(false);
+        RadioOutputCFR->SetValue(true);
+    }
+    OutputFramerate = new wxTextCtrl(base, -1, initialOutput, wxDefaultPosition, wxSize(60, 20));
+    OutputSizerBottom->Add(RadioOutputCFR, 0, wxEXPAND, 0);
+    OutputSizerBottom->Add(OutputFramerate, 0, wxEXPAND | wxLEFT, 5);
+    OutputSizerBottom->AddStretchSpacer(1);
+
+    // Reverse checkbox
+    Reverse = new wxCheckBox(base, -1, _("&Reverse transformation"));
+
+    // Output final
+    OutputSizer->Add(OutputSizerTop, 0, wxLEFT, 5);
+    OutputSizer->Add(OutputSizerBottom, 0, wxLEFT, 5);
+
+    // Main window
+    auto MainSizer = new wxFlexGridSizer(3, 2, 5, 10);
+    MainSizer->Add(new wxStaticText(base, -1, _("Input framerate: ")), 0, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0);
+    MainSizer->Add(InputSizer, 0, wxEXPAND, 0);
+    MainSizer->Add(new wxStaticText(base, -1, _("Output: ")), 0, wxALIGN_CENTER_VERTICAL, 0);
+    MainSizer->Add(OutputSizer, 0, wxEXPAND, 0);
+    MainSizer->Add(Reverse, 0, wxTOP | wxEXPAND, 5);
+
+    // Window
+    base->SetSizerAndFit(MainSizer);
+    return base;
 }
 
-void AssTransformFramerateFilter::LoadSettings(bool is_default, agi::Context *c) {
-	this->c = c;
-
-	if (is_default) {
-		auto provider = c->project->VideoProvider();
-		Output = c->project->Timecodes();
-		Input = provider ? provider->GetFPS() : Output;
-	}
-	else {
-		double temp;
-		InputFramerate->GetValue().ToDouble(&temp);
-		Input = temp;
-		if (RadioOutputCFR->GetValue()) {
-			OutputFramerate->GetValue().ToDouble(&temp);
-			Output = temp;
-		}
-		else Output = c->project->Timecodes();
-
-		if (Reverse->IsChecked())
-			std::swap(Input, Output);
-	}
+void AssTransformFramerateFilter::LoadSettings(bool is_default, agi::Context *c)
+{
+    this->c = c;
+
+    if (is_default) {
+        auto provider = c->project->VideoProvider();
+        Output = c->project->Timecodes();
+        Input = provider ? provider->GetFPS() : Output;
+    }
+    else {
+        double temp;
+        InputFramerate->GetValue().ToDouble(&temp);
+        Input = temp;
+        if (RadioOutputCFR->GetValue()) {
+            OutputFramerate->GetValue().ToDouble(&temp);
+            Output = temp;
+        }
+        else Output = c->project->Timecodes();
+
+        if (Reverse->IsChecked())
+            std::swap(Input, Output);
+    }
 }
 
 /// Truncate a time to centisecond precision
-static int trunc_cs(int time) {
-	return (time / 10) * 10;
+static int trunc_cs(int time)
+{
+    return (time / 10) * 10;
 }
 
-void AssTransformFramerateFilter::TransformTimeTags(std::string const& name, AssOverrideParameter *curParam, void *curData) {
-	VariableDataType type = curParam->GetType();
-	if (type != VariableDataType::INT && type != VariableDataType::FLOAT) return;
-
-	AssTransformFramerateFilter *instance = static_cast<AssTransformFramerateFilter*>(curData);
-	AssDialogue *curDiag = instance->line;
-
-	int parVal = curParam->Get<int>();
-
-	switch (curParam->classification) {
-		case AssParameterClass::RELATIVE_TIME_START: {
-			int value = instance->ConvertTime(trunc_cs(curDiag->Start) + parVal) - instance->newStart;
-
-			// An end time of 0 is actually the end time of the line, so ensure
-			// nonzero is never converted to 0
-			// Needed here rather than the end case because start/end here mean
-			// which end of the line the time is relative to, not whether it's
-			// the start or end time (compare \move and \fad)
-			if (value == 0 && parVal != 0) value = 1;
-			curParam->Set(value);
-			break;
-		}
-		case AssParameterClass::RELATIVE_TIME_END:
-			curParam->Set(instance->newEnd - instance->ConvertTime(trunc_cs(curDiag->End) - parVal));
-			break;
-		case AssParameterClass::KARAOKE: {
-			int start = curDiag->Start / 10 + instance->oldK + parVal;
-			int value = (instance->ConvertTime(start * 10) - instance->newStart) / 10 - instance->newK;
-			instance->oldK += parVal;
-			instance->newK += value;
-			curParam->Set(value);
-			break;
-		}
-		default:
-			return;
-	}
+void AssTransformFramerateFilter::TransformTimeTags(std::string const &name, AssOverrideParameter *curParam, void *curData)
+{
+    VariableDataType type = curParam->GetType();
+    if (type != VariableDataType::INT &&type != VariableDataType::FLOAT) return;
+
+    AssTransformFramerateFilter *instance = static_cast<AssTransformFramerateFilter *>(curData);
+    AssDialogue *curDiag = instance->line;
+
+    int parVal = curParam->Get<int>();
+
+    switch (curParam->classification) {
+    case AssParameterClass::RELATIVE_TIME_START: {
+        int value = instance->ConvertTime(trunc_cs(curDiag->Start) + parVal) - instance->newStart;
+
+        // An end time of 0 is actually the end time of the line, so ensure
+        // nonzero is never converted to 0
+        // Needed here rather than the end case because start/end here mean
+        // which end of the line the time is relative to, not whether it's
+        // the start or end time (compare \move and \fad)
+        if (value == 0 && parVal != 0) value = 1;
+        curParam->Set(value);
+        break;
+    }
+    case AssParameterClass::RELATIVE_TIME_END:
+        curParam->Set(instance->newEnd - instance->ConvertTime(trunc_cs(curDiag->End) - parVal));
+        break;
+    case AssParameterClass::KARAOKE: {
+        int start = curDiag->Start / 10 + instance->oldK + parVal;
+        int value = (instance->ConvertTime(start * 10) - instance->newStart) / 10 - instance->newK;
+        instance->oldK += parVal;
+        instance->newK += value;
+        curParam->Set(value);
+        break;
+    }
+    default:
+        return;
+    }
 }
 
-void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs) {
-	if (!Input.IsLoaded() || !Output.IsLoaded()) return;
-	for (auto& curDialogue : subs->Events) {
-		line = &curDialogue;
-		newK = 0;
-		oldK = 0;
-		newStart = trunc_cs(ConvertTime(curDialogue.Start));
-		newEnd = trunc_cs(ConvertTime(curDialogue.End) + 9);
-
-		// Process stuff
-		auto blocks = line->ParseTags();
-		for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
-			block->ProcessParameters(TransformTimeTags, this);
-		curDialogue.Start = newStart;
-		curDialogue.End = newEnd;
-		curDialogue.UpdateText(blocks);
-	}
+void AssTransformFramerateFilter::TransformFrameRate(AssFile *subs)
+{
+    if (!Input.IsLoaded() || !Output.IsLoaded()) return;
+    for (auto &curDialogue : subs->Events) {
+        line = &curDialogue;
+        newK = 0;
+        oldK = 0;
+        newStart = trunc_cs(ConvertTime(curDialogue.Start));
+        newEnd = trunc_cs(ConvertTime(curDialogue.End) + 9);
+
+        // Process stuff
+        auto blocks = line->ParseTags();
+        for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
+            block->ProcessParameters(TransformTimeTags, this);
+        curDialogue.Start = newStart;
+        curDialogue.End = newEnd;
+        curDialogue.UpdateText(blocks);
+    }
 }
 
-int AssTransformFramerateFilter::ConvertTime(int time) {
-	int frame = Output.FrameAtTime(time);
-	int frameStart = Output.TimeAtFrame(frame);
-	int frameEnd = Output.TimeAtFrame(frame + 1);
-	int frameDur = frameEnd - frameStart;
-	double dist = double(time - frameStart) / frameDur;
+int AssTransformFramerateFilter::ConvertTime(int time)
+{
+    int frame = Output.FrameAtTime(time);
+    int frameStart = Output.TimeAtFrame(frame);
+    int frameEnd = Output.TimeAtFrame(frame + 1);
+    int frameDur = frameEnd - frameStart;
+    double dist = double(time - frameStart) / frameDur;
 
-	int newStart = Input.TimeAtFrame(frame);
-	int newEnd = Input.TimeAtFrame(frame + 1);
-	int newDur = newEnd - newStart;
+    int newStart = Input.TimeAtFrame(frame);
+    int newEnd = Input.TimeAtFrame(frame + 1);
+    int newDur = newEnd - newStart;
 
-	return newStart + newDur * dist;
+    return newStart + newDur * dist;
 }
diff --git a/src/export_framerate.h b/src/export_framerate.h
index 3ed57e445bfbe339323528d383ec44404760f6dd..5fd27eda6c5503b3dc8da24523aa39ebe2a9c984 100644
--- a/src/export_framerate.h
+++ b/src/export_framerate.h
@@ -40,46 +40,46 @@ class wxTextCtrl;
 /// @class AssTransformFramerateFilter
 /// @brief Transform subtitle times, including those in override tags, from an input framerate to an output framerate
 class AssTransformFramerateFilter final : public AssExportFilter {
-	agi::Context *c = nullptr;
-	AssDialogue *line = nullptr;
-	int newStart = 0;
-	int newEnd = 0;
-	int newK = 0;
-	int oldK = 0;
+    agi::Context *c = nullptr;
+    AssDialogue *line = nullptr;
+    int newStart = 0;
+    int newEnd = 0;
+    int newK = 0;
+    int oldK = 0;
 
-	// Yes, these are backwards. It sort of makes sense if you think about what it's doing.
-	agi::vfr::Framerate Input;  ///< Destination frame rate
-	agi::vfr::Framerate Output; ///< Source frame rate
+    // Yes, these are backwards. It sort of makes sense if you think about what it's doing.
+    agi::vfr::Framerate Input;  ///< Destination frame rate
+    agi::vfr::Framerate Output; ///< Source frame rate
 
-	wxTextCtrl *InputFramerate; ///< Input frame rate text box
-	wxTextCtrl *OutputFramerate; ///< Output frame rate text box
+    wxTextCtrl *InputFramerate; ///< Input frame rate text box
+    wxTextCtrl *OutputFramerate; ///< Output frame rate text box
 
-	wxRadioButton *RadioOutputCFR; ///< CFR radio control
-	wxRadioButton *RadioOutputVFR; ///< VFR radio control
+    wxRadioButton *RadioOutputCFR; ///< CFR radio control
+    wxRadioButton *RadioOutputVFR; ///< VFR radio control
 
-	wxCheckBox *Reverse; ///< Switch input and output
+    wxCheckBox *Reverse; ///< Switch input and output
 
-	/// @brief Apply the transformation to a file
-	/// @param subs File to process
-	void TransformFrameRate(AssFile *subs);
-	/// @brief Transform a single tag
-	/// @param name Name of the tag
-	/// @param curParam Current parameter being processed
-	/// @param userdata Filter instance
-	static void TransformTimeTags(std::string const& name, AssOverrideParameter *curParam, void *userdata);
+    /// @brief Apply the transformation to a file
+    /// @param subs File to process
+    void TransformFrameRate(AssFile *subs);
+    /// @brief Transform a single tag
+    /// @param name Name of the tag
+    /// @param curParam Current parameter being processed
+    /// @param userdata Filter instance
+    static void TransformTimeTags(std::string const &name, AssOverrideParameter *curParam, void *userdata);
 
-	/// @brief Convert a time from the input frame rate to the output frame rate
-	/// @param time Time in ms to convert
-	/// @return Time in ms
-	///
-	/// This preserves two things:
-	///   1. The frame number
-	///   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);
+    /// @brief Convert a time from the input frame rate to the output frame rate
+    /// @param time Time in ms to convert
+    /// @return Time in ms
+    ///
+    /// This preserves two things:
+    ///   1. The frame number
+    ///   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);
 public:
-	AssTransformFramerateFilter();
-	void ProcessSubs(AssFile *subs, wxWindow *) override;
-	wxWindow *GetConfigDialogWindow(wxWindow *parent, agi::Context *c) override;
-	void LoadSettings(bool is_default, agi::Context *c) override;
+    AssTransformFramerateFilter();
+    void ProcessSubs(AssFile *subs, wxWindow *) override;
+    wxWindow *GetConfigDialogWindow(wxWindow *parent, agi::Context *c) override;
+    void LoadSettings(bool is_default, agi::Context *c) override;
 };
diff --git a/src/factory_manager.h b/src/factory_manager.h
index 89a20d0ba01f65344bcf2ecde7c105f84f3777de..6289d2ebc4e6e5cb824c74d77470a527b48cb566 100644
--- a/src/factory_manager.h
+++ b/src/factory_manager.h
@@ -19,35 +19,37 @@
 
 namespace {
 template<typename Container>
-std::vector<std::string> GetClasses(Container const& c) {
-	std::vector<std::string> list;
-	for (auto const& provider : c) {
-		if (!provider.hidden)
-			list.push_back(provider.name);
-	}
-	return list;
+std::vector<std::string> GetClasses(Container const &c)
+{
+    std::vector<std::string> list;
+    for (auto const &provider : c) {
+        if (!provider.hidden)
+            list.push_back(provider.name);
+    }
+    return list;
 }
 
 template<typename Container>
-auto GetSorted(Container const& c, std::string const& preferred) -> std::vector<decltype(&*c.begin())> {
-	std::vector<decltype(&*c.begin())> sorted;
-	sorted.reserve(std::distance(c.begin(), c.end()));
-	size_t end_of_hidden = 0;
-	bool any_hidden = false;
-	for (auto const& provider : c) {
-		if (provider.hidden) {
-			sorted.push_back(&provider);
-			any_hidden = true;
-		}
-		else if (any_hidden && end_of_hidden == 0) {
-			end_of_hidden = sorted.size();
-			sorted.push_back(&provider);
-		}
-		else if (preferred == provider.name)
-			sorted.insert(sorted.begin() + end_of_hidden, &provider);
-		else
-			sorted.push_back(&provider);
-	}
-	return sorted;
+auto GetSorted(Container const &c, std::string const &preferred) -> std::vector<decltype(&*c.begin())>
+{
+    std::vector<decltype(&*c.begin())> sorted;
+    sorted.reserve(std::distance(c.begin(), c.end()));
+    size_t end_of_hidden = 0;
+    bool any_hidden = false;
+    for (auto const &provider : c) {
+        if (provider.hidden) {
+            sorted.push_back(&provider);
+            any_hidden = true;
+        }
+        else if (any_hidden && end_of_hidden == 0) {
+            end_of_hidden = sorted.size();
+            sorted.push_back(&provider);
+        }
+        else if (preferred == provider.name)
+            sorted.insert(sorted.begin() + end_of_hidden, &provider);
+        else
+            sorted.push_back(&provider);
+    }
+    return sorted;
 }
 }
diff --git a/src/ffmpegsource_common.cpp b/src/ffmpegsource_common.cpp
index 3ffa7f4fbeeeb81ded6bc24fdf8a87e0862a55a5..2945f5b078fe807424353f57da9e8b9f6e8eb379 100644
--- a/src/ffmpegsource_common.cpp
+++ b/src/ffmpegsource_common.cpp
@@ -51,9 +51,9 @@
 #include <wx/choicdlg.h>
 
 FFmpegSourceProvider::FFmpegSourceProvider(agi::BackgroundRunner *br)
-: br(br)
+    : br(br)
 {
-	FFMS_Init(0, 0);
+    FFMS_Init(0, 0);
 }
 
 /// @brief Does indexing of a source file
@@ -61,145 +61,152 @@ FFmpegSourceProvider::FFmpegSourceProvider(agi::BackgroundRunner *br)
 /// @param CacheName    The filename of the output index file
 /// @param Trackmask    A binary mask of the track numbers to index
 FFMS_Index *FFmpegSourceProvider::DoIndexing(FFMS_Indexer *Indexer,
-	                                         agi::fs::path const& CacheName,
-	                                         TrackSelection Track,
-	                                         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;
-
-	// index all audio tracks
-	FFMS_Index *Index;
-	br->Run([&](agi::ProgressSink *ps) {
-		ps->SetTitle(from_wx(_("Indexing")));
-		ps->SetMessage(from_wx(_("Reading timecodes and frame/sample data")));
-		TIndexCallback callback = [](int64_t Current, int64_t Total, void *Private) -> int {
-			auto ps = static_cast<agi::ProgressSink *>(Private);
-			ps->SetProgress(Current, Total);
-			return ps->IsCancelled();
-		};
-		if (Track == TrackSelection::All)
-			FFMS_TrackTypeIndexSettings(Indexer, FFMS_TYPE_AUDIO, 1, 0);
-		else if (Track != TrackSelection::None)
-			FFMS_TrackIndexSettings(Indexer, static_cast<int>(Track), 1, 0);
-		FFMS_TrackTypeIndexSettings(Indexer, FFMS_TYPE_VIDEO, 1, 0);
-		FFMS_SetProgressCallback(Indexer, callback, ps);
-		Index = FFMS_DoIndexing2(Indexer, IndexEH, &ErrInfo);
-	});
-
-	if (Index == nullptr)
-		throw agi::EnvironmentError(std::string("Failed to index: ") + ErrInfo.Buffer);
-
-	// write index to disk for later use
-	FFMS_WriteIndex(CacheName.string().c_str(), Index, &ErrInfo);
-
-	return Index;
+        agi::fs::path const &CacheName,
+        TrackSelection Track,
+        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;
+
+    // index all audio tracks
+    FFMS_Index *Index;
+    br->Run([&](agi::ProgressSink * ps) {
+        ps->SetTitle(from_wx(_("Indexing")));
+        ps->SetMessage(from_wx(_("Reading timecodes and frame/sample data")));
+        TIndexCallback callback = [](int64_t Current, int64_t Total, void *Private) -> int {
+            auto ps = static_cast<agi::ProgressSink *>(Private);
+            ps->SetProgress(Current, Total);
+            return ps->IsCancelled();
+        };
+        if (Track == TrackSelection::All)
+            FFMS_TrackTypeIndexSettings(Indexer, FFMS_TYPE_AUDIO, 1, 0);
+        else if (Track != TrackSelection::None)
+            FFMS_TrackIndexSettings(Indexer, static_cast<int>(Track), 1, 0);
+        FFMS_TrackTypeIndexSettings(Indexer, FFMS_TYPE_VIDEO, 1, 0);
+        FFMS_SetProgressCallback(Indexer, callback, ps);
+        Index = FFMS_DoIndexing2(Indexer, IndexEH, &ErrInfo);
+    });
+
+    if (Index == nullptr)
+        throw agi::EnvironmentError(std::string("Failed to index: ") + ErrInfo.Buffer);
+
+    // write index to disk for later use
+    FFMS_WriteIndex(CacheName.string().c_str(), Index, &ErrInfo);
+
+    return Index;
 }
 
 /// @brief Finds all tracks of the given type and return their track numbers and respective codec names
 /// @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<int, std::string> FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type) {
-	std::map<int,std::string> TrackList;
-	int NumTracks = FFMS_GetNumTracksI(Indexer);
-
-	for (int i=0; i<NumTracks; i++) {
-		if (FFMS_GetTrackTypeI(Indexer, i) == Type) {
-			if (auto CodecName = FFMS_GetCodecNameI(Indexer, i))
-				TrackList[i] = CodecName;
-		}
-	}
-	return TrackList;
+std::map<int, std::string> FFmpegSourceProvider::GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type)
+{
+    std::map<int, std::string> TrackList;
+    int NumTracks = FFMS_GetNumTracksI(Indexer);
+
+    for (int i = 0; i < NumTracks; i++) {
+        if (FFMS_GetTrackTypeI(Indexer, i) == Type) {
+            if (auto CodecName = FFMS_GetCodecNameI(Indexer, i))
+                TrackList[i] = CodecName;
+        }
+    }
+    return TrackList;
 }
 
 FFmpegSourceProvider::TrackSelection
 FFmpegSourceProvider::AskForTrackSelection(const std::map<int, std::string> &TrackList,
-                                           FFMS_TrackType Type) {
-	std::vector<int> TrackNumbers;
-	wxArrayString Choices;
-
-	for (auto const& track : TrackList) {
-		Choices.Add(agi::wxformat(_("Track %02d: %s"), track.first, track.second));
-		TrackNumbers.push_back(track.first);
-	}
-
-	int Choice = wxGetSingleChoiceIndex(
-		Type == FFMS_TYPE_VIDEO ? _("Multiple video tracks detected, please choose the one you wish to load:") : _("Multiple audio tracks detected, please choose the one you wish to load:"),
-		Type == FFMS_TYPE_VIDEO ? _("Choose video track") : _("Choose audio track"),
-		Choices);
-
-	if (Choice < 0)
-		return TrackSelection::None;
-	return static_cast<TrackSelection>(TrackNumbers[Choice]);
+        FFMS_TrackType Type)
+{
+    std::vector<int> TrackNumbers;
+    wxArrayString Choices;
+
+    for (auto const &track : TrackList) {
+        Choices.Add(agi::wxformat(_("Track %02d: %s"), track.first, track.second));
+        TrackNumbers.push_back(track.first);
+    }
+
+    int Choice = wxGetSingleChoiceIndex(
+                     Type == FFMS_TYPE_VIDEO ? _("Multiple video tracks detected, please choose the one you wish to load:") : _("Multiple audio tracks detected, please choose the one you wish to load:"),
+                     Type == FFMS_TYPE_VIDEO ? _("Choose video track") : _("Choose audio track"),
+                     Choices);
+
+    if (Choice < 0)
+        return TrackSelection::None;
+    return static_cast<TrackSelection>(TrackNumbers[Choice]);
 }
 
 /// @brief Set ffms2 log level according to setting in config.dat
-void FFmpegSourceProvider::SetLogLevel() {
-	auto LogLevel = OPT_GET("Provider/FFmpegSource/Log Level")->GetString();
-	boost::to_lower(LogLevel);
-
-	if (LogLevel == "panic")
-		FFMS_SetLogLevel(FFMS_LOG_PANIC);
-	else if (LogLevel == "fatal")
-		FFMS_SetLogLevel(FFMS_LOG_FATAL);
-	else if (LogLevel == "error")
-		FFMS_SetLogLevel(FFMS_LOG_ERROR);
-	else if (LogLevel == "warning")
-		FFMS_SetLogLevel(FFMS_LOG_WARNING);
-	else if (LogLevel == "info")
-		FFMS_SetLogLevel(FFMS_LOG_INFO);
-	else if (LogLevel == "verbose")
-		FFMS_SetLogLevel(FFMS_LOG_VERBOSE);
-	else if (LogLevel == "debug")
-		FFMS_SetLogLevel(FFMS_LOG_DEBUG);
-	else
-		FFMS_SetLogLevel(FFMS_LOG_QUIET);
+void FFmpegSourceProvider::SetLogLevel()
+{
+    auto LogLevel = OPT_GET("Provider/FFmpegSource/Log Level")->GetString();
+    boost::to_lower(LogLevel);
+
+    if (LogLevel == "panic")
+        FFMS_SetLogLevel(FFMS_LOG_PANIC);
+    else if (LogLevel == "fatal")
+        FFMS_SetLogLevel(FFMS_LOG_FATAL);
+    else if (LogLevel == "error")
+        FFMS_SetLogLevel(FFMS_LOG_ERROR);
+    else if (LogLevel == "warning")
+        FFMS_SetLogLevel(FFMS_LOG_WARNING);
+    else if (LogLevel == "info")
+        FFMS_SetLogLevel(FFMS_LOG_INFO);
+    else if (LogLevel == "verbose")
+        FFMS_SetLogLevel(FFMS_LOG_VERBOSE);
+    else if (LogLevel == "debug")
+        FFMS_SetLogLevel(FFMS_LOG_DEBUG);
+    else
+        FFMS_SetLogLevel(FFMS_LOG_QUIET);
 }
 
-FFMS_IndexErrorHandling FFmpegSourceProvider::GetErrorHandlingMode() {
-	auto Mode = OPT_GET("Provider/Audio/FFmpegSource/Decode Error Handling")->GetString();
-	boost::to_lower(Mode);
-
-	if (Mode == "ignore")
-		return FFMS_IEH_IGNORE;
-	if (Mode == "clear")
-		return FFMS_IEH_CLEAR_TRACK;
-	if (Mode == "stop")
-		return FFMS_IEH_STOP_TRACK;
-	if (Mode == "abort")
-		return FFMS_IEH_ABORT;
-	return FFMS_IEH_STOP_TRACK; // questionable default?
+FFMS_IndexErrorHandling FFmpegSourceProvider::GetErrorHandlingMode()
+{
+    auto Mode = OPT_GET("Provider/Audio/FFmpegSource/Decode Error Handling")->GetString();
+    boost::to_lower(Mode);
+
+    if (Mode == "ignore")
+        return FFMS_IEH_IGNORE;
+    if (Mode == "clear")
+        return FFMS_IEH_CLEAR_TRACK;
+    if (Mode == "stop")
+        return FFMS_IEH_STOP_TRACK;
+    if (Mode == "abort")
+        return FFMS_IEH_ABORT;
+    return FFMS_IEH_STOP_TRACK; // questionable default?
 }
 
 /// @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.
-agi::fs::path FFmpegSourceProvider::GetCacheFilename(agi::fs::path const& filename) {
-	// Get the size of the file to be hashed
-	uintmax_t len = agi::fs::Size(filename);
+agi::fs::path FFmpegSourceProvider::GetCacheFilename(agi::fs::path const &filename)
+{
+    // Get the size of the file to be hashed
+    uintmax_t len = agi::fs::Size(filename);
 
-	// Get the hash of the filename
-	boost::crc_32_type hash;
-	hash.process_bytes(filename.string().c_str(), filename.string().size());
+    // Get the hash of the filename
+    boost::crc_32_type hash;
+    hash.process_bytes(filename.string().c_str(), filename.string().size());
 
-	// Generate the filename
-	auto result = config::path->Decode("?local/ffms2cache/" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".ffindex");
+    // Generate the filename
+    auto result = config::path->Decode("?local/ffms2cache/" + std::to_string(hash.checksum()) + "_" + std::to_string(len) + "_" + std::to_string(agi::fs::ModifiedTime(filename)) + ".ffindex");
 
-	// Ensure that folder exists
-	agi::fs::CreateDirectory(result.parent_path());
+    // Ensure that folder exists
+    agi::fs::CreateDirectory(result.parent_path());
 
-	return result;
+    return result;
 }
 
-void FFmpegSourceProvider::CleanCache() {
-	::CleanCache(config::path->Decode("?local/ffms2cache/"),
-		"*.ffindex",
-		OPT_GET("Provider/FFmpegSource/Cache/Size")->GetInt(),
-		OPT_GET("Provider/FFmpegSource/Cache/Files")->GetInt());
+void FFmpegSourceProvider::CleanCache()
+{
+    ::CleanCache(config::path->Decode("?local/ffms2cache/"),
+                 "*.ffindex",
+                 OPT_GET("Provider/FFmpegSource/Cache/Size")->GetInt(),
+                 OPT_GET("Provider/FFmpegSource/Cache/Files")->GetInt());
 }
 
 #endif // WITH_FFMS2
diff --git a/src/ffmpegsource_common.h b/src/ffmpegsource_common.h
index 383d5baa2439aeca8e52611c4c57d5827b04d7cf..33d9b00db3856c3f5296a29f3f8ab4cc8c9cb2b5 100644
--- a/src/ffmpegsource_common.h
+++ b/src/ffmpegsource_common.h
@@ -45,30 +45,30 @@ namespace agi { class BackgroundRunner; }
 /// @class FFmpegSourceProvider
 /// @brief Base class for FFMS2 source providers; contains common functions etc
 class FFmpegSourceProvider {
-	friend class FFmpegSourceCacheCleaner;
-	agi::BackgroundRunner *br;
+    friend class FFmpegSourceCacheCleaner;
+    agi::BackgroundRunner *br;
 
 public:
-	FFmpegSourceProvider(agi::BackgroundRunner *br);
+    FFmpegSourceProvider(agi::BackgroundRunner *br);
 
-	// X11 is wonderful
+    // X11 is wonderful
 #undef None
 
-	enum class TrackSelection : int {
-		None = -1,
-		All = -2
-	};
+    enum class TrackSelection : int {
+        None = -1,
+        All = -2
+    };
 
-	void CleanCache();
+    void CleanCache();
 
-	FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, agi::fs::path const& Cachename,
-		                   TrackSelection Track,
-		                   FFMS_IndexErrorHandling IndexEH);
-	std::map<int, std::string> GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type);
-	TrackSelection AskForTrackSelection(const std::map<int, std::string>& TrackList, FFMS_TrackType Type);
-	agi::fs::path GetCacheFilename(agi::fs::path const& filename);
-	void SetLogLevel();
-	FFMS_IndexErrorHandling GetErrorHandlingMode();
+    FFMS_Index *DoIndexing(FFMS_Indexer *Indexer, agi::fs::path const &Cachename,
+                           TrackSelection Track,
+                           FFMS_IndexErrorHandling IndexEH);
+    std::map<int, std::string> GetTracksOfType(FFMS_Indexer *Indexer, FFMS_TrackType Type);
+    TrackSelection AskForTrackSelection(const std::map<int, std::string> &TrackList, FFMS_TrackType Type);
+    agi::fs::path GetCacheFilename(agi::fs::path const &filename);
+    void SetLogLevel();
+    FFMS_IndexErrorHandling GetErrorHandlingMode();
 };
 
 #endif /* WITH_FFMS2 */
diff --git a/src/fft.cpp b/src/fft.cpp
index f8eff5afef4fbd26ac5282ab65b69b253f846a7b..16e1651c60e354d5729be39daa5d2be43bbdda1e 100644
--- a/src/fft.cpp
+++ b/src/fft.cpp
@@ -41,131 +41,138 @@
 
 #include <cmath>
 
-void FFT::DoTransform (size_t n_samples,float *input,float *output_r,float *output_i,bool inverse) {
-	if (!IsPowerOfTwo(n_samples))
-		throw agi::InternalError("FFT requires power of two input.");
-
-	// Inverse transform
-	float angle_num = 2.0f * 3.1415926535897932384626433832795f;
-	if (inverse) angle_num = -angle_num;
-
-	// Variables
-	unsigned int i, j, k, n;
-	float tr, ti;
-
-	// Calculate needed bits
-	unsigned int NumBits;
-	NumBits = NumberOfBitsNeeded(n_samples);
-
-	// Copy samples to output buffers
-	for (i=0;i<n_samples;i++) {
-		j = ReverseBits (i,NumBits);
-		output_r[j] = input[i];
-		output_i[j] = 0.0f;
-	}
-
-	unsigned int BlockEnd = 1;
-	unsigned int BlockSize;
-	for (BlockSize = 2;BlockSize<=n_samples;BlockSize<<=1) {
-		// Calculate variables for this iteration
-		float delta_angle = angle_num / (float)BlockSize;
-		float sm2 = sin (-2 * delta_angle);
-		float sm1 = sin (-delta_angle);
-		float cm2 = cos (-2 * delta_angle);
-		float cm1 = cos (-delta_angle);
-		float w = 2 * cm1;
-		float ar0, ar1, ar2, ai0, ai1, ai2;
-
-		// Apply for every sample
-		for(i=0;i<n_samples;i+=BlockSize) {
-			ar1 = cm1;
-			ar2 = cm2;
-			ai1 = sm1;
-			ai2 = sm2;
-
-			for (j=i,n=0;n<BlockEnd;j++,n++) {
-				k = j + BlockEnd;
-
-				ar0 = w*ar1 - ar2;
-				ai0 = w*ai1 - ai2;
-				ar2 = ar1;
-				ai2 = ai1;
-				ar1 = ar0;
-				ai1 = ai0;
-
-				tr = ar0*output_r[k] - ai0*output_i[k];
-				ti = ar0*output_i[k] + ai0*output_r[k];
-
-				output_r[k] = output_r[j] - tr;
-				output_i[k] = output_i[j] - ti;
-
-				output_r[j] += tr;
-				output_i[j] += ti;
-			}
-		}
-
-		// Set next block end to current size
-		BlockEnd = BlockSize;
-	}
-
-	// Divide everything by number of samples if it's an inverse transform
-	if (inverse) {
-		float denom = 1.0f/(float)n_samples;
-		for (i=0;i<n_samples;i++) {
-			output_r[i] *= denom;
-			output_i[i] *= denom;
-		}
-	}
+void FFT::DoTransform (size_t n_samples, float *input, float *output_r, float *output_i, bool inverse)
+{
+    if (!IsPowerOfTwo(n_samples))
+        throw agi::InternalError("FFT requires power of two input.");
+
+    // Inverse transform
+    float angle_num = 2.0f * 3.1415926535897932384626433832795f;
+    if (inverse) angle_num = -angle_num;
+
+    // Variables
+    unsigned int i, j, k, n;
+    float tr, ti;
+
+    // Calculate needed bits
+    unsigned int NumBits;
+    NumBits = NumberOfBitsNeeded(n_samples);
+
+    // Copy samples to output buffers
+    for (i = 0; i < n_samples; i++) {
+        j = ReverseBits (i, NumBits);
+        output_r[j] = input[i];
+        output_i[j] = 0.0f;
+    }
+
+    unsigned int BlockEnd = 1;
+    unsigned int BlockSize;
+    for (BlockSize = 2; BlockSize <= n_samples; BlockSize <<= 1) {
+        // Calculate variables for this iteration
+        float delta_angle = angle_num / (float)BlockSize;
+        float sm2 = sin (-2 * delta_angle);
+        float sm1 = sin (-delta_angle);
+        float cm2 = cos (-2 * delta_angle);
+        float cm1 = cos (-delta_angle);
+        float w = 2 * cm1;
+        float ar0, ar1, ar2, ai0, ai1, ai2;
+
+        // Apply for every sample
+        for (i = 0; i < n_samples; i += BlockSize) {
+            ar1 = cm1;
+            ar2 = cm2;
+            ai1 = sm1;
+            ai2 = sm2;
+
+            for (j = i, n = 0; n < BlockEnd; j++, n++) {
+                k = j + BlockEnd;
+
+                ar0 = w * ar1 - ar2;
+                ai0 = w * ai1 - ai2;
+                ar2 = ar1;
+                ai2 = ai1;
+                ar1 = ar0;
+                ai1 = ai0;
+
+                tr = ar0 * output_r[k] - ai0 * output_i[k];
+                ti = ar0 * output_i[k] + ai0 * output_r[k];
+
+                output_r[k] = output_r[j] - tr;
+                output_i[k] = output_i[j] - ti;
+
+                output_r[j] += tr;
+                output_i[j] += ti;
+            }
+        }
+
+        // Set next block end to current size
+        BlockEnd = BlockSize;
+    }
+
+    // Divide everything by number of samples if it's an inverse transform
+    if (inverse) {
+        float denom = 1.0f / (float)n_samples;
+        for (i = 0; i < n_samples; i++) {
+            output_r[i] *= denom;
+            output_i[i] *= denom;
+        }
+    }
 }
 
-void FFT::Transform(size_t n_samples,float *input,float *output_r,float *output_i) {
-	DoTransform(n_samples,input,output_r,output_i,false);
+void FFT::Transform(size_t n_samples, float *input, float *output_r, float *output_i)
+{
+    DoTransform(n_samples, input, output_r, output_i, false);
 }
 
-void FFT::InverseTransform(size_t n_samples,float *input,float *output_r,float *output_i) {
-	DoTransform(n_samples,input,output_r,output_i,true);
+void FFT::InverseTransform(size_t n_samples, float *input, float *output_r, float *output_i)
+{
+    DoTransform(n_samples, input, output_r, output_i, true);
 }
 
 /// @brief Checks if number is a power of two
-bool FFT::IsPowerOfTwo (unsigned int x) {
-	if (x < 2) return false;
-	if (x & (x-1)) return false;
+bool FFT::IsPowerOfTwo (unsigned int x)
+{
+    if (x < 2) return false;
+    if (x & (x - 1)) return false;
     return true;
 }
 
 /// @brief Bits needed by the FFT
-unsigned int FFT::NumberOfBitsNeeded (unsigned int n_samples) {
-	int i;
+unsigned int FFT::NumberOfBitsNeeded (unsigned int n_samples)
+{
+    int i;
 
-	if (n_samples < 2) {
-		return 0;
-	}
+    if (n_samples < 2) {
+        return 0;
+    }
 
-	for (i=0; ;i++) {
-		if(n_samples & (1 << i)) return i;
+    for (i = 0; ; i++) {
+        if (n_samples & (1 << i)) return i;
     }
 }
 
 /// @brief Get reversed bit position
-unsigned int FFT::ReverseBits (unsigned int index, unsigned int bits) {
-	unsigned int i, rev;
+unsigned int FFT::ReverseBits (unsigned int index, unsigned int bits)
+{
+    unsigned int i, rev;
 
-	for(i=rev=0;i<bits;i++) {
-		rev = (rev << 1) | (index & 1);
-		index >>= 1;
-	}
+    for (i = rev = 0; i < bits; i++) {
+        rev = (rev << 1) | (index & 1);
+        index >>= 1;
+    }
 
-	return rev;
+    return rev;
 }
 
 /// @brief Get frequency at index
-float FFT::FrequencyAtIndex (unsigned int baseFreq, unsigned int n_samples, unsigned int index) {
-	if (index >= n_samples) return 0.0;
-	else if (index <= n_samples/2) {
-		return ((float)index / (float)n_samples * baseFreq);
-	}
-	else {
-		return (-(float)(n_samples-index) / (float)n_samples * baseFreq);
-	}
+float FFT::FrequencyAtIndex (unsigned int baseFreq, unsigned int n_samples, unsigned int index)
+{
+    if (index >= n_samples) return 0.0;
+    else if (index <= n_samples / 2) {
+        return ((float)index / (float)n_samples * baseFreq);
+    }
+    else {
+        return (-(float)(n_samples - index) / (float)n_samples * baseFreq);
+    }
 }
 #endif
diff --git a/src/fft.h b/src/fft.h
index f3f709f49b5d37eee0562b9e4239e4f031e626b4..adda1e7d490f7888fea1d00b1c53b0175ff71d2a 100644
--- a/src/fft.h
+++ b/src/fft.h
@@ -30,13 +30,13 @@
 #include <cstdlib>
 
 class FFT {
-	void DoTransform(size_t n_samples,float *input,float *output_r,float *output_i,bool inverse);
+    void DoTransform(size_t n_samples, float *input, float *output_r, float *output_i, bool inverse);
 
 public:
-	void Transform(size_t n_samples,float *input,float *output_r,float *output_i);
-	void InverseTransform(size_t n_samples,float *input,float *output_r,float *output_i);
-	bool IsPowerOfTwo(unsigned int x);
-	unsigned int NumberOfBitsNeeded(unsigned int n_samples);
-	unsigned int ReverseBits(unsigned int index, unsigned int bits);
-	float FrequencyAtIndex(unsigned int baseFreq, unsigned int n_samples, unsigned int index);
+    void Transform(size_t n_samples, float *input, float *output_r, float *output_i);
+    void InverseTransform(size_t n_samples, float *input, float *output_r, float *output_i);
+    bool IsPowerOfTwo(unsigned int x);
+    unsigned int NumberOfBitsNeeded(unsigned int n_samples);
+    unsigned int ReverseBits(unsigned int index, unsigned int bits);
+    float FrequencyAtIndex(unsigned int baseFreq, unsigned int n_samples, unsigned int index);
 };
diff --git a/src/flyweight_hash.h b/src/flyweight_hash.h
index 3eb3afff2e84d5a203f87d525433f9ae38d08acf..cadd08462391ddc0208b56a8acba85ef771f72af 100644
--- a/src/flyweight_hash.h
+++ b/src/flyweight_hash.h
@@ -19,10 +19,10 @@
 #include <boost/flyweight.hpp>
 
 namespace std {
-	template <typename T>
-	struct hash<boost::flyweight<T>> {
-		size_t operator()(boost::flyweight<T> const& ss) const {
-			return hash<const void*>()(&ss.get());
-		}
-	};
+template <typename T>
+struct hash<boost::flyweight<T>> {
+    size_t operator()(boost::flyweight<T> const &ss) const {
+        return hash<const void *>()(&ss.get());
+    }
+};
 }
diff --git a/src/font_file_lister.cpp b/src/font_file_lister.cpp
index 813e2ca9678af8ff62a734849fcc6224e60a6ca7..01be4bd9afb753c3f23f6be29a596e900f3d6449 100644
--- a/src/font_file_lister.cpp
+++ b/src/font_file_lister.cpp
@@ -31,212 +31,218 @@
 #include <wx/intl.h>
 
 namespace {
-wxString format_missing(wxString const& str) {
-	wxString printable;
-	wxString unprintable;
-	for (wxUniChar c : str) {
-		if (!u_isUWhiteSpace(c.GetValue()))
-			printable += c;
-		else {
-			unprintable += fmt_wx("\n - U+%04X ", c.GetValue());
-			UErrorCode ec;
-			char buf[1024];
-			auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec);
-			if (len != 0 && U_SUCCESS(ec))
-				unprintable += to_wx(buf);
-			if (c.GetValue() == 0xA0)
-				unprintable += " (\\h)";
-		}
-	}
-
-	return printable + unprintable;
+wxString format_missing(wxString const &str)
+{
+    wxString printable;
+    wxString unprintable;
+    for (wxUniChar c : str) {
+        if (!u_isUWhiteSpace(c.GetValue()))
+            printable += c;
+        else {
+            unprintable += fmt_wx("\n - U+%04X ", c.GetValue());
+            UErrorCode ec;
+            char buf[1024];
+            auto len = u_charName(c.GetValue(), U_EXTENDED_CHAR_NAME, buf, sizeof buf, &ec);
+            if (len != 0 && U_SUCCESS(ec))
+                unprintable += to_wx(buf);
+            if (c.GetValue() == 0xA0)
+                unprintable += " (\\h)";
+        }
+    }
+
+    return printable + unprintable;
 }
 }
 
 FontCollector::FontCollector(FontCollectorStatusCallback status_callback)
-: status_callback(std::move(status_callback))
-, lister(this->status_callback)
+    : status_callback(std::move(status_callback))
+    , lister(this->status_callback)
 {
 }
 
-void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index) {
-	if (line->Comment) return;
-
-	auto style_it = styles.find(line->Style);
-	if (style_it == end(styles)) {
-		status_callback(fmt_tl("Style '%s' does not exist\n", line->Style), 2);
-		++missing;
-		return;
-	}
-
-	StyleInfo style = style_it->second;
-	StyleInfo initial = style;
-
-	bool overriden = false;
-
-	for (auto& block : line->ParseTags()) {
-		switch (block->GetType()) {
-		case AssBlockType::OVERRIDE:
-			for (auto const& tag : static_cast<AssDialogueBlockOverride&>(*block).Tags) {
-				if (tag.Name == "\\r") {
-					style = styles[tag.Params[0].Get(line->Style.get())];
-					overriden = false;
-				}
-				else if (tag.Name == "\\b") {
-					style.bold = tag.Params[0].Get(initial.bold);
-					overriden = true;
-				}
-				else if (tag.Name == "\\i") {
-					style.italic = tag.Params[0].Get(initial.italic);
-					overriden = true;
-				}
-				else if (tag.Name == "\\fn") {
-					style.facename = tag.Params[0].Get(initial.facename);
-					overriden = true;
-				}
-			}
-			break;
-		case AssBlockType::PLAIN: {
-			auto text = block->GetText();
-
-			if (text.empty())
-				continue;
-
-			auto& usage = used_styles[style];
-
-			if (overriden) {
-				auto& lines = usage.lines;
-				if (lines.empty() || lines.back() != index)
-					lines.push_back(index);
-			}
-
-			auto& chars = usage.chars;
-			auto size = static_cast<int>(text.size());
-			for (int i = 0; i < size; ) {
-				if (text[i] == '\\' && i + 1 < size) {
-					char next = text[++i];
-					if (next == 'N' || next == 'n') {
-						++i;
-						continue;
-					}
-					if (next == 'h') {
-						++i;
-						chars.push_back(0xA0);
-						continue;
-					}
-
-					chars.push_back('\\');
-					continue;
-				}
-
-				UChar32 c;
-				U8_NEXT(&text[0], i, size, c);
-				chars.push_back(c);
-			}
-
-			sort(begin(chars), end(chars));
-			chars.erase(unique(chars.begin(), chars.end()), chars.end());
-			break;
-		}
-		case AssBlockType::DRAWING:
-		case AssBlockType::COMMENT:
-			break;
-		}
-	}
+void FontCollector::ProcessDialogueLine(const AssDialogue *line, int index)
+{
+    if (line->Comment) return;
+
+    auto style_it = styles.find(line->Style);
+    if (style_it == end(styles)) {
+        status_callback(fmt_tl("Style '%s' does not exist\n", line->Style), 2);
+        ++missing;
+        return;
+    }
+
+    StyleInfo style = style_it->second;
+    StyleInfo initial = style;
+
+    bool overriden = false;
+
+    for (auto &block : line->ParseTags()) {
+        switch (block->GetType()) {
+        case AssBlockType::OVERRIDE:
+            for (auto const &tag : static_cast<AssDialogueBlockOverride &>(*block).Tags) {
+                if (tag.Name == "\\r") {
+                    style = styles[tag.Params[0].Get(line->Style.get())];
+                    overriden = false;
+                }
+                else if (tag.Name == "\\b") {
+                    style.bold = tag.Params[0].Get(initial.bold);
+                    overriden = true;
+                }
+                else if (tag.Name == "\\i") {
+                    style.italic = tag.Params[0].Get(initial.italic);
+                    overriden = true;
+                }
+                else if (tag.Name == "\\fn") {
+                    style.facename = tag.Params[0].Get(initial.facename);
+                    overriden = true;
+                }
+            }
+            break;
+        case AssBlockType::PLAIN: {
+            auto text = block->GetText();
+
+            if (text.empty())
+                continue;
+
+            auto &usage = used_styles[style];
+
+            if (overriden) {
+                auto &lines = usage.lines;
+                if (lines.empty() || lines.back() != index)
+                    lines.push_back(index);
+            }
+
+            auto &chars = usage.chars;
+            auto size = static_cast<int>(text.size());
+            for (int i = 0; i < size; ) {
+                if (text[i] == '\\' && i + 1 < size) {
+                    char next = text[++i];
+                    if (next == 'N' || next == 'n') {
+                        ++i;
+                        continue;
+                    }
+                    if (next == 'h') {
+                        ++i;
+                        chars.push_back(0xA0);
+                        continue;
+                    }
+
+                    chars.push_back('\\');
+                    continue;
+                }
+
+                UChar32 c;
+                U8_NEXT(&text[0], i, size, c);
+                chars.push_back(c);
+            }
+
+            sort(begin(chars), end(chars));
+            chars.erase(unique(chars.begin(), chars.end()), chars.end());
+            break;
+        }
+        case AssBlockType::DRAWING:
+        case AssBlockType::COMMENT:
+            break;
+        }
+    }
 }
 
-void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const& style) {
-	if (style.second.chars.empty()) return;
-
-	auto res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
-
-	if (res.paths.empty()) {
-		status_callback(fmt_tl("Could not find font '%s'\n", style.first.facename), 2);
-		PrintUsage(style.second);
-		++missing;
-	}
-	else {
-		for (auto& elem : res.paths) {
-			elem.make_preferred();
-			if (std::find(begin(results), end(results), elem) == end(results)) {
-				status_callback(fmt_tl("Found '%s' at '%s'\n", style.first.facename, elem), 0);
-				results.push_back(elem);
-			}
-		}
-
-		if (res.fake_bold)
-			status_callback(fmt_tl("'%s' does not have a bold variant.\n", style.first.facename), 3);
-		if (res.fake_italic)
-			status_callback(fmt_tl("'%s' does not have an italic variant.\n", style.first.facename), 3);
-
-		if (res.missing.size()) {
-			if (res.missing.size() > 50)
-				status_callback(fmt_tl("'%s' is missing %d glyphs used.\n", style.first.facename, res.missing.size()), 2);
-			else if (res.missing.size() > 0)
-				status_callback(fmt_tl("'%s' is missing the following glyphs used: %s\n", style.first.facename, format_missing(res.missing)), 2);
-			PrintUsage(style.second);
-			++missing_glyphs;
-		}
-		else if (res.fake_bold || res.fake_italic)
-			PrintUsage(style.second);
-	}
+void FontCollector::ProcessChunk(std::pair<StyleInfo, UsageData> const &style)
+{
+    if (style.second.chars.empty()) return;
+
+    auto res = lister.GetFontPaths(style.first.facename, style.first.bold, style.first.italic, style.second.chars);
+
+    if (res.paths.empty()) {
+        status_callback(fmt_tl("Could not find font '%s'\n", style.first.facename), 2);
+        PrintUsage(style.second);
+        ++missing;
+    }
+    else {
+        for (auto &elem : res.paths) {
+            elem.make_preferred();
+            if (std::find(begin(results), end(results), elem) == end(results)) {
+                status_callback(fmt_tl("Found '%s' at '%s'\n", style.first.facename, elem), 0);
+                results.push_back(elem);
+            }
+        }
+
+        if (res.fake_bold)
+            status_callback(fmt_tl("'%s' does not have a bold variant.\n", style.first.facename), 3);
+        if (res.fake_italic)
+            status_callback(fmt_tl("'%s' does not have an italic variant.\n", style.first.facename), 3);
+
+        if (res.missing.size()) {
+            if (res.missing.size() > 50)
+                status_callback(fmt_tl("'%s' is missing %d glyphs used.\n", style.first.facename, res.missing.size()), 2);
+            else if (res.missing.size() > 0)
+                status_callback(fmt_tl("'%s' is missing the following glyphs used: %s\n", style.first.facename, format_missing(res.missing)), 2);
+            PrintUsage(style.second);
+            ++missing_glyphs;
+        }
+        else if (res.fake_bold || res.fake_italic)
+            PrintUsage(style.second);
+    }
 }
 
-void FontCollector::PrintUsage(UsageData const& data) {
-	if (data.styles.size()) {
-		status_callback(_("Used in styles:\n"), 2);
-		for (auto const& style : data.styles)
-			status_callback(fmt_wx("  - %s\n", style), 2);
-	}
-
-	if (data.lines.size()) {
-		status_callback(_("Used on lines:"), 2);
-		for (int line : data.lines)
-			status_callback(fmt_wx(" %d", line), 2);
-		status_callback("\n", 2);
-	}
-	status_callback("\n", 2);
+void FontCollector::PrintUsage(UsageData const &data)
+{
+    if (data.styles.size()) {
+        status_callback(_("Used in styles:\n"), 2);
+        for (auto const &style : data.styles)
+            status_callback(fmt_wx("  - %s\n", style), 2);
+    }
+
+    if (data.lines.size()) {
+        status_callback(_("Used on lines:"), 2);
+        for (int line : data.lines)
+            status_callback(fmt_wx(" %d", line), 2);
+        status_callback("\n", 2);
+    }
+    status_callback("\n", 2);
 }
 
-std::vector<agi::fs::path> FontCollector::GetFontPaths(const AssFile *file) {
-	missing = 0;
-	missing_glyphs = 0;
-
-	status_callback(_("Parsing file\n"), 0);
-
-	for (auto const& style : file->Styles) {
-		StyleInfo &info = styles[style.name];
-		info.facename = style.font;
-		info.bold     = style.bold;
-		info.italic   = style.italic;
-		used_styles[info].styles.push_back(style.name);
-	}
-
-	int index = 0;
-	for (auto const& diag : file->Events)
-		ProcessDialogueLine(&diag, ++index);
-
-	status_callback(_("Searching for font files\n"), 0);
-	for (auto const& style : used_styles) ProcessChunk(style);
-	status_callback(_("Done\n\n"), 0);
-
-	std::vector<agi::fs::path> paths;
-	paths.reserve(results.size());
-	paths.insert(paths.end(), results.begin(), results.end());
-
-	if (missing == 0)
-		status_callback(_("All fonts found.\n"), 1);
-	else
-		status_callback(fmt_plural(missing, "One font could not be found\n", "%d fonts could not be found.\n", missing), 2);
-	if (missing_glyphs != 0)
-		status_callback(fmt_plural(missing_glyphs,
-			"One font was found, but was missing glyphs used in the script.\n",
-			"%d fonts were found, but were missing glyphs used in the script.\n",
-			missing_glyphs), 2);
-
-	return paths;
+std::vector<agi::fs::path> FontCollector::GetFontPaths(const AssFile *file)
+{
+    missing = 0;
+    missing_glyphs = 0;
+
+    status_callback(_("Parsing file\n"), 0);
+
+    for (auto const &style : file->Styles) {
+        StyleInfo &info = styles[style.name];
+        info.facename = style.font;
+        info.bold     = style.bold;
+        info.italic   = style.italic;
+        used_styles[info].styles.push_back(style.name);
+    }
+
+    int index = 0;
+    for (auto const &diag : file->Events)
+        ProcessDialogueLine(&diag, ++index);
+
+    status_callback(_("Searching for font files\n"), 0);
+    for (auto const &style : used_styles) ProcessChunk(style);
+    status_callback(_("Done\n\n"), 0);
+
+    std::vector<agi::fs::path> paths;
+    paths.reserve(results.size());
+    paths.insert(paths.end(), results.begin(), results.end());
+
+    if (missing == 0)
+        status_callback(_("All fonts found.\n"), 1);
+    else
+        status_callback(fmt_plural(missing, "One font could not be found\n", "%d fonts could not be found.\n", missing), 2);
+    if (missing_glyphs != 0)
+        status_callback(fmt_plural(missing_glyphs,
+                                   "One font was found, but was missing glyphs used in the script.\n",
+                                   "%d fonts were found, but were missing glyphs used in the script.\n",
+                                   missing_glyphs), 2);
+
+    return paths;
 }
 
-bool FontCollector::StyleInfo::operator<(StyleInfo const& rgt) const {
-	return std::tie(facename, bold, italic) < std::tie(rgt.facename, rgt.bold, rgt.italic);
+bool FontCollector::StyleInfo::operator<(StyleInfo const &rgt) const
+{
+    return std::tie(facename, bold, italic) < std::tie(rgt.facename, rgt.bold, rgt.italic);
 }
diff --git a/src/font_file_lister.h b/src/font_file_lister.h
index 9c7cd51ad748b960dfdd39c98e0a1aba30c8212c..634e1f46bc40d34a91ac87bfd88ace694cbf6f2b 100644
--- a/src/font_file_lister.h
+++ b/src/font_file_lister.h
@@ -32,34 +32,34 @@ class AssFile;
 typedef std::function<void (wxString, int)> FontCollectorStatusCallback;
 
 struct CollectionResult {
-	/// Characters which could not be found in any font files
-	wxString missing;
-	/// Paths to the file(s) containing the requested font
-	std::vector<agi::fs::path> paths;
-	bool fake_bold = false;
-	bool fake_italic = false;
+    /// Characters which could not be found in any font files
+    wxString missing;
+    /// Paths to the file(s) containing the requested font
+    std::vector<agi::fs::path> paths;
+    bool fake_bold = false;
+    bool fake_italic = false;
 };
 
 #ifdef _WIN32
 class GdiFontFileLister {
-	std::unordered_multimap<uint32_t, agi::fs::path> index;
-	agi::scoped_holder<HDC> dc;
-	std::string buffer;
+    std::unordered_multimap<uint32_t, agi::fs::path> index;
+    agi::scoped_holder<HDC> dc;
+    std::string buffer;
 
-	bool ProcessLogFont(LOGFONTW const& expected, LOGFONTW const& actual, std::vector<int> const& characters);
+    bool ProcessLogFont(LOGFONTW const &expected, LOGFONTW const &actual, std::vector<int> const &characters);
 
 public:
-	/// Constructor
-	/// @param cb Callback for status logging
-	GdiFontFileLister(FontCollectorStatusCallback &cb);
-
-	/// @brief Get the path to the font with the given styles
-	/// @param facename Name of font face
-	/// @param bold ASS font weight
-	/// @param italic Italic?
-	/// @param characters Characters in this style
-	/// @return Path to the matching font file(s), or empty if not found
-	CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::vector<int> const& characters);
+    /// Constructor
+    /// @param cb Callback for status logging
+    GdiFontFileLister(FontCollectorStatusCallback &cb);
+
+    /// @brief Get the path to the font with the given styles
+    /// @param facename Name of font face
+    /// @param bold ASS font weight
+    /// @param italic Italic?
+    /// @param characters Characters in this style
+    /// @return Path to the matching font file(s), or empty if not found
+    CollectionResult GetFontPaths(std::string const &facename, int bold, bool italic, std::vector<int> const &characters);
 };
 
 using FontFileLister = GdiFontFileLister;
@@ -67,15 +67,15 @@ using FontFileLister = GdiFontFileLister;
 #elif defined(__APPLE__)
 
 struct CoreTextFontFileLister {
-	CoreTextFontFileLister(FontCollectorStatusCallback &) {}
-
-	/// @brief Get the path to the font with the given styles
-	/// @param facename Name of font face
-	/// @param bold ASS font weight
-	/// @param italic Italic?
-	/// @param characters Characters in this style
-	/// @return Path to the matching font file(s), or empty if not found
-	CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::vector<int> const& characters);
+    CoreTextFontFileLister(FontCollectorStatusCallback &) {}
+
+    /// @brief Get the path to the font with the given styles
+    /// @param facename Name of font face
+    /// @param bold ASS font weight
+    /// @param italic Italic?
+    /// @param characters Characters in this style
+    /// @return Path to the matching font file(s), or empty if not found
+    CollectionResult GetFontPaths(std::string const &facename, int bold, bool italic, std::vector<int> const &characters);
 };
 
 using FontFileLister = CoreTextFontFileLister;
@@ -88,26 +88,26 @@ typedef struct _FcFontSet FcFontSet;
 /// @class FontConfigFontFileLister
 /// @brief fontconfig powered font lister
 class FontConfigFontFileLister {
-	agi::scoped_holder<FcConfig*> config;
-
-	/// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
-	/// @param family font fullname
-	/// @param bold weight attribute
-	/// @param italic italic attribute
-	/// @return font set
-	FcFontSet *MatchFullname(const char *family, int weight, int slant);
+    agi::scoped_holder<FcConfig *> config;
+
+    /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
+    /// @param family font fullname
+    /// @param bold weight attribute
+    /// @param italic italic attribute
+    /// @return font set
+    FcFontSet *MatchFullname(const char *family, int weight, int slant);
 public:
-	/// Constructor
-	/// @param cb Callback for status logging
-	FontConfigFontFileLister(FontCollectorStatusCallback &cb);
-
-	/// @brief Get the path to the font with the given styles
-	/// @param facename Name of font face
-	/// @param bold ASS font weight
-	/// @param italic Italic?
-	/// @param characters Characters in this style
-	/// @return Path to the matching font file(s), or empty if not found
-	CollectionResult GetFontPaths(std::string const& facename, int bold, bool italic, std::vector<int> const& characters);
+    /// Constructor
+    /// @param cb Callback for status logging
+    FontConfigFontFileLister(FontCollectorStatusCallback &cb);
+
+    /// @brief Get the path to the font with the given styles
+    /// @param facename Name of font face
+    /// @param bold ASS font weight
+    /// @param italic Italic?
+    /// @param characters Characters in this style
+    /// @return Path to the matching font file(s), or empty if not found
+    CollectionResult GetFontPaths(std::string const &facename, int bold, bool italic, std::vector<int> const &characters);
 };
 
 using FontFileLister = FontConfigFontFileLister;
@@ -116,55 +116,55 @@ using FontFileLister = FontConfigFontFileLister;
 /// @class FontCollector
 /// @brief Class which collects the paths to all fonts used in a script
 class FontCollector {
-	/// All data needed to find the font file used to render text
-	struct StyleInfo {
-		std::string facename;
-		int bold;
-		bool italic;
-		bool operator<(StyleInfo const& rgt) const;
-	};
-
-	/// Data about where each style is used
-	struct UsageData {
-		std::vector<int> chars;          ///< Characters used in this style which glyphs will be needed for
-		std::vector<int> lines;          ///< Lines on which this style is used via overrides
-		std::vector<std::string> styles; ///< ASS styles which use this style
-	};
-
-	/// Message callback provider by caller
-	FontCollectorStatusCallback status_callback;
-
-	FontFileLister lister;
-
-	/// The set of all glyphs used in the file
-	std::map<StyleInfo, UsageData> used_styles;
-	/// Style name -> ASS style definition
-	std::map<std::string, StyleInfo> styles;
-	/// Paths to found required font files
-	std::vector<agi::fs::path> results;
-	/// Number of fonts which could not be found
-	int missing = 0;
-	/// Number of fonts which were found, but did not contain all used glyphs
-	int missing_glyphs = 0;
-
-	/// Gather all of the unique styles with text on a line
-	void ProcessDialogueLine(const AssDialogue *line, int index);
-
-	/// Get the font for a single style
-	void ProcessChunk(std::pair<StyleInfo, UsageData> const& style);
-
-	/// Print the lines and styles on which a missing font is used
-	void PrintUsage(UsageData const& data);
+    /// All data needed to find the font file used to render text
+    struct StyleInfo {
+        std::string facename;
+        int bold;
+        bool italic;
+        bool operator<(StyleInfo const &rgt) const;
+    };
+
+    /// Data about where each style is used
+    struct UsageData {
+        std::vector<int> chars;          ///< Characters used in this style which glyphs will be needed for
+        std::vector<int> lines;          ///< Lines on which this style is used via overrides
+        std::vector<std::string> styles; ///< ASS styles which use this style
+    };
+
+    /// Message callback provider by caller
+    FontCollectorStatusCallback status_callback;
+
+    FontFileLister lister;
+
+    /// The set of all glyphs used in the file
+    std::map<StyleInfo, UsageData> used_styles;
+    /// Style name -> ASS style definition
+    std::map<std::string, StyleInfo> styles;
+    /// Paths to found required font files
+    std::vector<agi::fs::path> results;
+    /// Number of fonts which could not be found
+    int missing = 0;
+    /// Number of fonts which were found, but did not contain all used glyphs
+    int missing_glyphs = 0;
+
+    /// Gather all of the unique styles with text on a line
+    void ProcessDialogueLine(const AssDialogue *line, int index);
+
+    /// Get the font for a single style
+    void ProcessChunk(std::pair<StyleInfo, UsageData> const &style);
+
+    /// Print the lines and styles on which a missing font is used
+    void PrintUsage(UsageData const &data);
 
 public:
-	/// Constructor
-	/// @param status_callback Function to pass status updates to
-	/// @param lister The actual font file lister
-	FontCollector(FontCollectorStatusCallback status_callback);
-
-	/// @brief Get a list of the locations of all font files used in the file
-	/// @param file Lines in the subtitle file to check
-	/// @param status Callback function for messages
-	/// @return List of paths to fonts
-	std::vector<agi::fs::path> GetFontPaths(const AssFile *file);
+    /// Constructor
+    /// @param status_callback Function to pass status updates to
+    /// @param lister The actual font file lister
+    FontCollector(FontCollectorStatusCallback status_callback);
+
+    /// @brief Get a list of the locations of all font files used in the file
+    /// @param file Lines in the subtitle file to check
+    /// @param status Callback function for messages
+    /// @return List of paths to fonts
+    std::vector<agi::fs::path> GetFontPaths(const AssFile *file);
 };
diff --git a/src/font_file_lister_fontconfig.cpp b/src/font_file_lister_fontconfig.cpp
index 408d596eefaa77371afdf78904093a4a72df6c53..cfc94f1a9bc72fe58b469c2e60918097ebfe9d4f 100644
--- a/src/font_file_lister_fontconfig.cpp
+++ b/src/font_file_lister_fontconfig.cpp
@@ -25,100 +25,103 @@
 #include <wx/intl.h>
 
 namespace {
-bool pattern_matches(FcPattern *pat, const char *field, std::string const& name) {
-	FcChar8 *str;
-	for (int i = 0; FcPatternGetString(pat, field, i, &str) == FcResultMatch; ++i) {
-		std::string sstr((char *)str);
-		boost::to_lower(sstr);
-		if (sstr == name)
-			return true;
-	}
-	return false;
+bool pattern_matches(FcPattern *pat, const char *field, std::string const &name)
+{
+    FcChar8 *str;
+    for (int i = 0; FcPatternGetString(pat, field, i, &str) == FcResultMatch; ++i) {
+        std::string sstr((char *)str);
+        boost::to_lower(sstr);
+        if (sstr == name)
+            return true;
+    }
+    return false;
 }
 
-void find_font(FcFontSet *src, FcFontSet *dst, std::string const& family) {
-	if (!src) return;
+void find_font(FcFontSet *src, FcFontSet *dst, std::string const &family)
+{
+    if (!src) return;
 
-	for (FcPattern *pat : boost::make_iterator_range(&src->fonts[0], &src->fonts[src->nfont])) {
-		int val;
-		if (FcPatternGetBool(pat, FC_OUTLINE, 0, &val) != FcResultMatch || val != FcTrue) continue;
+    for (FcPattern *pat : boost::make_iterator_range(&src->fonts[0], &src->fonts[src->nfont])) {
+        int val;
+        if (FcPatternGetBool(pat, FC_OUTLINE, 0, &val) != FcResultMatch || val != FcTrue) continue;
 
-		if (pattern_matches(pat, FC_FULLNAME, family) || pattern_matches(pat, FC_FAMILY, family))
-			FcFontSetAdd(dst, FcPatternDuplicate(pat));
-	}
+        if (pattern_matches(pat, FC_FULLNAME, family) || pattern_matches(pat, FC_FAMILY, family))
+            FcFontSetAdd(dst, FcPatternDuplicate(pat));
+    }
 }
 
 }
 
 FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback &cb)
-: config(FcInitLoadConfig(), FcConfigDestroy)
+    : config(FcInitLoadConfig(), FcConfigDestroy)
 {
-	cb(_("Updating font cache\n"), 0);
-	FcConfigBuildFonts(config);
+    cb(_("Updating font cache\n"), 0);
+    FcConfigBuildFonts(config);
 }
 
-CollectionResult FontConfigFontFileLister::GetFontPaths(std::string const& facename, int bold, bool italic, std::vector<int> const& characters) {
-	CollectionResult ret;
-
-	std::string family = facename[0] == '@' ? facename.substr(1) : facename;
-	boost::to_lower(family);
-
-	int weight = bold == 0 ? 80 :
-	             bold == 1 ? 200 :
-	                         bold;
-	int slant  = italic ? 110 : 0;
-
-	// Create a fontconfig pattern to match the desired weight/slant
-	agi::scoped_holder<FcPattern*> pat(FcPatternCreate(), FcPatternDestroy);
-	if (!pat) return ret;
-
-	FcPatternAddBool(pat, FC_OUTLINE, true);
-	FcPatternAddInteger(pat, FC_SLANT, slant);
-	FcPatternAddInteger(pat, FC_WEIGHT, weight);
-
-	FcDefaultSubstitute(pat);
-	if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret;
-
-	// Create a font set with only correctly named fonts
-	// This is needed because the patterns returned by font matching only
-	// include the first family and fullname, so we can't always verify that
-	// we got the actual font we were asking for after the fact
-	agi::scoped_holder<FcFontSet*> fset(FcFontSetCreate(), FcFontSetDestroy);
-	find_font(FcConfigGetFonts(config, FcSetApplication), fset, family);
-	find_font(FcConfigGetFonts(config, FcSetSystem), fset, family);
-
-	// Get the best match from fontconfig
-	FcResult result;
-	FcFontSet *sets[] = { (FcFontSet*)fset };
-
-	agi::scoped_holder<FcFontSet*> matches(FcFontSetSort(config, sets, 1, pat, false, nullptr, &result), FcFontSetDestroy);
-	if (matches->nfont == 0)
-		return ret;
-
-	auto match = matches->fonts[0];
-
-	FcChar8 *file;
-	if(FcPatternGetString(match, FC_FILE, 0, &file) != FcResultMatch)
-		return ret;
-
-	FcCharSet *charset;
-	if (FcPatternGetCharSet(match, FC_CHARSET, 0, &charset) == FcResultMatch) {
-		for (int chr : characters) {
-			if (!FcCharSetHasChar(charset, chr))
-				ret.missing += chr;
-		}
-	}
-
-	if (weight > 80) {
-		int actual_weight = weight;
-		if (FcPatternGetInteger(match, FC_WEIGHT, 0, &actual_weight) == FcResultMatch)
-			ret.fake_bold = actual_weight <= 80;
-	}
-
-	int actual_slant = slant;
-	if (FcPatternGetInteger(match, FC_SLANT, 0, &actual_slant) == FcResultMatch)
-		ret.fake_italic = italic && !actual_slant;
-
-	ret.paths.emplace_back((const char *)file);
-	return ret;
+CollectionResult FontConfigFontFileLister::GetFontPaths(std::string const &facename, int bold, bool italic, std::vector<int> const &characters)
+{
+    CollectionResult ret;
+
+    std::string family = facename[0] == '@' ? facename.substr(1) : facename;
+    boost::to_lower(family);
+
+    int weight = bold == 0 ? 80 :
+                 bold == 1 ? 200 :
+                 bold;
+    int slant  = italic ? 110 : 0;
+
+    // Create a fontconfig pattern to match the desired weight/slant
+    agi::scoped_holder<FcPattern *> pat(FcPatternCreate(), FcPatternDestroy);
+    if (!pat) return ret;
+
+    FcPatternAddBool(pat, FC_OUTLINE, true);
+    FcPatternAddInteger(pat, FC_SLANT, slant);
+    FcPatternAddInteger(pat, FC_WEIGHT, weight);
+
+    FcDefaultSubstitute(pat);
+    if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret;
+
+    // Create a font set with only correctly named fonts
+    // This is needed because the patterns returned by font matching only
+    // include the first family and fullname, so we can't always verify that
+    // we got the actual font we were asking for after the fact
+    agi::scoped_holder<FcFontSet *> fset(FcFontSetCreate(), FcFontSetDestroy);
+    find_font(FcConfigGetFonts(config, FcSetApplication), fset, family);
+    find_font(FcConfigGetFonts(config, FcSetSystem), fset, family);
+
+    // Get the best match from fontconfig
+    FcResult result;
+    FcFontSet *sets[] = { (FcFontSet *)fset };
+
+    agi::scoped_holder<FcFontSet *> matches(FcFontSetSort(config, sets, 1, pat, false, nullptr, &result), FcFontSetDestroy);
+    if (matches->nfont == 0)
+        return ret;
+
+    auto match = matches->fonts[0];
+
+    FcChar8 *file;
+    if (FcPatternGetString(match, FC_FILE, 0, &file) != FcResultMatch)
+        return ret;
+
+    FcCharSet *charset;
+    if (FcPatternGetCharSet(match, FC_CHARSET, 0, &charset) == FcResultMatch) {
+        for (int chr : characters) {
+            if (!FcCharSetHasChar(charset, chr))
+                ret.missing += chr;
+        }
+    }
+
+    if (weight > 80) {
+        int actual_weight = weight;
+        if (FcPatternGetInteger(match, FC_WEIGHT, 0, &actual_weight) == FcResultMatch)
+            ret.fake_bold = actual_weight <= 80;
+    }
+
+    int actual_slant = slant;
+    if (FcPatternGetInteger(match, FC_SLANT, 0, &actual_slant) == FcResultMatch)
+        ret.fake_italic = italic && !actual_slant;
+
+    ret.paths.emplace_back((const char *)file);
+    return ret;
 }
diff --git a/src/font_file_lister_gdi.cpp b/src/font_file_lister_gdi.cpp
index 14ff9261a8ff1db3b5c9fee97f8bb4912cbc7f7a..f5839b58b77c7c4f317dfe56b35ab2055af1e5c9 100644
--- a/src/font_file_lister_gdi.cpp
+++ b/src/font_file_lister_gdi.cpp
@@ -28,272 +28,278 @@
 #include <unicode/utf16.h>
 #include <Usp10.h>
 
-static void read_fonts_from_key(HKEY hkey, agi::fs::path font_dir, std::vector<agi::fs::path> &files) {
-	static const auto fonts_key_name = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
-	
-	HKEY key;
-	auto ret = RegOpenKeyExW(hkey, fonts_key_name, 0, KEY_QUERY_VALUE, &key);
-	if (ret != ERROR_SUCCESS) return;
-	BOOST_SCOPE_EXIT_ALL(=) { RegCloseKey(key); };
+static void read_fonts_from_key(HKEY hkey, agi::fs::path font_dir, std::vector<agi::fs::path> &files)
+{
+    static const auto fonts_key_name = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
+
+    HKEY key;
+    auto ret = RegOpenKeyExW(hkey, fonts_key_name, 0, KEY_QUERY_VALUE, &key);
+    if (ret != ERROR_SUCCESS) return;
+    BOOST_SCOPE_EXIT_ALL( = ) { RegCloseKey(key); };
 
-	DWORD name_buf_size = SHRT_MAX;
-	DWORD data_buf_size = MAX_PATH;
+    DWORD name_buf_size = SHRT_MAX;
+    DWORD data_buf_size = MAX_PATH;
 
-	auto font_name = new wchar_t[name_buf_size];
-	auto font_filename = new wchar_t[data_buf_size];
+    auto font_name = new wchar_t[name_buf_size];
+    auto font_filename = new wchar_t[data_buf_size];
 
-	for (DWORD i = 0;; ++i) {
+    for (DWORD i = 0;; ++i) {
 retry:
-		DWORD name_len = name_buf_size;
-		DWORD data_len = data_buf_size;
-
-		ret = RegEnumValueW(key, i, font_name, &name_len, NULL, NULL, reinterpret_cast<BYTE*>(font_filename), &data_len);
-		if (ret == ERROR_MORE_DATA) {
-			data_buf_size = data_len;
-			delete font_filename;
-			font_filename = new wchar_t[data_buf_size];
-			goto retry;
-		}
-		if (ret == ERROR_NO_MORE_ITEMS) break;
-		if (ret != ERROR_SUCCESS) continue;
-
-		agi::fs::path font_path(font_filename);
-		if (!agi::fs::FileExists(font_path))
-			// Doesn't make a ton of sense to do this with user fonts, but they seem to be stored as full paths anyway
-			font_path = font_dir / font_path;
-		if (agi::fs::FileExists(font_path)) // The path might simply be invalid
-			files.push_back(font_path);
-	}
-
-	delete font_name;
-	delete font_filename;
+        DWORD name_len = name_buf_size;
+        DWORD data_len = data_buf_size;
+
+        ret = RegEnumValueW(key, i, font_name, &name_len, NULL, NULL, reinterpret_cast<BYTE *>(font_filename), &data_len);
+        if (ret == ERROR_MORE_DATA) {
+            data_buf_size = data_len;
+            delete font_filename;
+            font_filename = new wchar_t[data_buf_size];
+            goto retry;
+        }
+        if (ret == ERROR_NO_MORE_ITEMS) break;
+        if (ret != ERROR_SUCCESS) continue;
+
+        agi::fs::path font_path(font_filename);
+        if (!agi::fs::FileExists(font_path))
+            // Doesn't make a ton of sense to do this with user fonts, but they seem to be stored as full paths anyway
+            font_path = font_dir / font_path;
+        if (agi::fs::FileExists(font_path)) // The path might simply be invalid
+            files.push_back(font_path);
+    }
+
+    delete font_name;
+    delete font_filename;
 }
 
 namespace {
-uint32_t murmur3(const char *data, uint32_t len) {
-	static const uint32_t c1 = 0xcc9e2d51;
-	static const uint32_t c2 = 0x1b873593;
-	static const uint32_t r1 = 15;
-	static const uint32_t r2 = 13;
-	static const uint32_t m = 5;
-	static const uint32_t n = 0xe6546b64;
-
-	uint32_t hash = 0;
-
-	const int nblocks = len / 4;
-	auto blocks = reinterpret_cast<const uint32_t *>(data);
-	for (uint32_t i = 0; i * 4 < len; ++i) {
-		uint32_t k = blocks[i];
-		k *= c1;
-		k = _rotl(k, r1);
-		k *= c2;
-
-		hash ^= k;
-		hash = _rotl(hash, r2) * m + n;
-	}
-
-	hash ^= len;
-	hash ^= hash >> 16;
-	hash *= 0x85ebca6b;
-	hash ^= hash >> 13;
-	hash *= 0xc2b2ae35;
-	hash ^= hash >> 16;
-
-	return hash;
+uint32_t murmur3(const char *data, uint32_t len)
+{
+    static const uint32_t c1 = 0xcc9e2d51;
+    static const uint32_t c2 = 0x1b873593;
+    static const uint32_t r1 = 15;
+    static const uint32_t r2 = 13;
+    static const uint32_t m = 5;
+    static const uint32_t n = 0xe6546b64;
+
+    uint32_t hash = 0;
+
+    const int nblocks = len / 4;
+    auto blocks = reinterpret_cast<const uint32_t *>(data);
+    for (uint32_t i = 0; i * 4 < len; ++i) {
+        uint32_t k = blocks[i];
+        k *= c1;
+        k = _rotl(k, r1);
+        k *= c2;
+
+        hash ^= k;
+        hash = _rotl(hash, r2) * m + n;
+    }
+
+    hash ^= len;
+    hash ^= hash >> 16;
+    hash *= 0x85ebca6b;
+    hash ^= hash >> 13;
+    hash *= 0xc2b2ae35;
+    hash ^= hash >> 16;
+
+    return hash;
 }
 
-std::vector<agi::fs::path> get_installed_fonts() {
-	std::vector<agi::fs::path> files;
+std::vector<agi::fs::path> get_installed_fonts()
+{
+    std::vector<agi::fs::path> files;
 
-	wchar_t fdir[MAX_PATH];
-	SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, fdir);
-	agi::fs::path font_dir(fdir);
+    wchar_t fdir[MAX_PATH];
+    SHGetFolderPathW(NULL, CSIDL_FONTS, NULL, 0, fdir);
+    agi::fs::path font_dir(fdir);
 
-	// System fonts
-	read_fonts_from_key(HKEY_LOCAL_MACHINE, font_dir, files);
+    // System fonts
+    read_fonts_from_key(HKEY_LOCAL_MACHINE, font_dir, files);
 
-	// User fonts
-	read_fonts_from_key(HKEY_CURRENT_USER, font_dir, files);
+    // User fonts
+    read_fonts_from_key(HKEY_CURRENT_USER, font_dir, files);
 
-	return files;
+    return files;
 }
 
 using font_index = std::unordered_multimap<uint32_t, agi::fs::path>;
 
-font_index index_fonts(FontCollectorStatusCallback &cb) {
-	font_index hash_to_path;
-	auto fonts = get_installed_fonts();
-	std::unique_ptr<char[]> buffer(new char[1024]);
-	for (auto const& path : fonts) {
-		try {
-			auto stream = agi::io::Open(path, true);
-			stream->read(&buffer[0], 1024);
-			auto hash = murmur3(&buffer[0], stream->tellg());
-			hash_to_path.emplace(hash, path);
-		}
-		catch (agi::Exception const& e) {
-			cb(to_wx(e.GetMessage() + "\n"), 3);
-		}
-	}
-	return hash_to_path;
+font_index index_fonts(FontCollectorStatusCallback &cb)
+{
+    font_index hash_to_path;
+    auto fonts = get_installed_fonts();
+    std::unique_ptr<char[]> buffer(new char[1024]);
+    for (auto const &path : fonts) {
+        try {
+            auto stream = agi::io::Open(path, true);
+            stream->read(&buffer[0], 1024);
+            auto hash = murmur3(&buffer[0], stream->tellg());
+            hash_to_path.emplace(hash, path);
+        }
+        catch (agi::Exception const &e) {
+            cb(to_wx(e.GetMessage() + "\n"), 3);
+        }
+    }
+    return hash_to_path;
 }
 
-void get_font_data(std::string& buffer, HDC dc) {
-	buffer.clear();
-
-	// For ttc files we have to ask for the "ttcf" table to get the complete file
-	DWORD ttcf = 0x66637474;
-	auto size = GetFontData(dc, ttcf, 0, nullptr, 0);
-	if (size == GDI_ERROR) {
-		ttcf = 0;
-		size = GetFontData(dc, 0, 0, nullptr, 0);
-	}
-	if (size == GDI_ERROR || size == 0)
-		return;
-
-	buffer.resize(size);
-	GetFontData(dc, ttcf, 0, &buffer[0], size);
+void get_font_data(std::string &buffer, HDC dc)
+{
+    buffer.clear();
+
+    // For ttc files we have to ask for the "ttcf" table to get the complete file
+    DWORD ttcf = 0x66637474;
+    auto size = GetFontData(dc, ttcf, 0, nullptr, 0);
+    if (size == GDI_ERROR) {
+        ttcf = 0;
+        size = GetFontData(dc, 0, 0, nullptr, 0);
+    }
+    if (size == GDI_ERROR || size == 0)
+        return;
+
+    buffer.resize(size);
+    GetFontData(dc, ttcf, 0, &buffer[0], size);
 }
 }
 
 GdiFontFileLister::GdiFontFileLister(FontCollectorStatusCallback &cb)
-: dc(CreateCompatibleDC(nullptr), [](HDC dc) { DeleteDC(dc); })
+    : dc(CreateCompatibleDC(nullptr), [](HDC dc) { DeleteDC(dc); })
 {
-	cb(_("Updating font cache\n"), 0);
-	index = index_fonts(cb);
+    cb(_("Updating font cache\n"), 0);
+    index = index_fonts(cb);
 }
 
-CollectionResult GdiFontFileLister::GetFontPaths(std::string const& facename, int bold, bool italic, std::vector<int> const& characters) {
-	CollectionResult ret;
-
-	LOGFONTW lf{};
-	lf.lfCharSet = DEFAULT_CHARSET;
-	wcsncpy(lf.lfFaceName, agi::charset::ConvertW(facename).c_str(), LF_FACESIZE);
-	lf.lfItalic = italic ? -1 : 0;
-	lf.lfWeight = bold == 0 ? 400 :
-	              bold == 1 ? 700 :
-	                          bold;
-
-	// Gather all of the styles for the given family name
-	std::vector<LOGFONTW> matches;
-	using type = decltype(matches);
-	EnumFontFamiliesEx(dc, &lf, [](const LOGFONT *lf, const TEXTMETRIC *, DWORD, LPARAM lParam) -> int {
-		reinterpret_cast<type*>(lParam)->push_back(*lf);
-		return 1;
-	}, (LPARAM)&matches, 0);
-
-	if (matches.empty())
-		return ret;
-
-	// If the user asked for a non-regular style, verify that it actually exists
-	if (italic || bold) {
-		bool has_bold = false;
-		bool has_italic = false;
-		bool has_bold_italic = false;
-
-		auto is_italic = [&](LOGFONTW const& lf) {
-			return !italic || lf.lfItalic;
-		};
-		auto is_bold = [&](LOGFONTW const& lf) {
-			return !bold
-				|| (bold == 1 && lf.lfWeight >= 700)
-				|| (bold > 1 && lf.lfWeight > bold);
-		};
-
-		for (auto const& match : matches) {
-			has_bold = has_bold || is_bold(match);
-			has_italic = has_italic || is_italic(match);
-			has_bold_italic = has_bold_italic || (is_bold(match) && is_italic(match));
-		}
-
-		ret.fake_italic = !has_italic;
-		ret.fake_bold = (italic && has_italic ? !has_bold_italic : !has_bold);
-	}
-
-	// Use the family name supplied by EnumFontFamiliesEx as it may be a localized version
-	memcpy(lf.lfFaceName, matches[0].lfFaceName, LF_FACESIZE);
-
-	// Open the font and get the data for it to look up in the index
-	auto hfont = CreateFontIndirectW(&lf);
-	SelectObject(dc, hfont);
-	BOOST_SCOPE_EXIT_ALL(=) {
-		SelectObject(dc, nullptr);
-		DeleteObject(hfont);
-	};
-
-	get_font_data(buffer, dc);
-
-	auto range = index.equal_range(murmur3(buffer.c_str(), std::min<size_t>(buffer.size(), 1024U)));
-	if (range.first == range.second)
-		return ret; // could instead write to a temp dir
-
-	// Compare the full files for each of the fonts with the same prefix
-	std::unique_ptr<char[]> file_buffer(new char[buffer.size()]);
-	for (auto it = range.first; it != range.second; ++it) {
-		auto stream = agi::io::Open(it->second, true);
-		stream->read(&file_buffer[0], buffer.size());
-		if ((size_t)stream->tellg() != buffer.size())
-			continue;
-		if (memcmp(&file_buffer[0], &buffer[0], buffer.size()) == 0) {
-			ret.paths.push_back(it->second);
-			break;
-		}
-	}
-
-	// No fonts actually matched
-	if (ret.paths.empty())
-		return ret;
-
-	// Convert the characters to a utf-16 string
-	std::wstring utf16characters;
-	utf16characters.reserve(characters.size());
-	for (int chr : characters) {
-		if (U16_LENGTH(chr) == 1)
-			utf16characters.push_back(static_cast<wchar_t>(chr));
-		else {
-			utf16characters.push_back(U16_LEAD(chr));
-			utf16characters.push_back(U16_TRAIL(chr));
-		}
-	}
-
-	SCRIPT_CACHE cache = nullptr;
-	std::unique_ptr<WORD[]> indices(new WORD[utf16characters.size()]);
-
-	// First try to check glyph coverage with Uniscribe, since it
-	// handles non-BMP unicode characters
-	auto hr = ScriptGetCMap(dc, &cache, utf16characters.data(),
-		utf16characters.size(), 0, indices.get());
-
-	// Uniscribe doesn't like some types of fonts, so fall back to GDI
-	if (hr == E_HANDLE) {
-		GetGlyphIndicesW(dc, utf16characters.data(), utf16characters.size(),
-			indices.get(), GGI_MARK_NONEXISTING_GLYPHS);
-		for (size_t i = 0; i < utf16characters.size(); ++i) {
-			if (U16_IS_SURROGATE(utf16characters[i]))
-				continue;
-			if (indices[i] == SHRT_MAX)
-				ret.missing += utf16characters[i];
-		}
-	}
-	else if (hr == S_FALSE) {
-		for (size_t i = 0; i < utf16characters.size(); ++i) {
-			// Uniscribe doesn't report glyph indexes for non-BMP characters,
-			// so we have to call ScriptGetCMap on each individual pair to
-			// determine if it's the missing one
-			if (U16_IS_LEAD(utf16characters[i])) {
-				hr = ScriptGetCMap(dc, &cache, &utf16characters[i], 2, 0, &indices[i]);
-				if (hr == S_FALSE) {
-					ret.missing += utf16characters[i];
-					ret.missing += utf16characters[i + 1];
-				}
-				++i;
-			}
-			else if (indices[i] == 0) {
-				ret.missing += utf16characters[i];
-			}
-		}
-	}
-	ScriptFreeCache(&cache);
-
-	return ret;
+CollectionResult GdiFontFileLister::GetFontPaths(std::string const &facename, int bold, bool italic, std::vector<int> const &characters)
+{
+    CollectionResult ret;
+
+    LOGFONTW lf{};
+    lf.lfCharSet = DEFAULT_CHARSET;
+    wcsncpy(lf.lfFaceName, agi::charset::ConvertW(facename).c_str(), LF_FACESIZE);
+    lf.lfItalic = italic ? -1 : 0;
+    lf.lfWeight = bold == 0 ? 400 :
+                  bold == 1 ? 700 :
+                  bold;
+
+    // Gather all of the styles for the given family name
+    std::vector<LOGFONTW> matches;
+    using type = decltype(matches);
+    EnumFontFamiliesEx(dc, &lf, [](const LOGFONT * lf, const TEXTMETRIC *, DWORD, LPARAM lParam) -> int {
+        reinterpret_cast<type *>(lParam)->push_back(*lf);
+        return 1;
+    }, (LPARAM)&matches, 0);
+
+    if (matches.empty())
+        return ret;
+
+    // If the user asked for a non-regular style, verify that it actually exists
+    if (italic || bold) {
+        bool has_bold = false;
+        bool has_italic = false;
+        bool has_bold_italic = false;
+
+        auto is_italic = [&](LOGFONTW const & lf) {
+            return !italic || lf.lfItalic;
+        };
+        auto is_bold = [&](LOGFONTW const & lf) {
+            return !bold
+                   || (bold == 1 && lf.lfWeight >= 700)
+                   || (bold > 1 && lf.lfWeight > bold);
+        };
+
+        for (auto const &match : matches) {
+            has_bold = has_bold || is_bold(match);
+            has_italic = has_italic || is_italic(match);
+            has_bold_italic = has_bold_italic || (is_bold(match) && is_italic(match));
+        }
+
+        ret.fake_italic = !has_italic;
+        ret.fake_bold = (italic && has_italic ? !has_bold_italic : !has_bold);
+    }
+
+    // Use the family name supplied by EnumFontFamiliesEx as it may be a localized version
+    memcpy(lf.lfFaceName, matches[0].lfFaceName, LF_FACESIZE);
+
+    // Open the font and get the data for it to look up in the index
+    auto hfont = CreateFontIndirectW(&lf);
+    SelectObject(dc, hfont);
+    BOOST_SCOPE_EXIT_ALL( = ) {
+        SelectObject(dc, nullptr);
+        DeleteObject(hfont);
+    };
+
+    get_font_data(buffer, dc);
+
+    auto range = index.equal_range(murmur3(buffer.c_str(), std::min<size_t>(buffer.size(), 1024U)));
+    if (range.first == range.second)
+        return ret; // could instead write to a temp dir
+
+    // Compare the full files for each of the fonts with the same prefix
+    std::unique_ptr<char[]> file_buffer(new char[buffer.size()]);
+    for (auto it = range.first; it != range.second; ++it) {
+        auto stream = agi::io::Open(it->second, true);
+        stream->read(&file_buffer[0], buffer.size());
+        if ((size_t)stream->tellg() != buffer.size())
+            continue;
+        if (memcmp(&file_buffer[0], &buffer[0], buffer.size()) == 0) {
+            ret.paths.push_back(it->second);
+            break;
+        }
+    }
+
+    // No fonts actually matched
+    if (ret.paths.empty())
+        return ret;
+
+    // Convert the characters to a utf-16 string
+    std::wstring utf16characters;
+    utf16characters.reserve(characters.size());
+    for (int chr : characters) {
+        if (U16_LENGTH(chr) == 1)
+            utf16characters.push_back(static_cast<wchar_t>(chr));
+        else {
+            utf16characters.push_back(U16_LEAD(chr));
+            utf16characters.push_back(U16_TRAIL(chr));
+        }
+    }
+
+    SCRIPT_CACHE cache = nullptr;
+    std::unique_ptr<WORD[]> indices(new WORD[utf16characters.size()]);
+
+    // First try to check glyph coverage with Uniscribe, since it
+    // handles non-BMP unicode characters
+    auto hr = ScriptGetCMap(dc, &cache, utf16characters.data(),
+                            utf16characters.size(), 0, indices.get());
+
+    // Uniscribe doesn't like some types of fonts, so fall back to GDI
+    if (hr == E_HANDLE) {
+        GetGlyphIndicesW(dc, utf16characters.data(), utf16characters.size(),
+                         indices.get(), GGI_MARK_NONEXISTING_GLYPHS);
+        for (size_t i = 0; i < utf16characters.size(); ++i) {
+            if (U16_IS_SURROGATE(utf16characters[i]))
+                continue;
+            if (indices[i] == SHRT_MAX)
+                ret.missing += utf16characters[i];
+        }
+    }
+    else if (hr == S_FALSE) {
+        for (size_t i = 0; i < utf16characters.size(); ++i) {
+            // Uniscribe doesn't report glyph indexes for non-BMP characters,
+            // so we have to call ScriptGetCMap on each individual pair to
+            // determine if it's the missing one
+            if (U16_IS_LEAD(utf16characters[i])) {
+                hr = ScriptGetCMap(dc, &cache, &utf16characters[i], 2, 0, &indices[i]);
+                if (hr == S_FALSE) {
+                    ret.missing += utf16characters[i];
+                    ret.missing += utf16characters[i + 1];
+                }
+                ++i;
+            }
+            else if (indices[i] == 0) {
+                ret.missing += utf16characters[i];
+            }
+        }
+    }
+    ScriptFreeCache(&cache);
+
+    return ret;
 }
diff --git a/src/format.h b/src/format.h
index 1835665d256334e83ac9d6874684e96ad87a50f8..7fe1f3c8240213670d0259ff44c73fa92bb0c88d 100644
--- a/src/format.h
+++ b/src/format.h
@@ -22,37 +22,40 @@
 namespace agi {
 template<>
 struct writer<char, wxString> {
-	static void write(std::basic_ostream<char>& out, int max_len, wxString const& value) {
-		writer<char, const wxStringCharType *>::write(out, max_len, value.wx_str());
-	}
+    static void write(std::basic_ostream<char> &out, int max_len, wxString const &value) {
+        writer<char, const wxStringCharType *>::write(out, max_len, value.wx_str());
+    }
 };
 
 template<>
 struct writer<wchar_t, wxString> {
-	static void write(std::basic_ostream<wchar_t>& out, int max_len, wxString const& value) {
-		writer<wchar_t, const wxStringCharType *>::write(out, max_len, value.wx_str());
-	}
+    static void write(std::basic_ostream<wchar_t> &out, int max_len, wxString const &value) {
+        writer<wchar_t, const wxStringCharType *>::write(out, max_len, value.wx_str());
+    }
 };
 
 template<typename... Args>
-std::string format(wxString const& fmt, Args&&... args) {
-	boost::interprocess::basic_vectorstream<std::basic_string<char>> out;
-	format(out, (const char *)fmt.utf8_str(), std::forward<Args>(args)...);
-	return out.vector();
+std::string format(wxString const &fmt, Args &&... args)
+{
+    boost::interprocess::basic_vectorstream<std::basic_string<char>> out;
+    format(out, (const char *)fmt.utf8_str(), std::forward<Args>(args)...);
+    return out.vector();
 }
 
 template<typename... Args>
-wxString wxformat(wxString const& fmt, Args&&... args) {
-	boost::interprocess::basic_vectorstream<std::basic_string<wxStringCharType>> out;
-	format(out, fmt.wx_str(), std::forward<Args>(args)...);
-	return out.vector();
+wxString wxformat(wxString const &fmt, Args &&... args)
+{
+    boost::interprocess::basic_vectorstream<std::basic_string<wxStringCharType>> out;
+    format(out, fmt.wx_str(), std::forward<Args>(args)...);
+    return out.vector();
 }
 
 template<typename... Args>
-wxString wxformat(const wxStringCharType *fmt, Args&&... args) {
-	boost::interprocess::basic_vectorstream<std::basic_string<wxStringCharType>> out;
-	format(out, fmt, std::forward<Args>(args)...);
-	return out.vector();
+wxString wxformat(const wxStringCharType *fmt, Args &&... args)
+{
+    boost::interprocess::basic_vectorstream<std::basic_string<wxStringCharType>> out;
+    format(out, fmt, std::forward<Args>(args)...);
+    return out.vector();
 }
 }
 
diff --git a/src/frame_main.cpp b/src/frame_main.cpp
index b35c3296081d054e7e2783b1cff21bd8bd19a8a6..15d7bade9a353c0e0919383a40dae9ae0cf05d13 100644
--- a/src/frame_main.cpp
+++ b/src/frame_main.cpp
@@ -70,7 +70,7 @@
 #include <wx/sysopt.h>
 
 enum {
-	ID_APP_TIMER_STATUSCLEAR = 12002
+    ID_APP_TIMER_STATUSCLEAR = 12002
 };
 
 #ifdef WITH_STARTUPLOG
@@ -81,274 +81,288 @@ enum {
 
 /// Handle files drag and dropped onto Aegisub
 class AegisubFileDropTarget final : public wxFileDropTarget {
-	agi::Context *context;
+    agi::Context *context;
 public:
-	AegisubFileDropTarget(agi::Context *context) : context(context) { }
-	bool OnDropFiles(wxCoord, wxCoord, wxArrayString const& filenames) override {
-		std::vector<agi::fs::path> files;
-		for (wxString const& fn : filenames)
-			files.push_back(from_wx(fn));
-		agi::dispatch::Main().Async([=] { context->project->LoadList(files); });
-		return true;
-	}
+    AegisubFileDropTarget(agi::Context *context) : context(context) { }
+    bool OnDropFiles(wxCoord, wxCoord, wxArrayString const &filenames) override {
+        std::vector<agi::fs::path> files;
+        for (wxString const &fn : filenames)
+            files.push_back(from_wx(fn));
+        agi::dispatch::Main().Async([ = ] { context->project->LoadList(files); });
+        return true;
+    }
 };
 
 FrameMain::FrameMain()
-: wxFrame(nullptr, -1, "", wxDefaultPosition, wxSize(920,700), wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN)
-, context(agi::make_unique<agi::Context>())
+    : wxFrame(nullptr, -1, "", wxDefaultPosition, wxSize(920, 700), wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN)
+    , context(agi::make_unique<agi::Context>())
 {
-	StartupLog("Entering FrameMain constructor");
+    StartupLog("Entering FrameMain constructor");
 
 #ifdef __WXGTK__
-	// XXX HACK XXX
-	// We need to set LC_ALL to "" here for input methods to work reliably.
-	setlocale(LC_ALL, "");
+    // XXX HACK XXX
+    // We need to set LC_ALL to "" here for input methods to work reliably.
+    setlocale(LC_ALL, "");
 
-	// However LC_NUMERIC must be "C", otherwise some parsing fails.
-	setlocale(LC_NUMERIC, "C");
+    // However LC_NUMERIC must be "C", otherwise some parsing fails.
+    setlocale(LC_NUMERIC, "C");
 #endif
 
-	StartupLog("Initializing context controls");
-	context->ass->AddCommitListener(&FrameMain::UpdateTitle, this);
-	context->subsController->AddFileOpenListener(&FrameMain::OnSubtitlesOpen, this);
-	context->subsController->AddFileSaveListener(&FrameMain::UpdateTitle, this);
-	context->project->AddAudioProviderListener(&FrameMain::OnAudioOpen, this);
-	context->project->AddVideoProviderListener(&FrameMain::OnVideoOpen, this);
+    StartupLog("Initializing context controls");
+    context->ass->AddCommitListener(&FrameMain::UpdateTitle, this);
+    context->subsController->AddFileOpenListener(&FrameMain::OnSubtitlesOpen, this);
+    context->subsController->AddFileSaveListener(&FrameMain::UpdateTitle, this);
+    context->project->AddAudioProviderListener(&FrameMain::OnAudioOpen, this);
+    context->project->AddVideoProviderListener(&FrameMain::OnVideoOpen, this);
 
-	StartupLog("Initializing context frames");
-	context->parent = this;
-	context->frame = this;
+    StartupLog("Initializing context frames");
+    context->parent = this;
+    context->frame = this;
 
-	StartupLog("Apply saved Maximized state");
-	if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);
+    StartupLog("Apply saved Maximized state");
+    if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);
 
-	StartupLog("Initialize toolbar");
-	wxSystemOptions::SetOption("msw.remap", 0);
-	OPT_SUB("App/Show Toolbar", &FrameMain::EnableToolBar, this);
-	EnableToolBar(*OPT_GET("App/Show Toolbar"));
+    StartupLog("Initialize toolbar");
+    wxSystemOptions::SetOption("msw.remap", 0);
+    OPT_SUB("App/Show Toolbar", &FrameMain::EnableToolBar, this);
+    EnableToolBar(*OPT_GET("App/Show Toolbar"));
 
-	StartupLog("Initialize menu bar");
-	menu::GetMenuBar("main", this, context.get());
+    StartupLog("Initialize menu bar");
+    menu::GetMenuBar("main", this, context.get());
 
-	StartupLog("Create status bar");
-	CreateStatusBar(2);
+    StartupLog("Create status bar");
+    CreateStatusBar(2);
 
-	StartupLog("Set icon");
+    StartupLog("Set icon");
 #ifdef _WIN32
-	SetIcon(wxICON(wxicon));
+    SetIcon(wxICON(wxicon));
 #else
-	wxIcon icon;
-	icon.CopyFromBitmap(GETIMAGE(wxicon));
-	SetIcon(icon);
+    wxIcon icon;
+    icon.CopyFromBitmap(GETIMAGE(wxicon));
+    SetIcon(icon);
 #endif
 
-	StartupLog("Create views and inner main window controls");
-	InitContents();
-	OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this);
+    StartupLog("Create views and inner main window controls");
+    InitContents();
+    OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this);
 
-	StartupLog("Set up drag/drop target");
-	SetDropTarget(new AegisubFileDropTarget(context.get()));
+    StartupLog("Set up drag/drop target");
+    SetDropTarget(new AegisubFileDropTarget(context.get()));
 
-	StartupLog("Load default file");
-	context->project->CloseSubtitles();
+    StartupLog("Load default file");
+    context->project->CloseSubtitles();
 
-	StartupLog("Display main window");
-	AddFullScreenButton(this);
-	Show();
-	SetDisplayMode(1, 1);
+    StartupLog("Display main window");
+    AddFullScreenButton(this);
+    Show();
+    SetDisplayMode(1, 1);
 
-	StartupLog("Leaving FrameMain constructor");
+    StartupLog("Leaving FrameMain constructor");
 }
 
-FrameMain::~FrameMain () {
-	context->project->CloseAudio();
-	context->project->CloseVideo();
+FrameMain::~FrameMain ()
+{
+    context->project->CloseAudio();
+    context->project->CloseVideo();
 
-	DestroyChildren();
+    DestroyChildren();
 }
 
-void FrameMain::EnableToolBar(agi::OptionValue const& opt) {
-	if (opt.GetBool()) {
-		if (!GetToolBar()) {
-			toolbar::AttachToolbar(this, "main", context.get(), "Default");
-			GetToolBar()->Realize();
-		}
-	}
-	else if (wxToolBar *old_tb = GetToolBar()) {
-		SetToolBar(nullptr);
-		delete old_tb;
-		Layout();
-	}
+void FrameMain::EnableToolBar(agi::OptionValue const &opt)
+{
+    if (opt.GetBool()) {
+        if (!GetToolBar()) {
+            toolbar::AttachToolbar(this, "main", context.get(), "Default");
+            GetToolBar()->Realize();
+        }
+    }
+    else if (wxToolBar *old_tb = GetToolBar()) {
+        SetToolBar(nullptr);
+        delete old_tb;
+        Layout();
+    }
 }
 
-void FrameMain::InitContents() {
-	StartupLog("Create background panel");
-	auto Panel = new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
-
-	StartupLog("Create subtitles grid");
-	context->subsGrid = new BaseGrid(Panel, context.get());
-
-	StartupLog("Create video box");
-	videoBox = new VideoBox(Panel, false, context.get());
-
-	StartupLog("Create audio box");
-	context->audioBox = audioBox = new AudioBox(Panel, context.get());
-
-	StartupLog("Create subtitle editing box");
-	auto EditBox = new SubsEditBox(Panel, context.get());
-
-	StartupLog("Arrange main sizers");
-	ToolsSizer = new wxBoxSizer(wxVERTICAL);
-	ToolsSizer->Add(audioBox, 0, wxEXPAND);
-	ToolsSizer->Add(EditBox, 1, wxEXPAND);
-	TopSizer = new wxBoxSizer(wxHORIZONTAL);
-	TopSizer->Add(videoBox, 0, wxEXPAND, 0);
-	TopSizer->Add(ToolsSizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
-	MainSizer = new wxBoxSizer(wxVERTICAL);
-	MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
-	MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0);
-	MainSizer->Add(context->subsGrid,1,wxEXPAND | wxALL,0);
-	Panel->SetSizer(MainSizer);
-
-	StartupLog("Perform layout");
-	Layout();
-	StartupLog("Leaving InitContents");
+void FrameMain::InitContents()
+{
+    StartupLog("Create background panel");
+    auto Panel = new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxCLIP_CHILDREN);
+
+    StartupLog("Create subtitles grid");
+    context->subsGrid = new BaseGrid(Panel, context.get());
+
+    StartupLog("Create video box");
+    videoBox = new VideoBox(Panel, false, context.get());
+
+    StartupLog("Create audio box");
+    context->audioBox = audioBox = new AudioBox(Panel, context.get());
+
+    StartupLog("Create subtitle editing box");
+    auto EditBox = new SubsEditBox(Panel, context.get());
+
+    StartupLog("Arrange main sizers");
+    ToolsSizer = new wxBoxSizer(wxVERTICAL);
+    ToolsSizer->Add(audioBox, 0, wxEXPAND);
+    ToolsSizer->Add(EditBox, 1, wxEXPAND);
+    TopSizer = new wxBoxSizer(wxHORIZONTAL);
+    TopSizer->Add(videoBox, 0, wxEXPAND, 0);
+    TopSizer->Add(ToolsSizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
+    MainSizer = new wxBoxSizer(wxVERTICAL);
+    MainSizer->Add(new wxStaticLine(Panel), 0, wxEXPAND | wxALL, 0);
+    MainSizer->Add(TopSizer, 0, wxEXPAND | wxALL, 0);
+    MainSizer->Add(context->subsGrid, 1, wxEXPAND | wxALL, 0);
+    Panel->SetSizer(MainSizer);
+
+    StartupLog("Perform layout");
+    Layout();
+    StartupLog("Leaving InitContents");
 }
 
-void FrameMain::SetDisplayMode(int video, int audio) {
-	if (!IsShownOnScreen()) return;
+void FrameMain::SetDisplayMode(int video, int audio)
+{
+    if (!IsShownOnScreen()) return;
 
-	bool sv = false, sa = false;
+    bool sv = false, sa = false;
 
-	if (video == -1) sv = showVideo;
-	else if (video)  sv = context->project->VideoProvider() && !context->dialog->Get<DialogDetachedVideo>();
+    if (video == -1) sv = showVideo;
+    else if (video)  sv = context->project->VideoProvider() && !context->dialog->Get<DialogDetachedVideo>();
 
-	if (audio == -1) sa = showAudio;
-	else if (audio)  sa = !!context->project->AudioProvider();
+    if (audio == -1) sa = showAudio;
+    else if (audio)  sa = !!context->project->AudioProvider();
 
-	// See if anything changed
-	if (sv == showVideo && sa == showAudio) return;
+    // See if anything changed
+    if (sv == showVideo && sa == showAudio) return;
 
-	showVideo = sv;
-	showAudio = sa;
+    showVideo = sv;
+    showAudio = sa;
 
-	bool didFreeze = !IsFrozen();
-	if (didFreeze) Freeze();
+    bool didFreeze = !IsFrozen();
+    if (didFreeze) Freeze();
 
-	context->videoController->Stop();
+    context->videoController->Stop();
 
-	TopSizer->Show(videoBox, showVideo, true);
-	ToolsSizer->Show(audioBox, showAudio, true);
+    TopSizer->Show(videoBox, showVideo, true);
+    ToolsSizer->Show(audioBox, showAudio, true);
 
-	MainSizer->CalcMin();
-	MainSizer->RecalcSizes();
-	MainSizer->Layout();
-	Layout();
+    MainSizer->CalcMin();
+    MainSizer->RecalcSizes();
+    MainSizer->Layout();
+    Layout();
 
-	if (didFreeze) Thaw();
+    if (didFreeze) Thaw();
 }
 
-void FrameMain::UpdateTitle() {
-	wxString newTitle;
-	if (context->subsController->IsModified()) newTitle << "* ";
-	newTitle << context->subsController->Filename().filename().wstring();
+void FrameMain::UpdateTitle()
+{
+    wxString newTitle;
+    if (context->subsController->IsModified()) newTitle << "* ";
+    newTitle << context->subsController->Filename().filename().wstring();
 
 #ifndef __WXMAC__
-	newTitle << " - Aegisub " << GetAegisubLongVersionString();
+    newTitle << " - Aegisub " << GetAegisubLongVersionString();
 #endif
 
 #if defined(__WXMAC__)
-	// On Mac, set the mark in the close button
-	OSXSetModified(context->subsController->IsModified());
+    // On Mac, set the mark in the close button
+    OSXSetModified(context->subsController->IsModified());
 #endif
 
-	if (GetTitle() != newTitle) SetTitle(newTitle);
+    if (GetTitle() != newTitle) SetTitle(newTitle);
 }
 
-void FrameMain::OnVideoOpen(AsyncVideoProvider *provider) {
-	if (!provider) {
-		SetDisplayMode(0, -1);
-		return;
-	}
-
-	Freeze();
-	int vidx = provider->GetWidth(), vidy = provider->GetHeight();
-
-	// Set zoom level based on video resolution and window size
-	double zoom = context->videoDisplay->GetZoom();
-	wxSize windowSize = GetSize();
-	if (vidx*3*zoom > windowSize.GetX()*4 || vidy*4*zoom > windowSize.GetY()*6)
-		context->videoDisplay->SetZoom(zoom * .25);
-	else if (vidx*3*zoom > windowSize.GetX()*2 || vidy*4*zoom > windowSize.GetY()*3)
-		context->videoDisplay->SetZoom(zoom * .5);
-
-	SetDisplayMode(1,-1);
-
-	if (OPT_GET("Video/Detached/Enabled")->GetBool() && !context->dialog->Get<DialogDetachedVideo>())
-		cmd::call("video/detach", context.get());
-	Thaw();
+void FrameMain::OnVideoOpen(AsyncVideoProvider *provider)
+{
+    if (!provider) {
+        SetDisplayMode(0, -1);
+        return;
+    }
+
+    Freeze();
+    int vidx = provider->GetWidth(), vidy = provider->GetHeight();
+
+    // Set zoom level based on video resolution and window size
+    double zoom = context->videoDisplay->GetZoom();
+    wxSize windowSize = GetSize();
+    if (vidx * 3 * zoom > windowSize.GetX() * 4 || vidy * 4 * zoom > windowSize.GetY() * 6)
+        context->videoDisplay->SetZoom(zoom * .25);
+    else if (vidx * 3 * zoom > windowSize.GetX() * 2 || vidy * 4 * zoom > windowSize.GetY() * 3)
+        context->videoDisplay->SetZoom(zoom * .5);
+
+    SetDisplayMode(1, -1);
+
+    if (OPT_GET("Video/Detached/Enabled")->GetBool() && !context->dialog->Get<DialogDetachedVideo>())
+        cmd::call("video/detach", context.get());
+    Thaw();
 }
 
-void FrameMain::OnVideoDetach(agi::OptionValue const& opt) {
-	if (opt.GetBool())
-		SetDisplayMode(0, -1);
-	else if (context->project->VideoProvider())
-		SetDisplayMode(1, -1);
+void FrameMain::OnVideoDetach(agi::OptionValue const &opt)
+{
+    if (opt.GetBool())
+        SetDisplayMode(0, -1);
+    else if (context->project->VideoProvider())
+        SetDisplayMode(1, -1);
 }
 
-void FrameMain::StatusTimeout(wxString text,int ms) {
-	SetStatusText(text,1);
-	StatusClear.SetOwner(this, ID_APP_TIMER_STATUSCLEAR);
-	StatusClear.Start(ms,true);
+void FrameMain::StatusTimeout(wxString text, int ms)
+{
+    SetStatusText(text, 1);
+    StatusClear.SetOwner(this, ID_APP_TIMER_STATUSCLEAR);
+    StatusClear.Start(ms, true);
 }
 
 BEGIN_EVENT_TABLE(FrameMain, wxFrame)
-	EVT_TIMER(ID_APP_TIMER_STATUSCLEAR, FrameMain::OnStatusClear)
-	EVT_CLOSE(FrameMain::OnCloseWindow)
-	EVT_CHAR_HOOK(FrameMain::OnKeyDown)
-	EVT_MOUSEWHEEL(FrameMain::OnMouseWheel)
+    EVT_TIMER(ID_APP_TIMER_STATUSCLEAR, FrameMain::OnStatusClear)
+    EVT_CLOSE(FrameMain::OnCloseWindow)
+    EVT_CHAR_HOOK(FrameMain::OnKeyDown)
+    EVT_MOUSEWHEEL(FrameMain::OnMouseWheel)
 END_EVENT_TABLE()
 
-void FrameMain::OnCloseWindow(wxCloseEvent &event) {
-	wxEventBlocker blocker(this, wxEVT_CLOSE_WINDOW);
+void FrameMain::OnCloseWindow(wxCloseEvent &event)
+{
+    wxEventBlocker blocker(this, wxEVT_CLOSE_WINDOW);
 
-	context->videoController->Stop();
-	context->audioController->Stop();
+    context->videoController->Stop();
+    context->audioController->Stop();
 
-	// Ask user if he wants to save first
-	if (context->subsController->TryToClose(event.CanVeto()) == wxCANCEL) {
-		event.Veto();
-		return;
-	}
+    // Ask user if he wants to save first
+    if (context->subsController->TryToClose(event.CanVeto()) == wxCANCEL) {
+        event.Veto();
+        return;
+    }
 
-	context->dialog.reset();
+    context->dialog.reset();
 
-	// Store maximization state
-	OPT_SET("App/Maximized")->SetBool(IsMaximized());
+    // Store maximization state
+    OPT_SET("App/Maximized")->SetBool(IsMaximized());
 
-	Destroy();
+    Destroy();
 }
 
-void FrameMain::OnStatusClear(wxTimerEvent &) {
-	SetStatusText("",1);
+void FrameMain::OnStatusClear(wxTimerEvent &)
+{
+    SetStatusText("", 1);
 }
 
-void FrameMain::OnAudioOpen(agi::AudioProvider *provider) {
-	if (provider)
-		SetDisplayMode(-1, 1);
-	else
-		SetDisplayMode(-1, 0);
+void FrameMain::OnAudioOpen(agi::AudioProvider *provider)
+{
+    if (provider)
+        SetDisplayMode(-1, 1);
+    else
+        SetDisplayMode(-1, 0);
 }
 
-void FrameMain::OnSubtitlesOpen() {
-	UpdateTitle();
-	SetDisplayMode(1, 1);
+void FrameMain::OnSubtitlesOpen()
+{
+    UpdateTitle();
+    SetDisplayMode(1, 1);
 }
 
-void FrameMain::OnKeyDown(wxKeyEvent &event) {
-	hotkey::check("Main Frame", context.get(), event);
+void FrameMain::OnKeyDown(wxKeyEvent &event)
+{
+    hotkey::check("Main Frame", context.get(), event);
 }
 
-void FrameMain::OnMouseWheel(wxMouseEvent &evt) {
-	ForwardMouseWheelEvent(this, evt);
+void FrameMain::OnMouseWheel(wxMouseEvent &evt)
+{
+    ForwardMouseWheelEvent(this, evt);
 }
diff --git a/src/frame_main.h b/src/frame_main.h
index e03295aed16938aa3a8f3fa085626dca7237eeab..16f26a83abf421aa0c678bba41775c12ce3e5013 100644
--- a/src/frame_main.h
+++ b/src/frame_main.h
@@ -39,9 +39,9 @@ namespace agi { class AudioProvider; }
 namespace agi { struct Context; class OptionValue; }
 
 class FrameMain : public wxFrame {
-	friend class AegisubApp;
+    friend class AegisubApp;
 
-	std::unique_ptr<agi::Context> context;
+    std::unique_ptr<agi::Context> context;
 
     // XXX: Make Freeze()/Thaw() noops on GTK, this seems to be buggy
 #ifdef __WXGTK__
@@ -49,50 +49,50 @@ class FrameMain : public wxFrame {
     void Thaw(void) {}
 #endif
 
-	bool showVideo = true; ///< Is the video display shown?
-	bool showAudio = true; ///< Is the audio display shown?
-	wxTimer StatusClear;   ///< Status bar timeout timer
+    bool showVideo = true; ///< Is the video display shown?
+    bool showAudio = true; ///< Is the audio display shown?
+    wxTimer StatusClear;   ///< Status bar timeout timer
 
-	void InitContents();
+    void InitContents();
 
-	void UpdateTitle();
+    void UpdateTitle();
 
-	void OnKeyDown(wxKeyEvent &event);
-	void OnMouseWheel(wxMouseEvent &evt);
+    void OnKeyDown(wxKeyEvent &event);
+    void OnMouseWheel(wxMouseEvent &evt);
 
-	void OnStatusClear(wxTimerEvent &event);
-	void OnCloseWindow (wxCloseEvent &event);
+    void OnStatusClear(wxTimerEvent &event);
+    void OnCloseWindow (wxCloseEvent &event);
 
-	void OnAudioOpen(agi::AudioProvider *provider);
-	void OnVideoOpen(AsyncVideoProvider *provider);
-	void OnVideoDetach(agi::OptionValue const& opt);
-	void OnSubtitlesOpen();
+    void OnAudioOpen(agi::AudioProvider *provider);
+    void OnVideoOpen(AsyncVideoProvider *provider);
+    void OnVideoDetach(agi::OptionValue const &opt);
+    void OnSubtitlesOpen();
 
-	void EnableToolBar(agi::OptionValue const& opt);
+    void EnableToolBar(agi::OptionValue const &opt);
 
-	AudioBox *audioBox;      ///< The audio area
-	VideoBox *videoBox;      ///< The video area
+    AudioBox *audioBox;      ///< The audio area
+    VideoBox *videoBox;      ///< The video area
 
-	wxSizer *MainSizer;  ///< Arranges things from top to bottom in the window
-	wxSizer *TopSizer;   ///< Arranges video box and tool box from left to right
-	wxSizer *ToolsSizer; ///< Arranges audio and editing areas top to bottom
+    wxSizer *MainSizer;  ///< Arranges things from top to bottom in the window
+    wxSizer *TopSizer;   ///< Arranges video box and tool box from left to right
+    wxSizer *ToolsSizer; ///< Arranges audio and editing areas top to bottom
 
 public:
-	FrameMain();
-	~FrameMain();
+    FrameMain();
+    ~FrameMain();
 
-	/// Set the status bar text
-	/// @param text New status bar text
-	/// @param ms Time in milliseconds that the message should be visible
-	void StatusTimeout(wxString text,int ms=10000);
+    /// Set the status bar text
+    /// @param text New status bar text
+    /// @param ms Time in milliseconds that the message should be visible
+    void StatusTimeout(wxString text, int ms = 10000);
 
-	/// @brief Set the video and audio display visibility
-	/// @param video -1: leave unchanged; 0: hide; 1: show
-	/// @param audio -1: leave unchanged; 0: hide; 1: show
-	void SetDisplayMode(int showVid,int showAudio);
+    /// @brief Set the video and audio display visibility
+    /// @param video -1: leave unchanged; 0: hide; 1: show
+    /// @param audio -1: leave unchanged; 0: hide; 1: show
+    void SetDisplayMode(int showVid, int showAudio);
 
-	bool IsVideoShown() const { return showVideo; }
-	bool IsAudioShown() const { return showAudio; }
+    bool IsVideoShown() const { return showVideo; }
+    bool IsAudioShown() const { return showAudio; }
 
-	DECLARE_EVENT_TABLE()
+    DECLARE_EVENT_TABLE()
 };
diff --git a/src/gl/glext.h b/src/gl/glext.h
index 4f5e87f8b5eb67e1563cb0944dd3e9ab0f457064..98ec33a3ce82747e5cbca5c002298139135ddaf1 100644
--- a/src/gl/glext.h
+++ b/src/gl/glext.h
@@ -5112,7 +5112,7 @@ typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m);
 #ifdef GL_GLEXT_PROTOTYPES
 GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
 GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
-GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount);
 GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param);
 GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params);
 GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param);
@@ -5120,7 +5120,7 @@ GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
 typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
-typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount);
 typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param);
 typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params);
 typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param);
@@ -5227,10 +5227,10 @@ GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer);
 GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
 GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
-GLAPI GLvoid* APIENTRY glMapBuffer (GLenum target, GLenum access);
+GLAPI GLvoid *APIENTRY glMapBuffer (GLenum target, GLenum access);
 GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target);
 GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params);
+GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, GLvoid * *params);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids);
 typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids);
@@ -5247,10 +5247,10 @@ typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer);
 typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
 typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data);
-typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
+typedef GLvoid *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access);
 typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid * *params);
 #endif
 
 #ifndef GL_VERSION_2_0
@@ -5286,11 +5286,11 @@ GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *param
 GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params);
 GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params);
 GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid* *pointer);
+GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid * *pointer);
 GLAPI GLboolean APIENTRY glIsProgram (GLuint program);
 GLAPI GLboolean APIENTRY glIsShader (GLuint shader);
 GLAPI void APIENTRY glLinkProgram (GLuint program);
-GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length);
+GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar * *string, const GLint *length);
 GLAPI void APIENTRY glUseProgram (GLuint program);
 GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0);
 GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1);
@@ -5380,11 +5380,11 @@ typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location,
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid * *pointer);
 typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program);
 typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader);
 typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
-typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length);
+typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar * *string, const GLint *length);
 typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
 typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
 typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
@@ -5480,7 +5480,7 @@ GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode);
 GLAPI void APIENTRY glEndTransformFeedback (void);
 GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer);
-GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
 GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp);
 GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode);
@@ -5527,7 +5527,7 @@ GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLin
 GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value);
 GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value);
 GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
-GLAPI const GLubyte * APIENTRY glGetStringi (GLenum name, GLuint index);
+GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);
 typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
@@ -5539,7 +5539,7 @@ typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode);
 typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void);
 typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer);
-typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
 typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp);
 typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode);
@@ -5586,7 +5586,7 @@ typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer
 typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value);
 typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value);
 typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
-typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
+typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
 #endif
 
 #ifndef GL_VERSION_3_1
@@ -5960,7 +5960,7 @@ GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, GLvoid *
 GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params);
 GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params);
 GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, GLvoid* *pointer);
+GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, GLvoid * *pointer);
 GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x);
@@ -6023,7 +6023,7 @@ typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pna
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, GLvoid * *pointer);
 typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program);
 #endif
 
@@ -6042,10 +6042,10 @@ GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer);
 GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage);
 GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data);
 GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data);
-GLAPI GLvoid* APIENTRY glMapBufferARB (GLenum target, GLenum access);
+GLAPI GLvoid *APIENTRY glMapBufferARB (GLenum target, GLenum access);
 GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target);
 GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, GLvoid* *params);
+GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, GLvoid * *params);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
 typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
@@ -6054,10 +6054,10 @@ typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer);
 typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data);
 typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data);
-typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access);
+typedef GLvoid *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access);
 typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, GLvoid * *params);
 #endif
 
 #ifndef GL_ARB_occlusion_query
@@ -6089,7 +6089,7 @@ GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj);
 GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname);
 GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj);
 GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType);
-GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
+GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, const GLint *length);
 GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj);
 GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void);
 GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj);
@@ -6129,7 +6129,7 @@ typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj);
 typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname);
 typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj);
 typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType);
-typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
+typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, const GLint *length);
 typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj);
 typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void);
 typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj);
@@ -6323,10 +6323,10 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint d
 #ifndef GL_ARB_map_buffer_range
 #define GL_ARB_map_buffer_range 1
 #ifdef GL_GLEXT_PROTOTYPES
-GLAPI GLvoid* APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+GLAPI GLvoid *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
 GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length);
 #endif /* GL_GLEXT_PROTOTYPES */
-typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+typedef GLvoid *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
 typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length);
 #endif
 
@@ -6363,7 +6363,7 @@ typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array);
 #ifndef GL_ARB_uniform_buffer_object
 #define GL_ARB_uniform_buffer_object 1
 #ifdef GL_GLEXT_PROTOTYPES
-GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices);
+GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar * *uniformNames, GLuint *uniformIndices);
 GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);
 GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);
 GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName);
@@ -6371,7 +6371,7 @@ GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlo
 GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName);
 GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
 #endif /* GL_GLEXT_PROTOTYPES */
-typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices);
+typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar * *uniformNames, GLuint *uniformIndices);
 typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);
 typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);
 typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName);
@@ -6406,12 +6406,12 @@ typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum w
 GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex);
 GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex);
 GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex);
-GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex);
+GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount, const GLint *basevertex);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex);
 typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex);
 typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex);
-typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount, const GLint *basevertex);
 #endif
 
 #ifndef GL_ARB_fragment_coord_conventions
@@ -6507,14 +6507,14 @@ typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLclampf value);
 #ifdef GL_GLEXT_PROTOTYPES
 GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string);
 GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name);
-GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length);
+GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar * *path, const GLint *length);
 GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name);
 GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string);
 GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string);
 typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name);
-typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length);
+typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar * *path, const GLint *length);
 typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name);
 typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string);
 typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params);
@@ -7073,7 +7073,7 @@ GLAPI void APIENTRY glArrayElementEXT (GLint i);
 GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count);
 GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer);
-GLAPI void APIENTRY glGetPointervEXT (GLenum pname, GLvoid* *params);
+GLAPI void APIENTRY glGetPointervEXT (GLenum pname, GLvoid * *params);
 GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
@@ -7083,7 +7083,7 @@ typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i);
 typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count);
 typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer);
-typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid * *params);
 typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
 typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer);
@@ -7529,15 +7529,15 @@ typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker);
 #ifndef GL_INTEL_parallel_arrays
 #define GL_INTEL_parallel_arrays 1
 #ifdef GL_GLEXT_PROTOTYPES
-GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer);
-GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const GLvoid* *pointer);
-GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer);
-GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const GLvoid* *pointer);
+GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const GLvoid * *pointer);
+GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const GLvoid * *pointer);
+GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const GLvoid * *pointer);
+GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const GLvoid * *pointer);
 #endif /* GL_GLEXT_PROTOTYPES */
-typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
-typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const GLvoid* *pointer);
-typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
-typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid * *pointer);
+typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const GLvoid * *pointer);
+typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid * *pointer);
+typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const GLvoid * *pointer);
 #endif
 
 #ifndef GL_HP_occlusion_test
@@ -7622,10 +7622,10 @@ typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode);
 #define GL_EXT_multi_draw_arrays 1
 #ifdef GL_GLEXT_PROTOTYPES
 GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
-GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
-typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount);
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid * *indices, GLsizei primcount);
 #endif
 
 #ifndef GL_EXT_fog_coord
@@ -7753,7 +7753,7 @@ GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code);
 GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code);
 GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code);
 GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code);
-GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const GLvoid* *pointer);
+GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const GLvoid * *pointer);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code);
 typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code);
@@ -7761,7 +7761,7 @@ typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code);
 typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code);
 typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code);
 typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code);
-typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const GLvoid * *pointer);
 #endif
 
 #ifndef GL_SUN_vertex
@@ -8046,32 +8046,32 @@ typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v);
 #define GL_IBM_multimode_draw_arrays 1
 #ifdef GL_GLEXT_PROTOTYPES
 GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride);
-GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride);
+GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid *const *indices, GLsizei primcount, GLint modestride);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride);
-typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei primcount, GLint modestride);
+typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const GLvoid *const *indices, GLsizei primcount, GLint modestride);
 #endif
 
 #ifndef GL_IBM_vertex_array_lists
 #define GL_IBM_vertex_array_lists 1
 #ifdef GL_GLEXT_PROTOTYPES
-GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-#endif /* GL_GLEXT_PROTOTYPES */
-typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
-typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid* *pointer, GLint ptrstride);
+GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+#endif /* GL_GLEXT_PROTOTYPES */
+typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
+typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const GLvoid * *pointer, GLint ptrstride);
 #endif
 
 #ifndef GL_SGIX_subsample
@@ -8250,7 +8250,7 @@ GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum
 GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params);
 GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params);
 GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, GLvoid* *pointer);
+GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, GLvoid * *pointer);
 GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id);
 GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program);
 GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
@@ -8315,7 +8315,7 @@ typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint addr
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params);
 typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, GLvoid* *pointer);
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, GLvoid * *pointer);
 typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id);
 typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program);
 typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
@@ -8518,7 +8518,7 @@ GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap);
 GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data);
 GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data);
 GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data);
-GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, GLvoid* *data);
+GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, GLvoid * *data);
 GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data);
 GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data);
 GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data);
@@ -8561,7 +8561,7 @@ typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum ca
 typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data);
 typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data);
 typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data);
-typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, GLvoid* *data);
+typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, GLvoid * *data);
 typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data);
 typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data);
 typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data);
@@ -9003,10 +9003,10 @@ typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index);
 #ifndef GL_ATI_map_object_buffer
 #define GL_ATI_map_object_buffer 1
 #ifdef GL_GLEXT_PROTOTYPES
-GLAPI GLvoid* APIENTRY glMapObjectBufferATI (GLuint buffer);
+GLAPI GLvoid *APIENTRY glMapObjectBufferATI (GLuint buffer);
 GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer);
 #endif /* GL_GLEXT_PROTOTYPES */
-typedef GLvoid* (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer);
+typedef GLvoid *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer);
 typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer);
 #endif
 
@@ -9447,7 +9447,7 @@ GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLuint count, const GLint *att
 GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
 GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer);
-GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name);
 GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name);
 GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
@@ -9459,7 +9459,7 @@ typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLuint count, cons
 typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
 typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer);
-typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name);
 typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name);
 typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
@@ -9540,7 +9540,7 @@ GLAPI void APIENTRY glEndTransformFeedbackEXT (void);
 GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
 GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer);
-GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode);
@@ -9548,7 +9548,7 @@ typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void);
 typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
 typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset);
 typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer);
-typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode);
+typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar * *varyings, GLenum bufferMode);
 typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
 #endif
 
@@ -9637,7 +9637,7 @@ GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum
 GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params);
 GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data);
 GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data);
-GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, GLvoid* *data);
+GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, GLvoid * *data);
 GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits);
 GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits);
 GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits);
@@ -9713,10 +9713,10 @@ GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLs
 GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value);
 GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage);
 GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
-GLAPI GLvoid* APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access);
+GLAPI GLvoid *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access);
 GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer);
 GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params);
-GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, GLvoid* *params);
+GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, GLvoid * *params);
 GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLvoid *data);
 GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer);
 GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer);
@@ -9824,7 +9824,7 @@ typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum co
 typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params);
 typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data);
 typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data);
-typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLvoid* *data);
+typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLvoid * *data);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *bits);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *bits);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *bits);
@@ -9900,10 +9900,10 @@ typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint l
 typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value);
 typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
-typedef GLvoid* (APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access);
+typedef GLvoid *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access);
 typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer);
 typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params);
-typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, GLvoid * *params);
 typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLvoid *data);
 typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer);
 typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer);
@@ -10046,10 +10046,10 @@ typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, G
 #define GL_APPLE_texture_range 1
 #ifdef GL_GLEXT_PROTOTYPES
 GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const GLvoid *pointer);
-GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, GLvoid* *params);
+GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, GLvoid * *params);
 #endif /* GL_GLEXT_PROTOTYPES */
 typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const GLvoid *pointer);
-typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, GLvoid* *params);
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, GLvoid * *params);
 #endif
 
 #ifndef GL_APPLE_float_pixels
diff --git a/src/gl_text.cpp b/src/gl_text.cpp
index 94f866961a701fc6750cc66dd4dac2cd56a7e938..12bacca9f1e1b93e3a8d579186c22decebcb6017 100644
--- a/src/gl_text.cpp
+++ b/src/gl_text.cpp
@@ -56,165 +56,162 @@ namespace {
 /// @class OpenGLTextGlyph
 /// @brief Struct storing the information needed to draw a glyph
 struct OpenGLTextGlyph {
-	wxString str; ///< String containing the glyph(s) this is for
-	int tex = 0;  ///< OpenGL texture to draw for this glyph
-	float x1 = 0; ///< Left x coordinate of this glyph in the containing texture
-	float x2 = 0; ///< Right x coordinate of this glyph in the containing texture
-	float y1 = 0; ///< Left y coordinate of this glyph in the containing texture
-	float y2 = 0; ///< Right y coordinate of this glyph in the containing texture
-	int w = 0;    ///< Width of the glyph in pixels
-	int h = 0;    ///< Height of the glyph in pixels
-	wxFont font;  ///< Font used for this glyph
-
-	OpenGLTextGlyph(int value, wxFont const& font)
-	: str(wxChar(value))
-	, font(font)
-	{
-		wxCoord desc,lead;
-
-		wxBitmap tempBmp(32, 32, 24);
-		wxMemoryDC dc(tempBmp);
-		dc.SetFont(font);
-		dc.GetTextExtent(str, &w, &h, &desc, &lead);
-	}
-
-	void Draw(float x, float y) const {
-		glBindTexture(GL_TEXTURE_2D, tex);
-		glEnableClientState(GL_VERTEX_ARRAY);
-		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-		float tex_coords[] = {
-			x1, y1,
-			x1, y2,
-			x2, y2,
-			x2, y1
-		};
-
-		float vert_coords[] = {
-			x, y,
-			x, y + h,
-			x + w, y + h,
-			x + w, y
-		};
-
-		glVertexPointer(2, GL_FLOAT, 0, vert_coords);
-		glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
-		glDrawArrays(GL_QUADS, 0, 4);
-
-		glDisableClientState(GL_VERTEX_ARRAY);
-		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	}
+    wxString str; ///< String containing the glyph(s) this is for
+    int tex = 0;  ///< OpenGL texture to draw for this glyph
+    float x1 = 0; ///< Left x coordinate of this glyph in the containing texture
+    float x2 = 0; ///< Right x coordinate of this glyph in the containing texture
+    float y1 = 0; ///< Left y coordinate of this glyph in the containing texture
+    float y2 = 0; ///< Right y coordinate of this glyph in the containing texture
+    int w = 0;    ///< Width of the glyph in pixels
+    int h = 0;    ///< Height of the glyph in pixels
+    wxFont font;  ///< Font used for this glyph
+
+    OpenGLTextGlyph(int value, wxFont const &font)
+        : str(wxChar(value))
+        , font(font) {
+        wxCoord desc, lead;
+
+        wxBitmap tempBmp(32, 32, 24);
+        wxMemoryDC dc(tempBmp);
+        dc.SetFont(font);
+        dc.GetTextExtent(str, &w, &h, &desc, &lead);
+    }
+
+    void Draw(float x, float y) const {
+        glBindTexture(GL_TEXTURE_2D, tex);
+        glEnableClientState(GL_VERTEX_ARRAY);
+        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+        float tex_coords[] = {
+            x1, y1,
+            x1, y2,
+            x2, y2,
+            x2, y1
+        };
+
+        float vert_coords[] = {
+            x, y,
+            x, y + h,
+            x + w, y + h,
+            x + w, y
+        };
+
+        glVertexPointer(2, GL_FLOAT, 0, vert_coords);
+        glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
+        glDrawArrays(GL_QUADS, 0, 4);
+
+        glDisableClientState(GL_VERTEX_ARRAY);
+        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    }
 };
 
 /// @class OpenGLTextTexture
 /// @brief OpenGL texture which stores one or more glyphs as sprites
 class OpenGLTextTexture final : boost::noncopyable {
-	int x = 0;      ///< Next x coordinate at which a glyph can be inserted
-	int y = 0;      ///< Next y coordinate at which a glyph can be inserted
-	int nextY = 0;  ///< Y coordinate of the next line; tracked due to that lines
-	                ///< are only as tall as needed to fit the glyphs in them
-	int width;      ///< Width of the texture
-	int height;     ///< Height of the texture
-	GLuint tex = 0; ///< The texture
-
-	/// Insert the glyph into this texture at the current coordinates
-	void Insert(OpenGLTextGlyph &glyph) {
-		int w = glyph.w;
-		int h = glyph.h;
-
-		// Fill glyph structure
-		glyph.x1 = float(x)/width;
-		glyph.y1 = float(y)/height;
-		glyph.x2 = float(x+w)/width;
-		glyph.y2 = float(y+h)/height;
-		glyph.tex = tex;
-
-		// Create bitmap and bind it to a DC
-		wxBitmap bmp(w + (w & 1), h + (h & 1), 24);
-		wxMemoryDC dc(bmp);
-
-		// Draw text and convert to image
-		dc.SetBackground(*wxBLACK_BRUSH);
-		dc.Clear();
-		dc.SetFont(glyph.font);
-		dc.SetTextForeground(*wxWHITE);
-		dc.DrawText(glyph.str, 0, 0);
-
-		// Convert RGB24 to Luminance + Alpha by using an arbitrary channel as A
-		wxImage img = bmp.ConvertToImage();
-		int imgw = img.GetWidth();
-		int imgh = img.GetHeight();
-		std::vector<unsigned char> alpha(imgw * imgh * 2, 255);
-		const unsigned char *read = img.GetData();
-		for (size_t write = 1; write < alpha.size(); write += 2, read += 3)
-			alpha[write] = *read;
-
-		// Upload image to video memory
-		glBindTexture(GL_TEXTURE_2D, tex);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, imgw, imgh, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, &alpha[0]);
-		if (glGetError()) throw agi::EnvironmentError("Internal OpenGL text renderer error: Error uploading glyph data to video memory.");
-	}
+    int x = 0;      ///< Next x coordinate at which a glyph can be inserted
+    int y = 0;      ///< Next y coordinate at which a glyph can be inserted
+    int nextY = 0;  ///< Y coordinate of the next line; tracked due to that lines
+    ///< are only as tall as needed to fit the glyphs in them
+    int width;      ///< Width of the texture
+    int height;     ///< Height of the texture
+    GLuint tex = 0; ///< The texture
+
+    /// Insert the glyph into this texture at the current coordinates
+    void Insert(OpenGLTextGlyph &glyph) {
+        int w = glyph.w;
+        int h = glyph.h;
+
+        // Fill glyph structure
+        glyph.x1 = float(x) / width;
+        glyph.y1 = float(y) / height;
+        glyph.x2 = float(x + w) / width;
+        glyph.y2 = float(y + h) / height;
+        glyph.tex = tex;
+
+        // Create bitmap and bind it to a DC
+        wxBitmap bmp(w + (w & 1), h + (h & 1), 24);
+        wxMemoryDC dc(bmp);
+
+        // Draw text and convert to image
+        dc.SetBackground(*wxBLACK_BRUSH);
+        dc.Clear();
+        dc.SetFont(glyph.font);
+        dc.SetTextForeground(*wxWHITE);
+        dc.DrawText(glyph.str, 0, 0);
+
+        // Convert RGB24 to Luminance + Alpha by using an arbitrary channel as A
+        wxImage img = bmp.ConvertToImage();
+        int imgw = img.GetWidth();
+        int imgh = img.GetHeight();
+        std::vector<unsigned char> alpha(imgw * imgh * 2, 255);
+        const unsigned char *read = img.GetData();
+        for (size_t write = 1; write < alpha.size(); write += 2, read += 3)
+            alpha[write] = *read;
+
+        // Upload image to video memory
+        glBindTexture(GL_TEXTURE_2D, tex);
+        glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, imgw, imgh, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, &alpha[0]);
+        if (glGetError()) throw agi::EnvironmentError("Internal OpenGL text renderer error: Error uploading glyph data to video memory.");
+    }
 
 public:
-	OpenGLTextTexture(OpenGLTextGlyph &glyph)
-	: width(std::max(SmallestPowerOf2(glyph.w), 64))
-	, height(std::max(SmallestPowerOf2(glyph.h), 64))
-	{
-		width = height = std::max(width, height);
-
-		// Generate and bind
-		glGenTextures(1, &tex);
-		glBindTexture(GL_TEXTURE_2D, tex);
-
-		// Texture parameters
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-
-		// Allocate texture
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
-		if (glGetError()) throw agi::EnvironmentError("Internal OpenGL text renderer error: Could not allocate text texture");
-
-		TryToInsert(glyph);
-	}
-
-	OpenGLTextTexture(OpenGLTextTexture&& rhs) BOOST_NOEXCEPT
-	: x(rhs.x)
-	, y(rhs.y)
-	, nextY(rhs.nextY)
-	, width(rhs.width)
-	, height(rhs.height)
-	, tex(rhs.tex)
-	{
-		rhs.tex = 0;
-	}
-
-	~OpenGLTextTexture() {
-		if (tex) glDeleteTextures(1, &tex);
-	}
-
-	/// @brief Try to insert a glyph into this texture
-	/// @param[in][out] glyph Texture to insert
-	/// @return Was the texture successfully added?
-	bool TryToInsert(OpenGLTextGlyph &glyph) {
-		if (glyph.w > width) return false;
-		if (y + glyph.h > height) return false;
-
-		// Can fit in this row?
-		if (x + glyph.w < width) {
-			Insert(glyph);
-			x += glyph.w;
-			nextY = std::max(nextY, y + glyph.h);
-			return true;
-		}
-
-		// Can fit the next row?
-		if (nextY + glyph.h > height) return false;
-		x = 0;
-		y = nextY;
-		return TryToInsert(glyph);
-	}
+    OpenGLTextTexture(OpenGLTextGlyph &glyph)
+        : width(std::max(SmallestPowerOf2(glyph.w), 64))
+        , height(std::max(SmallestPowerOf2(glyph.h), 64)) {
+        width = height = std::max(width, height);
+
+        // Generate and bind
+        glGenTextures(1, &tex);
+        glBindTexture(GL_TEXTURE_2D, tex);
+
+        // Texture parameters
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+        // Allocate texture
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr);
+        if (glGetError()) throw agi::EnvironmentError("Internal OpenGL text renderer error: Could not allocate text texture");
+
+        TryToInsert(glyph);
+    }
+
+    OpenGLTextTexture(OpenGLTextTexture &&rhs) BOOST_NOEXCEPT
+: x(rhs.x)
+    , y(rhs.y)
+    , nextY(rhs.nextY)
+    , width(rhs.width)
+    , height(rhs.height)
+    , tex(rhs.tex) {
+        rhs.tex = 0;
+    }
+
+    ~OpenGLTextTexture() {
+        if (tex) glDeleteTextures(1, &tex);
+    }
+
+    /// @brief Try to insert a glyph into this texture
+    /// @param[in][out] glyph Texture to insert
+    /// @return Was the texture successfully added?
+    bool TryToInsert(OpenGLTextGlyph &glyph) {
+        if (glyph.w > width) return false;
+        if (y + glyph.h > height) return false;
+
+        // Can fit in this row?
+        if (x + glyph.w < width) {
+            Insert(glyph);
+            x += glyph.w;
+            nextY = std::max(nextY, y + glyph.h);
+            return true;
+        }
+
+        // Can fit the next row?
+        if (nextY + glyph.h > height) return false;
+        x = 0;
+        y = nextY;
+        return TryToInsert(glyph);
+    }
 };
 
 }
@@ -222,85 +219,92 @@ public:
 OpenGLText::OpenGLText() { }
 OpenGLText::~OpenGLText() { }
 
-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;
-
-	// Set font
-	fontFace = face;
-	fontSize = size;
-	fontBold = bold;
-	fontItalics = italics;
-	font.SetFaceName(to_wx(fontFace));
-	font.SetPointSize(size);
-	font.SetWeight(bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL);
-
-	// Delete all old data
-	textures.clear();
-	glyphs.clear();
+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;
+
+    // Set font
+    fontFace = face;
+    fontSize = size;
+    fontBold = bold;
+    fontItalics = italics;
+    font.SetFaceName(to_wx(fontFace));
+    font.SetPointSize(size);
+    font.SetWeight(bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL);
+
+    // Delete all old data
+    textures.clear();
+    glyphs.clear();
 }
 
-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::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 std::string &text, int x, int y) {
-	glEnable(GL_BLEND);
-	glEnable(GL_TEXTURE_2D);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
-	// Draw border
-	glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
-	DrawString(text, x-1, y);
-	DrawString(text, x+1, y);
-	DrawString(text, x, y-1);
-	DrawString(text, x, y+1);
-
-	// Draw primary string
-	glColor4f(r, g, b, a);
-	DrawString(text, x, y);
-
-	// Disable blend
-	glDisable(GL_TEXTURE_2D);
-	glDisable(GL_BLEND);
+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);
+
+    // Draw border
+    glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
+    DrawString(text, x - 1, y);
+    DrawString(text, x + 1, y);
+    DrawString(text, x, y - 1);
+    DrawString(text, x, y + 1);
+
+    // Draw primary string
+    glColor4f(r, g, b, a);
+    DrawString(text, x, y);
+
+    // Disable blend
+    glDisable(GL_TEXTURE_2D);
+    glDisable(GL_BLEND);
 }
 
-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::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(std::string const& text, int &w, int &h) {
-	w = h = 0;
+void OpenGLText::GetExtent(std::string const &text, int &w, int &h)
+{
+    w = h = 0;
 
-	for (char curChar : text) {
-		OpenGLTextGlyph const& glyph = GetGlyph(curChar);
-		w += glyph.w;
-		h = std::max(h, glyph.h);
-	}
+    for (char curChar : text) {
+        OpenGLTextGlyph const &glyph = GetGlyph(curChar);
+        w += glyph.w;
+        h = std::max(h, glyph.h);
+    }
 }
 
-OpenGLTextGlyph const& OpenGLText::GetGlyph(int i) {
-	auto res = glyphs.find(i);
-	return res != glyphs.end() ? res->second : CreateGlyph(i);
+OpenGLTextGlyph const &OpenGLText::GetGlyph(int i)
+{
+    auto res = glyphs.find(i);
+    return res != glyphs.end() ? res->second : CreateGlyph(i);
 }
 
-OpenGLTextGlyph const& OpenGLText::CreateGlyph(int n) {
-	OpenGLTextGlyph &glyph = glyphs.emplace(n, OpenGLTextGlyph(n, font)).first->second;
+OpenGLTextGlyph const &OpenGLText::CreateGlyph(int n)
+{
+    OpenGLTextGlyph &glyph = glyphs.emplace(n, OpenGLTextGlyph(n, font)).first->second;
 
-	// Insert into some texture
-	for (auto& texture : textures) {
-		if (texture.TryToInsert(glyph))
-			return glyph;
-	}
+    // Insert into some texture
+    for (auto &texture : textures) {
+        if (texture.TryToInsert(glyph))
+            return glyph;
+    }
 
-	// No texture could fit it, create a new one
-	textures.emplace_back(glyph);
-	return glyph;
+    // No texture could fit it, create a new one
+    textures.emplace_back(glyph);
+    return glyph;
 }
diff --git a/src/gl_text.h b/src/gl_text.h
index e25045f12440c03f056e9c7c91dc8094dfb6951f..26a64962fc786f9cd0f28e805538f8384d9e3f16 100644
--- a/src/gl_text.h
+++ b/src/gl_text.h
@@ -44,53 +44,53 @@ namespace agi { struct Color; }
 typedef boost::container::map<int, OpenGLTextGlyph> glyphMap;
 
 class OpenGLText {
-	float r = 1.f, g = 1.f, b = 1.f, a = 1.f;
+    float r = 1.f, g = 1.f, b = 1.f, a = 1.f;
 
-	int fontSize = 0;
-	bool fontBold = false;
-	bool fontItalics = false;
-	std::string fontFace;
-	wxFont font;
+    int fontSize = 0;
+    bool fontBold = false;
+    bool fontItalics = false;
+    std::string fontFace;
+    wxFont font;
 
-	glyphMap glyphs;
+    glyphMap glyphs;
 
-	std::vector<OpenGLTextTexture> textures;
+    std::vector<OpenGLTextTexture> textures;
 
-	OpenGLText(OpenGLText const&) = delete;
-	OpenGLText& operator=(OpenGLText const&) = delete;
+    OpenGLText(OpenGLText const &) = delete;
+    OpenGLText &operator=(OpenGLText const &) = delete;
 
-	/// @brief Get the glyph for the character chr, creating it if necessary
-	/// @param chr Character to get the glyph of
-	/// @return The appropriate OpenGLTextGlyph
-	OpenGLTextGlyph const& GetGlyph(int chr);
-	/// @brief Create a new glyph
-	OpenGLTextGlyph const& CreateGlyph(int chr);
+    /// @brief Get the glyph for the character chr, creating it if necessary
+    /// @param chr Character to get the glyph of
+    /// @return The appropriate OpenGLTextGlyph
+    OpenGLTextGlyph const &GetGlyph(int chr);
+    /// @brief Create a new glyph
+    OpenGLTextGlyph const &CreateGlyph(int chr);
 
-	void DrawString(const std::string &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; }
+    /// @brief Get the currently active font
+    wxFont GetFont() const { return font; }
 
-	/// @brief Set the currently active font
-	/// @param face    Name of the desired font
-	/// @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(std::string const& face, int size, bool bold, bool italics);
-	/// @brief Set the text color
-	/// @param col   Color
-	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 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 std::string &text,int &w,int &h);
+    /// @brief Set the currently active font
+    /// @param face    Name of the desired font
+    /// @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(std::string const &face, int size, bool bold, bool italics);
+    /// @brief Set the text color
+    /// @param col   Color
+    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 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 std::string &text, int &w, int &h);
 
-	OpenGLText();
-	~OpenGLText();
+    OpenGLText();
+    ~OpenGLText();
 };
diff --git a/src/gl_wrap.cpp b/src/gl_wrap.cpp
index 66e5829583da2e5f584a03bc636197ef1c5cf985..72d66b4dd4f6773c9a262caab1cebd8bfa4470c0 100644
--- a/src/gl_wrap.cpp
+++ b/src/gl_wrap.cpp
@@ -54,386 +54,413 @@ static const float pi = 3.1415926535897932384626433832795f;
 #endif
 
 class VertexArray {
-	std::vector<float> data;
-	size_t dim;
+    std::vector<float> data;
+    size_t dim;
 public:
-	VertexArray(size_t dims, size_t elems) {
-		SetSize(dims, elems);
-	}
-
-	void SetSize(size_t dims, size_t elems) {
-		dim = dims;
-		data.resize(elems * dim);
-	}
-
-	void Set(size_t i, float x, float y) {
-		data[i * dim] = x;
-		data[i * dim + 1] = y;
-	}
-
-	void Set(size_t i, float x, float y, float z) {
-		data[i * dim] = x;
-		data[i * dim + 1] = y;
-		data[i * dim + 2] = z;
-	}
-
-	void Set(size_t i, Vector2D p) {
-		data[i * dim] = p.X();
-		data[i * dim + 1] = p.Y();
-	}
-
-	void Draw(GLenum mode, bool clear = true) {
-		glEnableClientState(GL_VERTEX_ARRAY);
-		glVertexPointer(dim, GL_FLOAT, 0, &data[0]);
-		glDrawArrays(mode, 0, data.size() / dim);
-		glDisableClientState(GL_VERTEX_ARRAY);
-		if (clear)
-			data.clear();
-	}
+    VertexArray(size_t dims, size_t elems) {
+        SetSize(dims, elems);
+    }
+
+    void SetSize(size_t dims, size_t elems) {
+        dim = dims;
+        data.resize(elems * dim);
+    }
+
+    void Set(size_t i, float x, float y) {
+        data[i * dim] = x;
+        data[i * dim + 1] = y;
+    }
+
+    void Set(size_t i, float x, float y, float z) {
+        data[i * dim] = x;
+        data[i * dim + 1] = y;
+        data[i * dim + 2] = z;
+    }
+
+    void Set(size_t i, Vector2D p) {
+        data[i * dim] = p.X();
+        data[i * dim + 1] = p.Y();
+    }
+
+    void Draw(GLenum mode, bool clear = true) {
+        glEnableClientState(GL_VERTEX_ARRAY);
+        glVertexPointer(dim, GL_FLOAT, 0, &data[0]);
+        glDrawArrays(mode, 0, data.size() / dim);
+        glDisableClientState(GL_VERTEX_ARRAY);
+        if (clear)
+            data.clear();
+    }
 };
 
-OpenGLWrapper::OpenGLWrapper() {
-	line_r = line_g = line_b = line_a = 1.f;
-	fill_r = fill_g = fill_b = fill_a = 1.f;
-	line_width = 1;
-	transform_pushed = false;
-	smooth = true;
+OpenGLWrapper::OpenGLWrapper()
+{
+    line_r = line_g = line_b = line_a = 1.f;
+    fill_r = fill_g = fill_b = fill_a = 1.f;
+    line_width = 1;
+    transform_pushed = false;
+    smooth = true;
 }
 
-void OpenGLWrapper::DrawLine(Vector2D p1, Vector2D p2) const {
-	SetModeLine();
-	VertexArray buf(2, 2);
-	buf.Set(0, p1);
-	buf.Set(1, p2);
-	buf.Draw(GL_LINES);
+void OpenGLWrapper::DrawLine(Vector2D p1, Vector2D p2) const
+{
+    SetModeLine();
+    VertexArray buf(2, 2);
+    buf.Set(0, p1);
+    buf.Set(1, p2);
+    buf.Draw(GL_LINES);
 }
 
-static inline Vector2D interp(Vector2D p1, Vector2D p2, float t) {
-	return t * p1 + (1 - t) * p2;
+static inline Vector2D interp(Vector2D p1, Vector2D p2, float t)
+{
+    return t * p1 + (1 - t) * p2;
 }
 
-void OpenGLWrapper::DrawDashedLine(Vector2D p1, Vector2D p2, float step) const {
-	step /= (p2 - p1).Len();
-	for (float t = 0; t < 1.f; t += 2 * step) {
-		DrawLine(interp(p1, p2, t), interp(p1, p2, t + step));
-	}
+void OpenGLWrapper::DrawDashedLine(Vector2D p1, Vector2D p2, float step) const
+{
+    step /= (p2 - p1).Len();
+    for (float t = 0; t < 1.f; t += 2 * step) {
+        DrawLine(interp(p1, p2, t), interp(p1, p2, t + step));
+    }
 }
 
-void OpenGLWrapper::DrawEllipse(Vector2D center, Vector2D radius) const {
-	DrawRing(center, radius.Y(), radius.Y(), radius.X() / radius.Y());
+void OpenGLWrapper::DrawEllipse(Vector2D center, Vector2D radius) const
+{
+    DrawRing(center, radius.Y(), radius.Y(), radius.X() / radius.Y());
 }
 
-void OpenGLWrapper::DrawRectangle(Vector2D p1, Vector2D p2) const {
-	VertexArray buf(2, 4);
-	buf.Set(0, p1);
-	buf.Set(1, Vector2D(p2, p1));
-	buf.Set(2, p2);
-	buf.Set(3, Vector2D(p1, p2));
-
-	// Fill
-	if (fill_a != 0.f) {
-		SetModeFill();
-		buf.Draw(GL_QUADS, false);
-	}
-	// Outline
-	if (line_a != 0.f) {
-		SetModeLine();
-		buf.Draw(GL_LINE_LOOP);
-	}
+void OpenGLWrapper::DrawRectangle(Vector2D p1, Vector2D p2) const
+{
+    VertexArray buf(2, 4);
+    buf.Set(0, p1);
+    buf.Set(1, Vector2D(p2, p1));
+    buf.Set(2, p2);
+    buf.Set(3, Vector2D(p1, p2));
+
+    // Fill
+    if (fill_a != 0.f) {
+        SetModeFill();
+        buf.Draw(GL_QUADS, false);
+    }
+    // Outline
+    if (line_a != 0.f) {
+        SetModeLine();
+        buf.Draw(GL_LINE_LOOP);
+    }
 }
 
-void OpenGLWrapper::DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const {
-	VertexArray buf(2, 3);
-	buf.Set(0, p1);
-	buf.Set(1, p2);
-	buf.Set(2, p3);
-
-	// Fill
-	if (fill_a != 0.f) {
-		SetModeFill();
-		buf.Draw(GL_TRIANGLES, false);
-	}
-	// Outline
-	if (line_a != 0.f) {
-		SetModeLine();
-		buf.Draw(GL_LINE_LOOP);
-	}
+void OpenGLWrapper::DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const
+{
+    VertexArray buf(2, 3);
+    buf.Set(0, p1);
+    buf.Set(1, p2);
+    buf.Set(2, p3);
+
+    // Fill
+    if (fill_a != 0.f) {
+        SetModeFill();
+        buf.Draw(GL_TRIANGLES, false);
+    }
+    // Outline
+    if (line_a != 0.f) {
+        SetModeLine();
+        buf.Draw(GL_LINE_LOOP);
+    }
 }
 
-void OpenGLWrapper::DrawRing(Vector2D center, float r1, float r2, float ar, float arc_start, float arc_end) const {
-	if (r2 > r1)
-		std::swap(r1, r2);
-
-	// Arc range
-	bool needs_end_caps = arc_start != arc_end;
-
-	arc_end *= deg2rad;
-	arc_start *= deg2rad;
-	if (arc_end <= arc_start)
-		arc_end += 2.f * pi;
-	float range = arc_end - arc_start;
-
-	// Math
-	int steps = std::max<int>(((r1 + r1 * ar) * range / (2.f * pi)) * 4, 12);
-	float step = range / steps;
-	float cur_angle = arc_start;
-
-	VertexArray buf(2, steps);
-
-	Vector2D scale_inner = Vector2D(ar, 1) * r1;
-	Vector2D scale_outer = Vector2D(ar, 1) * r2;
-
-	if (fill_a != 0.f) {
-		SetModeFill();
-
-		// Annulus
-		if (r1 != r2) {
-			buf.SetSize(2, (steps + 1) * 2);
-			for (int i = 0; i <= steps; i++) {
-				Vector2D offset = Vector2D::FromAngle(cur_angle);
-				buf.Set(i * 2 + 0, center + offset * scale_inner);
-				buf.Set(i * 2 + 1, center + offset * scale_outer);
-				cur_angle += step;
-			}
-			buf.Draw(GL_QUAD_STRIP);
-		}
-		// Circle
-		else {
-			buf.SetSize(2, steps);
-			for (int i = 0; i < steps; i++) {
-				buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
-				cur_angle += step;
-			}
-			buf.Draw(GL_POLYGON);
-		}
-
-		cur_angle = arc_start;
-	}
-
-	if (line_a == 0.f) return;
-
-	// Outer
-	steps++;
-	buf.SetSize(2, steps);
-
-	SetModeLine();
-	for (int i = 0; i < steps; i++) {
-		buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_outer);
-		cur_angle += step;
-	}
-	buf.Draw(GL_LINE_STRIP);
-
-	// Inner
-	if (r1 == r2) return;
-
-	cur_angle = arc_start;
-	buf.SetSize(2, steps);
-	for (int i = 0; i < steps; i++) {
-		buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
-		cur_angle += step;
-	}
-	buf.Draw(GL_LINE_STRIP);
-
-	if (!needs_end_caps) return;
-
-	buf.SetSize(2, 4);
-	buf.Set(0, center + Vector2D::FromAngle(arc_start) * scale_inner);
-	buf.Set(1, center + Vector2D::FromAngle(arc_start) * scale_outer);
-	buf.Set(2, center + Vector2D::FromAngle(arc_end) * scale_inner);
-	buf.Set(3, center + Vector2D::FromAngle(arc_end) * scale_outer);
-	buf.Draw(GL_LINES);
+void OpenGLWrapper::DrawRing(Vector2D center, float r1, float r2, float ar, float arc_start, float arc_end) const
+{
+    if (r2 > r1)
+        std::swap(r1, r2);
+
+    // Arc range
+    bool needs_end_caps = arc_start != arc_end;
+
+    arc_end *= deg2rad;
+    arc_start *= deg2rad;
+    if (arc_end <= arc_start)
+        arc_end += 2.f * pi;
+    float range = arc_end - arc_start;
+
+    // Math
+    int steps = std::max<int>(((r1 + r1 * ar) * range / (2.f * pi)) * 4, 12);
+    float step = range / steps;
+    float cur_angle = arc_start;
+
+    VertexArray buf(2, steps);
+
+    Vector2D scale_inner = Vector2D(ar, 1) * r1;
+    Vector2D scale_outer = Vector2D(ar, 1) * r2;
+
+    if (fill_a != 0.f) {
+        SetModeFill();
+
+        // Annulus
+        if (r1 != r2) {
+            buf.SetSize(2, (steps + 1) * 2);
+            for (int i = 0; i <= steps; i++) {
+                Vector2D offset = Vector2D::FromAngle(cur_angle);
+                buf.Set(i * 2 + 0, center + offset * scale_inner);
+                buf.Set(i * 2 + 1, center + offset * scale_outer);
+                cur_angle += step;
+            }
+            buf.Draw(GL_QUAD_STRIP);
+        }
+        // Circle
+        else {
+            buf.SetSize(2, steps);
+            for (int i = 0; i < steps; i++) {
+                buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
+                cur_angle += step;
+            }
+            buf.Draw(GL_POLYGON);
+        }
+
+        cur_angle = arc_start;
+    }
+
+    if (line_a == 0.f) return;
+
+    // Outer
+    steps++;
+    buf.SetSize(2, steps);
+
+    SetModeLine();
+    for (int i = 0; i < steps; i++) {
+        buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_outer);
+        cur_angle += step;
+    }
+    buf.Draw(GL_LINE_STRIP);
+
+    // Inner
+    if (r1 == r2) return;
+
+    cur_angle = arc_start;
+    buf.SetSize(2, steps);
+    for (int i = 0; i < steps; i++) {
+        buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
+        cur_angle += step;
+    }
+    buf.Draw(GL_LINE_STRIP);
+
+    if (!needs_end_caps) return;
+
+    buf.SetSize(2, 4);
+    buf.Set(0, center + Vector2D::FromAngle(arc_start) * scale_inner);
+    buf.Set(1, center + Vector2D::FromAngle(arc_start) * scale_outer);
+    buf.Set(2, center + Vector2D::FromAngle(arc_end) * scale_inner);
+    buf.Set(3, center + Vector2D::FromAngle(arc_end) * scale_outer);
+    buf.Draw(GL_LINES);
 }
 
-void OpenGLWrapper::SetLineColour(wxColour col, float alpha, int width) {
-	line_r = col.Red() / 255.f;
-	line_g = col.Green() / 255.f;
-	line_b = col.Blue() / 255.f;
-	line_a = alpha;
-	line_width = width;
+void OpenGLWrapper::SetLineColour(wxColour col, float alpha, int width)
+{
+    line_r = col.Red() / 255.f;
+    line_g = col.Green() / 255.f;
+    line_b = col.Blue() / 255.f;
+    line_a = alpha;
+    line_width = width;
 }
 
-void OpenGLWrapper::SetFillColour(wxColour col, float alpha) {
-	fill_r = col.Red() / 255.f;
-	fill_g = col.Green() / 255.f;
-	fill_b = col.Blue() / 255.f;
-	fill_a = alpha;
+void OpenGLWrapper::SetFillColour(wxColour col, float alpha)
+{
+    fill_r = col.Red() / 255.f;
+    fill_g = col.Green() / 255.f;
+    fill_b = col.Blue() / 255.f;
+    fill_a = alpha;
 }
 
-void OpenGLWrapper::SetModeLine() const {
-	glColor4f(line_r, line_g, line_b, line_a);
-	glEnable(GL_BLEND);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	glLineWidth(line_width);
-	if (smooth)
-		glEnable(GL_LINE_SMOOTH);
-	else
-		glDisable(GL_LINE_SMOOTH);
+void OpenGLWrapper::SetModeLine() const
+{
+    glColor4f(line_r, line_g, line_b, line_a);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glLineWidth(line_width);
+    if (smooth)
+        glEnable(GL_LINE_SMOOTH);
+    else
+        glDisable(GL_LINE_SMOOTH);
 }
 
-void OpenGLWrapper::SetModeFill() const {
-	glColor4f(fill_r, fill_g, fill_b, fill_a);
-	if (fill_a == 1.f) glDisable(GL_BLEND);
-	else {
-		glEnable(GL_BLEND);
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	}
+void OpenGLWrapper::SetModeFill() const
+{
+    glColor4f(fill_r, fill_g, fill_b, fill_a);
+    if (fill_a == 1.f) glDisable(GL_BLEND);
+    else {
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    }
 }
 
-void OpenGLWrapper::SetInvert() {
-	glEnable(GL_COLOR_LOGIC_OP);
-	glLogicOp(GL_INVERT);
+void OpenGLWrapper::SetInvert()
+{
+    glEnable(GL_COLOR_LOGIC_OP);
+    glLogicOp(GL_INVERT);
 
-	// GL_LINE_SMOOTH combines badly with inverting
-	smooth = false;
+    // GL_LINE_SMOOTH combines badly with inverting
+    smooth = false;
 }
 
-void OpenGLWrapper::ClearInvert() {
-	glDisable(GL_COLOR_LOGIC_OP);
-	smooth = true;
+void OpenGLWrapper::ClearInvert()
+{
+    glDisable(GL_COLOR_LOGIC_OP);
+    smooth = true;
 }
 
-bool OpenGLWrapper::IsExtensionSupported(const char *ext) {
-	char *extList = (char * )glGetString(GL_EXTENSIONS);
-	return extList && !!strstr(extList, ext);
+bool OpenGLWrapper::IsExtensionSupported(const char *ext)
+{
+    char *extList = (char * )glGetString(GL_EXTENSIONS);
+    return extList && !!strstr(extList, ext);
 }
 
-void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const& lines) {
-	DrawLines(dim, &lines[0], lines.size() / dim);
+void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const &lines)
+{
+    DrawLines(dim, &lines[0], lines.size() / dim);
 }
 
-void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const& lines, size_t c_dim, std::vector<float> const& colors) {
-	glShadeModel(GL_SMOOTH);
-	glEnableClientState(GL_COLOR_ARRAY);
-	glColorPointer(c_dim, GL_FLOAT, 0, &colors[0]);
-	DrawLines(dim, &lines[0], lines.size() / dim);
-	glDisableClientState(GL_COLOR_ARRAY);
-	glShadeModel(GL_FLAT);
+void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const &lines, size_t c_dim, std::vector<float> const &colors)
+{
+    glShadeModel(GL_SMOOTH);
+    glEnableClientState(GL_COLOR_ARRAY);
+    glColorPointer(c_dim, GL_FLOAT, 0, &colors[0]);
+    DrawLines(dim, &lines[0], lines.size() / dim);
+    glDisableClientState(GL_COLOR_ARRAY);
+    glShadeModel(GL_FLAT);
 }
 
-void OpenGLWrapper::DrawLines(size_t dim, const float *lines, size_t n) {
-	SetModeLine();
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glVertexPointer(dim, GL_FLOAT, 0, lines);
-	glDrawArrays(GL_LINES, 0, n);
-	glDisableClientState(GL_VERTEX_ARRAY);
+void OpenGLWrapper::DrawLines(size_t dim, const float *lines, size_t n)
+{
+    SetModeLine();
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(dim, GL_FLOAT, 0, lines);
+    glDrawArrays(GL_LINES, 0, n);
+    glDisableClientState(GL_VERTEX_ARRAY);
 }
 
-void OpenGLWrapper::DrawLineStrip(size_t dim, std::vector<float> const& lines) {
-	SetModeLine();
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glVertexPointer(dim, GL_FLOAT, 0, &lines[0]);
-	glDrawArrays(GL_LINE_STRIP, 0, lines.size() / dim);
-	glDisableClientState(GL_VERTEX_ARRAY);
+void OpenGLWrapper::DrawLineStrip(size_t dim, std::vector<float> const &lines)
+{
+    SetModeLine();
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(dim, GL_FLOAT, 0, &lines[0]);
+    glDrawArrays(GL_LINE_STRIP, 0, lines.size() / dim);
+    glDisableClientState(GL_VERTEX_ARRAY);
 }
 
 // Substitute for glMultiDrawArrays for sub-1.4 OpenGL
 // Not required on OS X.
 #ifndef __APPLE__
-static void APIENTRY glMultiDrawArraysFallback(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) {
-	for (int i = 0; i < primcount; ++i) {
-		glDrawArrays(mode, *first++, *count++);
-	}
+static void APIENTRY glMultiDrawArraysFallback(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
+{
+    for (int i = 0; i < primcount; ++i) {
+        glDrawArrays(mode, *first++, *count++);
+    }
 }
 #endif
 
-void OpenGLWrapper::DrawMultiPolygon(std::vector<float> const& points, std::vector<int> &start, std::vector<int> &count, Vector2D video_pos, Vector2D video_size, bool invert) {
-	GL_EXT(PFNGLMULTIDRAWARRAYSPROC, glMultiDrawArrays);
+void OpenGLWrapper::DrawMultiPolygon(std::vector<float> const &points, std::vector<int> &start, std::vector<int> &count, Vector2D video_pos, Vector2D video_size, bool invert)
+{
+    GL_EXT(PFNGLMULTIDRAWARRAYSPROC, glMultiDrawArrays);
 
-	float real_line_a = line_a;
-	line_a = 0;
+    float real_line_a = line_a;
+    line_a = 0;
 
-	// The following is nonzero winding-number PIP based on stencils
+    // The following is nonzero winding-number PIP based on stencils
 
-	// Draw to stencil only
-	glEnable(GL_STENCIL_TEST);
-	glColorMask(0, 0, 0, 0);
+    // Draw to stencil only
+    glEnable(GL_STENCIL_TEST);
+    glColorMask(0, 0, 0, 0);
 
-	// GL_INCR_WRAP was added in 1.4, so instead set the entire stencil to 128
-	// and wobble from there
-	glStencilFunc(GL_NEVER, 128, 0xFF);
-	glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+    // GL_INCR_WRAP was added in 1.4, so instead set the entire stencil to 128
+    // and wobble from there
+    glStencilFunc(GL_NEVER, 128, 0xFF);
+    glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
 
-	Vector2D video_max = video_pos + video_size;
-	DrawRectangle(video_pos, video_max);
+    Vector2D video_max = video_pos + video_size;
+    DrawRectangle(video_pos, video_max);
 
-	// Increment the winding number for each forward facing triangle
-	glStencilOp(GL_INCR, GL_INCR, GL_INCR);
-	glEnable(GL_CULL_FACE);
+    // Increment the winding number for each forward facing triangle
+    glStencilOp(GL_INCR, GL_INCR, GL_INCR);
+    glEnable(GL_CULL_FACE);
 
-	glCullFace(GL_BACK);
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glVertexPointer(2, GL_FLOAT, 0, &points[0]);
-	glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size());
+    glCullFace(GL_BACK);
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(2, GL_FLOAT, 0, &points[0]);
+    glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size());
 
-	// Decrement the winding number for each backfacing triangle
-	glStencilOp(GL_DECR, GL_DECR, GL_DECR);
-	glCullFace(GL_FRONT);
-	glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size());
-	glDisable(GL_CULL_FACE);
+    // Decrement the winding number for each backfacing triangle
+    glStencilOp(GL_DECR, GL_DECR, GL_DECR);
+    glCullFace(GL_FRONT);
+    glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size());
+    glDisable(GL_CULL_FACE);
 
-	// Draw the actual rectangle
-	glColorMask(1, 1, 1, 1);
+    // Draw the actual rectangle
+    glColorMask(1, 1, 1, 1);
 
-	// VSFilter draws when the winding number is nonzero, so we want to draw the
-	// mask when the winding number is zero (where 128 is zero due to the lack of
-	// wrapping combined with unsigned numbers)
-	glStencilFunc(invert ? GL_EQUAL : GL_NOTEQUAL, 128, 0xFF);
-	DrawRectangle(video_pos, video_max);
-	glDisable(GL_STENCIL_TEST);
+    // VSFilter draws when the winding number is nonzero, so we want to draw the
+    // mask when the winding number is zero (where 128 is zero due to the lack of
+    // wrapping combined with unsigned numbers)
+    glStencilFunc(invert ? GL_EQUAL : GL_NOTEQUAL, 128, 0xFF);
+    DrawRectangle(video_pos, video_max);
+    glDisable(GL_STENCIL_TEST);
 
-	// Draw lines
-	line_a = real_line_a;
-	SetModeLine();
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glVertexPointer(2, GL_FLOAT, 0, &points[0]);
-	glMultiDrawArrays(GL_LINE_LOOP, &start[0], &count[0], start.size());
+    // Draw lines
+    line_a = real_line_a;
+    SetModeLine();
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(2, GL_FLOAT, 0, &points[0]);
+    glMultiDrawArrays(GL_LINE_LOOP, &start[0], &count[0], start.size());
 
-	glDisableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_VERTEX_ARRAY);
 }
 
-void OpenGLWrapper::SetOrigin(Vector2D origin) {
-	PrepareTransform();
-	glTranslatef(origin.X(), origin.Y(), -1.f);
+void OpenGLWrapper::SetOrigin(Vector2D origin)
+{
+    PrepareTransform();
+    glTranslatef(origin.X(), origin.Y(), -1.f);
 }
 
-void OpenGLWrapper::SetScale(Vector2D scale) {
-	PrepareTransform();
-	glScalef(scale.X() / 100.f, scale.Y() / 100.f, 1.f);
+void OpenGLWrapper::SetScale(Vector2D scale)
+{
+    PrepareTransform();
+    glScalef(scale.X() / 100.f, scale.Y() / 100.f, 1.f);
 }
 
-void OpenGLWrapper::SetRotation(float x, float y, float z) {
-	PrepareTransform();
-	float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 };
-	glMultMatrixf(matrix);
-	glScalef(1.f, 1.f, 8.f);
-	glRotatef(y, 0.f, -1.f, 0.f);
-	glRotatef(x, -1.f, 0.f, 0.f);
-	glRotatef(z, 0.f, 0.f, -1.f);
+void OpenGLWrapper::SetRotation(float x, float y, float z)
+{
+    PrepareTransform();
+    float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 };
+    glMultMatrixf(matrix);
+    glScalef(1.f, 1.f, 8.f);
+    glRotatef(y, 0.f, -1.f, 0.f);
+    glRotatef(x, -1.f, 0.f, 0.f);
+    glRotatef(z, 0.f, 0.f, -1.f);
 }
 
-void OpenGLWrapper::SetShear(float x, float y) {
-	PrepareTransform();
-	float matrix[16] = {
-		1, y, 0, 0,
-		x, 1, 0, 0,
-		0, 0, 1, 0,
-		0, 0, 0, 1
-	};
-	glMultMatrixf(matrix);
+void OpenGLWrapper::SetShear(float x, float y)
+{
+    PrepareTransform();
+    float matrix[16] = {
+        1, y, 0, 0,
+        x, 1, 0, 0,
+        0, 0, 1, 0,
+        0, 0, 0, 1
+    };
+    glMultMatrixf(matrix);
 }
 
-void OpenGLWrapper::PrepareTransform() {
-	if (!transform_pushed) {
-		glMatrixMode(GL_MODELVIEW);
-		glPushMatrix();
-		glLoadIdentity();
-		transform_pushed = true;
-	}
+void OpenGLWrapper::PrepareTransform()
+{
+    if (!transform_pushed) {
+        glMatrixMode(GL_MODELVIEW);
+        glPushMatrix();
+        glLoadIdentity();
+        transform_pushed = true;
+    }
 }
 
-void OpenGLWrapper::ResetTransform() {
-	if (transform_pushed) {
-		glPopMatrix();
-		transform_pushed = false;
-	}
+void OpenGLWrapper::ResetTransform()
+{
+    if (transform_pushed) {
+        glPopMatrix();
+        transform_pushed = false;
+    }
 }
diff --git a/src/gl_wrap.h b/src/gl_wrap.h
index 4e3cd34e94f99760a7bf4a2a560af2125c90cd9b..b88f3c9ce92074aac91dc8099121434829e7e5ee 100644
--- a/src/gl_wrap.h
+++ b/src/gl_wrap.h
@@ -26,53 +26,53 @@
 class wxColour;
 
 class OpenGLWrapper {
-	float line_r, line_g, line_b, line_a;
-	float fill_r, fill_g, fill_b, fill_a;
+    float line_r, line_g, line_b, line_a;
+    float fill_r, fill_g, fill_b, fill_a;
 
-	int line_width;
-	bool smooth;
+    int line_width;
+    bool smooth;
 
-	bool transform_pushed;
-	void PrepareTransform();
+    bool transform_pushed;
+    void PrepareTransform();
 
 public:
-	OpenGLWrapper();
+    OpenGLWrapper();
 
-	void SetLineColour(wxColour col, float alpha = 1.0f, int width = 1);
-	void SetFillColour(wxColour col, float alpha = 1.0f);
-	void SetModeLine() const;
-	void SetModeFill() const;
+    void SetLineColour(wxColour col, float alpha = 1.0f, int width = 1);
+    void SetFillColour(wxColour col, float alpha = 1.0f);
+    void SetModeLine() const;
+    void SetModeFill() const;
 
-	void SetInvert();
-	void ClearInvert();
+    void SetInvert();
+    void ClearInvert();
 
-	void SetScale(Vector2D scale);
-	void SetOrigin(Vector2D origin);
-	void SetRotation(float x, float y, float z);
-	void SetShear(float x, float y);
-	void ResetTransform();
+    void SetScale(Vector2D scale);
+    void SetOrigin(Vector2D origin);
+    void SetRotation(float x, float y, float z);
+    void SetShear(float x, float y);
+    void ResetTransform();
 
-	void DrawLine(Vector2D p1, Vector2D p2) const;
-	void DrawDashedLine(Vector2D p1, Vector2D p2, float dashLen) const;
-	void DrawEllipse(Vector2D center, Vector2D radius) const;
-	void DrawCircle(Vector2D center, float radius) const { DrawEllipse(center, Vector2D(radius, radius)); }
-	void DrawRectangle(Vector2D p1, Vector2D p2) const;
-	void DrawRing(Vector2D center, float r1, float r2, float ar = 1.0f, float arcStart = 0.0f, float arcEnd = 0.0f) const;
-	void DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const;
+    void DrawLine(Vector2D p1, Vector2D p2) const;
+    void DrawDashedLine(Vector2D p1, Vector2D p2, float dashLen) const;
+    void DrawEllipse(Vector2D center, Vector2D radius) const;
+    void DrawCircle(Vector2D center, float radius) const { DrawEllipse(center, Vector2D(radius, radius)); }
+    void DrawRectangle(Vector2D p1, Vector2D p2) const;
+    void DrawRing(Vector2D center, float r1, float r2, float ar = 1.0f, float arcStart = 0.0f, float arcEnd = 0.0f) const;
+    void DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const;
 
-	void DrawLines(size_t dim, std::vector<float> const& lines);
-	void DrawLines(size_t dim, std::vector<float> const& lines, size_t c_dim, std::vector<float> const& colors);
-	void DrawLines(size_t dim, const float *lines, size_t n);
-	void DrawLineStrip(size_t dim, std::vector<float> const& lines);
+    void DrawLines(size_t dim, std::vector<float> const &lines);
+    void DrawLines(size_t dim, std::vector<float> const &lines, size_t c_dim, std::vector<float> const &colors);
+    void DrawLines(size_t dim, const float *lines, size_t n);
+    void DrawLineStrip(size_t dim, std::vector<float> const &lines);
 
-	/// Draw a multipolygon serialized into a single array
-	/// @param points List of coordinates
-	/// @param start Indices in points which are the start of a new polygon
-	/// @param count Number of points in each polygon
-	/// @param video_pos Top-left corner of the visible area
-	/// @param video_size Bottom-right corner of the visible area
-	/// @param invert Draw the area outside the polygons instead
-	void DrawMultiPolygon(std::vector<float> const& points, std::vector<int> &start, std::vector<int> &count, Vector2D video_pos, Vector2D video_size, bool invert);
+    /// Draw a multipolygon serialized into a single array
+    /// @param points List of coordinates
+    /// @param start Indices in points which are the start of a new polygon
+    /// @param count Number of points in each polygon
+    /// @param video_pos Top-left corner of the visible area
+    /// @param video_size Bottom-right corner of the visible area
+    /// @param invert Draw the area outside the polygons instead
+    void DrawMultiPolygon(std::vector<float> const &points, std::vector<int> &start, std::vector<int> &count, Vector2D video_pos, Vector2D video_size, bool invert);
 
-	static bool IsExtensionSupported(const char *ext);
+    static bool IsExtensionSupported(const char *ext);
 };
diff --git a/src/grid_column.cpp b/src/grid_column.cpp
index 321a09439f615328179dd0c7da2895f2e9b2ab50..683f107816d19335324ee5ad065606379ee60849 100644
--- a/src/grid_column.cpp
+++ b/src/grid_column.cpp
@@ -27,70 +27,78 @@
 
 #include <wx/dc.h>
 
-void WidthHelper::Age() {
-	for (auto it = begin(widths), e = end(widths); it != e; ) {
-		if (it->second.age == age)
-			++it;
-		else
-			it = widths.erase(it);
-	}
-	++age;
+void WidthHelper::Age()
+{
+    for (auto it = begin(widths), e = end(widths); it != e; ) {
+        if (it->second.age == age)
+            ++it;
+        else
+            it = widths.erase(it);
+    }
+    ++age;
 }
 
-int WidthHelper::operator()(boost::flyweight<std::string> const& str) {
-	if (str.get().empty()) return 0;
-	auto it = widths.find(str);
-	if (it != end(widths)) {
-		it->second.age = age;
-		return it->second.width;
-	}
+int WidthHelper::operator()(boost::flyweight<std::string> const &str)
+{
+    if (str.get().empty()) return 0;
+    auto it = widths.find(str);
+    if (it != end(widths)) {
+        it->second.age = age;
+        return it->second.width;
+    }
 
 #ifdef _WIN32
-	wxMBConvUTF8 conv;
-	size_t len = conv.ToWChar(nullptr, 0, str.get().c_str(), str.get().size());
-	scratch.resize(len);
-	conv.ToWChar(const_cast<wchar_t *>(scratch.wx_str()), len, str.get().c_str(), str.get().size());
-	int width = dc->GetTextExtent(scratch).GetWidth();
+    wxMBConvUTF8 conv;
+    size_t len = conv.ToWChar(nullptr, 0, str.get().c_str(), str.get().size());
+    scratch.resize(len);
+    conv.ToWChar(const_cast<wchar_t *>(scratch.wx_str()), len, str.get().c_str(), str.get().size());
+    int width = dc->GetTextExtent(scratch).GetWidth();
 #else
-	int width = dc->GetTextExtent(to_wx(str)).GetWidth();
+    int width = dc->GetTextExtent(to_wx(str)).GetWidth();
 #endif
 
-	widths[str] = {width, age};
-	return width;
+    widths[str] = {width, age};
+    return width;
 }
 
-int WidthHelper::operator()(std::string const& str) {
-	return dc->GetTextExtent(to_wx(str)).GetWidth();
+int WidthHelper::operator()(std::string const &str)
+{
+    return dc->GetTextExtent(to_wx(str)).GetWidth();
 }
 
-int WidthHelper::operator()(wxString const& str) {
-	return dc->GetTextExtent(str).GetWidth();
+int WidthHelper::operator()(wxString const &str)
+{
+    return dc->GetTextExtent(str).GetWidth();
 }
 
-int WidthHelper::operator()(const char *str) {
-	return dc->GetTextExtent(wxString::FromUTF8(str)).GetWidth();
+int WidthHelper::operator()(const char *str)
+{
+    return dc->GetTextExtent(wxString::FromUTF8(str)).GetWidth();
 }
 
-int WidthHelper::operator()(const wchar_t *str) {
-	return dc->GetTextExtent(str).GetWidth();
+int WidthHelper::operator()(const wchar_t *str)
+{
+    return dc->GetTextExtent(str).GetWidth();
 }
 
-void GridColumn::UpdateWidth(const agi::Context *c, WidthHelper &helper) {
-	if (!visible) {
-		width = 0;
-		return;
-	}
+void GridColumn::UpdateWidth(const agi::Context *c, WidthHelper &helper)
+{
+    if (!visible) {
+        width = 0;
+        return;
+    }
 
-	width = Width(c, helper);
-	if (width) // 10 is an arbitrary amount of padding
-		width = 10 + std::max(width, helper(Header()));
+    width = Width(c, helper);
+    if (width) // 10 is an arbitrary amount of padding
+        width = 10 + std::max(width, helper(Header()));
 }
 
-void GridColumn::Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *c) const {
-	wxString str = Value(d, c);
-	if (Centered())
-		x += (width - 6 - dc.GetTextExtent(str).GetWidth()) / 2;
-	dc.DrawText(str, x + 4, y + 2);
+void GridColumn::Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *c) const
+{
+    wxString str = Value(d, c);
+    if (Centered())
+        x += (width - 6 - dc.GetTextExtent(str).GetWidth()) / 2;
+    dc.DrawText(str, x + 4, y + 2);
 }
 
 namespace {
@@ -102,323 +110,327 @@ namespace {
 	public: wxString const& Description() const override { return description; }
 
 struct GridColumnLineNumber final : GridColumn {
-	COLUMN_HEADER(_("#"))
-	COLUMN_DESCRIPTION(_("Line Number"))
-	bool Centered() const override { return true; }
+    COLUMN_HEADER(_("#"))
+    COLUMN_DESCRIPTION(_("Line Number"))
+    bool Centered() const override { return true; }
 
-	wxString Value(const AssDialogue *d, const agi::Context * = nullptr) const override {
-		return std::to_wstring(d->Row + 1);
-	}
+    wxString Value(const AssDialogue *d, const agi::Context * = nullptr) const override {
+        return std::to_wstring(d->Row + 1);
+    }
 
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return helper(Value(&c->ass->Events.back()));
-	}
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return helper(Value(&c->ass->Events.back()));
+    }
 };
 
 template<typename T>
-T max_value(T AssDialogueBase::*field, EntryList<AssDialogue> const& lines) {
-	T value = 0;
-	for (AssDialogue const& line : lines) {
-		if (line.*field > value)
-			value = line.*field;
-	}
-	return value;
+T max_value(T AssDialogueBase::*field, EntryList<AssDialogue> const &lines)
+{
+    T value = 0;
+    for (AssDialogue const &line : lines) {
+        if (line.*field > value)
+            value = line.*field;
+    }
+    return value;
 }
 
 struct GridColumnLayer final : GridColumn {
-	COLUMN_HEADER(_("L"))
-	COLUMN_DESCRIPTION(_("Layer"))
-	bool Centered() const override { return true; }
-
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		return d->Layer ? wxString(std::to_wstring(d->Layer)) : wxString();
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		int max_layer = max_value(&AssDialogue::Layer, c->ass->Events);
-		return max_layer == 0 ? 0 : helper(std::to_wstring(max_layer));
-	}
+    COLUMN_HEADER(_("L"))
+    COLUMN_DESCRIPTION(_("Layer"))
+    bool Centered() const override { return true; }
+
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        return d->Layer ? wxString(std::to_wstring(d->Layer)) : wxString();
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        int max_layer = max_value(&AssDialogue::Layer, c->ass->Events);
+        return max_layer == 0 ? 0 : helper(std::to_wstring(max_layer));
+    }
 };
 
 struct GridColumnTime : GridColumn {
-	bool by_frame = false;
+    bool by_frame = false;
 
-	bool Centered() const override { return true; }
-	void SetByFrame(bool by_frame) override { this->by_frame = by_frame; }
+    bool Centered() const override { return true; }
+    void SetByFrame(bool by_frame) override { this->by_frame = by_frame; }
 };
 
 struct GridColumnStartTime final : GridColumnTime {
-	COLUMN_HEADER(_("Start"))
-	COLUMN_DESCRIPTION(_("Start Time"))
-
-	wxString Value(const AssDialogue *d, const agi::Context *c) const override {
-		if (by_frame)
-			return std::to_wstring(c->videoController->FrameAtTime(d->Start, agi::vfr::START));
-		return to_wx(d->Start.GetAssFormatted());
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		if (!by_frame)
-			return helper(wxS("0:00:00.00"));
-		int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::Start, c->ass->Events), agi::vfr::START);
-		return helper(std::to_wstring(frame));
-	}
+    COLUMN_HEADER(_("Start"))
+    COLUMN_DESCRIPTION(_("Start Time"))
+
+    wxString Value(const AssDialogue *d, const agi::Context *c) const override {
+        if (by_frame)
+            return std::to_wstring(c->videoController->FrameAtTime(d->Start, agi::vfr::START));
+        return to_wx(d->Start.GetAssFormatted());
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        if (!by_frame)
+            return helper(wxS("0:00:00.00"));
+        int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::Start, c->ass->Events), agi::vfr::START);
+        return helper(std::to_wstring(frame));
+    }
 };
 
 struct GridColumnEndTime final : GridColumnTime {
-	COLUMN_HEADER(_("End"))
-	COLUMN_DESCRIPTION(_("End Time"))
-
-	wxString Value(const AssDialogue *d, const agi::Context *c) const override {
-		if (by_frame)
-			return std::to_wstring(c->videoController->FrameAtTime(d->End, agi::vfr::END));
-		return to_wx(d->End.GetAssFormatted());
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		if (!by_frame)
-			return helper(wxS("0:00:00.00"));
-		int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::End, c->ass->Events), agi::vfr::END);
-		return helper(std::to_wstring(frame));
-	}
+    COLUMN_HEADER(_("End"))
+    COLUMN_DESCRIPTION(_("End Time"))
+
+    wxString Value(const AssDialogue *d, const agi::Context *c) const override {
+        if (by_frame)
+            return std::to_wstring(c->videoController->FrameAtTime(d->End, agi::vfr::END));
+        return to_wx(d->End.GetAssFormatted());
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        if (!by_frame)
+            return helper(wxS("0:00:00.00"));
+        int frame = c->videoController->FrameAtTime(max_value(&AssDialogue::End, c->ass->Events), agi::vfr::END);
+        return helper(std::to_wstring(frame));
+    }
 };
 
 template<typename T>
-int max_width(T AssDialogueBase::*field, EntryList<AssDialogue> const& lines, WidthHelper &helper) {
-	int w = 0;
-	for (AssDialogue const& line : lines) {
-		auto const& v = line.*field;
-		if (v.get().empty()) continue;
-		int width = helper(v);
-		if (width > w)
-			w = width;
-	}
-	return w;
+int max_width(T AssDialogueBase::*field, EntryList<AssDialogue> const &lines, WidthHelper &helper)
+{
+    int w = 0;
+    for (AssDialogue const &line : lines) {
+        auto const &v = line.*field;
+        if (v.get().empty()) continue;
+        int width = helper(v);
+        if (width > w)
+            w = width;
+    }
+    return w;
 }
 
 struct GridColumnStyle final : GridColumn {
-	COLUMN_HEADER(_("Style"))
-	COLUMN_DESCRIPTION(_("Style"))
-	bool Centered() const override { return false; }
+    COLUMN_HEADER(_("Style"))
+    COLUMN_DESCRIPTION(_("Style"))
+    bool Centered() const override { return false; }
 
-	wxString Value(const AssDialogue *d, const agi::Context *c) const override {
-		return to_wx(d->Style);
-	}
+    wxString Value(const AssDialogue *d, const agi::Context *c) const override {
+        return to_wx(d->Style);
+    }
 
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return max_width(&AssDialogue::Style, c->ass->Events, helper);
-	}
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return max_width(&AssDialogue::Style, c->ass->Events, helper);
+    }
 };
 
 struct GridColumnEffect final : GridColumn {
-	COLUMN_HEADER(_("Effect"))
-	COLUMN_DESCRIPTION(_("Effect"))
-	bool Centered() const override { return false; }
+    COLUMN_HEADER(_("Effect"))
+    COLUMN_DESCRIPTION(_("Effect"))
+    bool Centered() const override { return false; }
 
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		return to_wx(d->Effect);
-	}
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        return to_wx(d->Effect);
+    }
 
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return max_width(&AssDialogue::Effect, c->ass->Events, helper);
-	}
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return max_width(&AssDialogue::Effect, c->ass->Events, helper);
+    }
 };
 
 struct GridColumnActor final : GridColumn {
-	COLUMN_HEADER(_("Actor"))
-	COLUMN_DESCRIPTION(_("Actor"))
-	bool Centered() const override { return false; }
+    COLUMN_HEADER(_("Actor"))
+    COLUMN_DESCRIPTION(_("Actor"))
+    bool Centered() const override { return false; }
 
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		return to_wx(d->Actor);
-	}
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        return to_wx(d->Actor);
+    }
 
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return max_width(&AssDialogue::Actor, c->ass->Events, helper);
-	}
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return max_width(&AssDialogue::Actor, c->ass->Events, helper);
+    }
 };
 
 struct GridColumnMargin : GridColumn {
-	int index;
-	GridColumnMargin(int index) : index(index) { }
-
-	bool Centered() const override { return true; }
-
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		return d->Margin[index] ? wxString(std::to_wstring(d->Margin[index])) : wxString();
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		int max = 0;
-		for (AssDialogue const& line : c->ass->Events) {
-			if (line.Margin[index] > max)
-				max = line.Margin[index];
-		}
-		return max == 0 ? 0 : helper(std::to_wstring(max));
-	}
+    int index;
+    GridColumnMargin(int index) : index(index) { }
+
+    bool Centered() const override { return true; }
+
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        return d->Margin[index] ? wxString(std::to_wstring(d->Margin[index])) : wxString();
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        int max = 0;
+        for (AssDialogue const &line : c->ass->Events) {
+            if (line.Margin[index] > max)
+                max = line.Margin[index];
+        }
+        return max == 0 ? 0 : helper(std::to_wstring(max));
+    }
 };
 
 struct GridColumnMarginLeft final : GridColumnMargin {
-	GridColumnMarginLeft() : GridColumnMargin(0) { }
-	COLUMN_HEADER(_("Left"))
-	COLUMN_DESCRIPTION(_("Left Margin"))
+    GridColumnMarginLeft() : GridColumnMargin(0) { }
+    COLUMN_HEADER(_("Left"))
+    COLUMN_DESCRIPTION(_("Left Margin"))
 };
 
 struct GridColumnMarginRight final : GridColumnMargin {
-	GridColumnMarginRight() : GridColumnMargin(1) { }
-	COLUMN_HEADER(_("Right"))
-	COLUMN_DESCRIPTION(_("Right Margin"))
+    GridColumnMarginRight() : GridColumnMargin(1) { }
+    COLUMN_HEADER(_("Right"))
+    COLUMN_DESCRIPTION(_("Right Margin"))
 };
 
 struct GridColumnMarginVert final : GridColumnMargin {
-	GridColumnMarginVert() : GridColumnMargin(2) { }
-	COLUMN_HEADER(_("Vert"))
-	COLUMN_DESCRIPTION(_("Vertical Margin"))
+    GridColumnMarginVert() : GridColumnMargin(2) { }
+    COLUMN_HEADER(_("Vert"))
+    COLUMN_DESCRIPTION(_("Vertical Margin"))
 };
 
-wxColor blend(wxColor fg, wxColor bg, double alpha) {
-	return wxColor(
-		wxColor::AlphaBlend(fg.Red(), bg.Red(), alpha),
-		wxColor::AlphaBlend(fg.Green(), bg.Green(), alpha),
-		wxColor::AlphaBlend(fg.Blue(), bg.Blue(), alpha));
+wxColor blend(wxColor fg, wxColor bg, double alpha)
+{
+    return wxColor(
+               wxColor::AlphaBlend(fg.Red(), bg.Red(), alpha),
+               wxColor::AlphaBlend(fg.Green(), bg.Green(), alpha),
+               wxColor::AlphaBlend(fg.Blue(), bg.Blue(), alpha));
 }
 
 class GridColumnCPS final : public GridColumn {
-	const agi::OptionValue *ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace");
-	const agi::OptionValue *ignore_punctuation = OPT_GET("Subtitle/Character Counter/Ignore Punctuation");
-	const agi::OptionValue *cps_warn = OPT_GET("Subtitle/Character Counter/CPS Warning Threshold");
-	const agi::OptionValue *cps_error = OPT_GET("Subtitle/Character Counter/CPS Error Threshold");
-	const agi::OptionValue *bg_color = OPT_GET("Colour/Subtitle Grid/CPS Error");
+    const agi::OptionValue *ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace");
+    const agi::OptionValue *ignore_punctuation = OPT_GET("Subtitle/Character Counter/Ignore Punctuation");
+    const agi::OptionValue *cps_warn = OPT_GET("Subtitle/Character Counter/CPS Warning Threshold");
+    const agi::OptionValue *cps_error = OPT_GET("Subtitle/Character Counter/CPS Error Threshold");
+    const agi::OptionValue *bg_color = OPT_GET("Colour/Subtitle Grid/CPS Error");
 
 public:
-	COLUMN_HEADER(_("CPS"))
-	COLUMN_DESCRIPTION(_("Characters Per Second"))
-	bool Centered() const override { return true; }
-	bool RefreshOnTextChange() const override { return true; }
-
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		return wxS("");
-	}
-
-	int CPS(const AssDialogue *d) const {
-		int duration = d->End - d->Start;
-		auto const& text = d->Text.get();
-
-		if (duration <= 100 || text.size() > static_cast<size_t>(duration))
-			return -1;
-
-		int ignore = agi::IGNORE_BLOCKS;
-		if (ignore_whitespace->GetBool())
-			ignore |= agi::IGNORE_WHITESPACE;
-		if (ignore_punctuation->GetBool())
-			ignore |= agi::IGNORE_PUNCTUATION;
-
-		return agi::CharacterCount(text, ignore) * 1000 / duration;
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return helper(wxS("999"));
-	}
-
-	void Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *) const override {
-		int cps = CPS(d);
-		if (cps < 0 || cps > 100) return;
-
-		wxString str = std::to_wstring(cps);
-		wxSize ext = dc.GetTextExtent(str);
-		auto tc = dc.GetTextForeground();
-
-		int cps_min = cps_warn->GetInt();
-		int cps_max = std::max<int>(cps_min, cps_error->GetInt());
-		if (cps > cps_min) {
-			double alpha = std::min((double)(cps - cps_min + 1) / (cps_max - cps_min + 1), 1.0);
-			dc.SetBrush(wxBrush(blend(to_wx(bg_color->GetColor()), dc.GetBrush().GetColour(), alpha)));
-			dc.SetPen(*wxTRANSPARENT_PEN);
-			dc.DrawRectangle(x, y + 1, width, ext.GetHeight() + 3);
-			dc.SetTextForeground(blend(*wxBLACK, tc, alpha));
-		}
-
-		x += (width + 2 - ext.GetWidth()) / 2;
-		dc.DrawText(str, x, y + 2);
-		dc.SetTextForeground(tc);
-	}
+    COLUMN_HEADER(_("CPS"))
+    COLUMN_DESCRIPTION(_("Characters Per Second"))
+    bool Centered() const override { return true; }
+    bool RefreshOnTextChange() const override { return true; }
+
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        return wxS("");
+    }
+
+    int CPS(const AssDialogue *d) const {
+        int duration = d->End - d->Start;
+        auto const &text = d->Text.get();
+
+        if (duration <= 100 || text.size() > static_cast<size_t>(duration))
+            return -1;
+
+        int ignore = agi::IGNORE_BLOCKS;
+        if (ignore_whitespace->GetBool())
+            ignore |= agi::IGNORE_WHITESPACE;
+        if (ignore_punctuation->GetBool())
+            ignore |= agi::IGNORE_PUNCTUATION;
+
+        return agi::CharacterCount(text, ignore) * 1000 / duration;
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return helper(wxS("999"));
+    }
+
+    void Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *) const override {
+        int cps = CPS(d);
+        if (cps < 0 || cps > 100) return;
+
+        wxString str = std::to_wstring(cps);
+        wxSize ext = dc.GetTextExtent(str);
+        auto tc = dc.GetTextForeground();
+
+        int cps_min = cps_warn->GetInt();
+        int cps_max = std::max<int>(cps_min, cps_error->GetInt());
+        if (cps > cps_min) {
+            double alpha = std::min((double)(cps - cps_min + 1) / (cps_max - cps_min + 1), 1.0);
+            dc.SetBrush(wxBrush(blend(to_wx(bg_color->GetColor()), dc.GetBrush().GetColour(), alpha)));
+            dc.SetPen(*wxTRANSPARENT_PEN);
+            dc.DrawRectangle(x, y + 1, width, ext.GetHeight() + 3);
+            dc.SetTextForeground(blend(*wxBLACK, tc, alpha));
+        }
+
+        x += (width + 2 - ext.GetWidth()) / 2;
+        dc.DrawText(str, x, y + 2);
+        dc.SetTextForeground(tc);
+    }
 };
 
 class GridColumnText final : public GridColumn {
-	const agi::OptionValue *override_mode;
-	wxString replace_char;
+    const agi::OptionValue *override_mode;
+    wxString replace_char;
 
-	agi::signal::Connection replace_char_connection;
+    agi::signal::Connection replace_char_connection;
 
 public:
-	GridColumnText()
-	: override_mode(OPT_GET("Subtitle/Grid/Hide Overrides"))
-	, replace_char(to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString()))
-	, replace_char_connection(OPT_SUB("Subtitle/Grid/Hide Overrides Char",
-		[&](agi::OptionValue const& v) { replace_char = to_wx(v.GetString()); }))
-	{
-	}
-
-	COLUMN_HEADER(_("Text"))
-	COLUMN_DESCRIPTION(_("Text"))
-	bool Centered() const override { return false; }
-	bool CanHide() const override { return false; }
-	bool RefreshOnTextChange() const override { return true; }
-
-	wxString Value(const AssDialogue *d, const agi::Context *) const override {
-		wxString str;
-		int mode = override_mode->GetInt();
-
-		// Show overrides
-		if (mode == 0)
-			str = to_wx(d->Text);
-		// Hidden overrides
-		else {
-			auto const& text = d->Text.get();
-			str.reserve(text.size());
-			size_t start = 0, pos;
-			while ((pos = text.find('{', start)) != std::string::npos) {
-				str += to_wx(text.substr(start, pos - start));
-				if (mode == 1)
-					str += replace_char;
-				start = text.find('}', pos);
-				if (start != std::string::npos) ++start;
-			}
-			if (start != std::string::npos)
-				str += to_wx(text.substr(start));
-		}
-
-		// Cap length and set text
-		if (str.size() > 512)
-			str = str.Left(512) + "...";
-		return str;
-	}
-
-	int Width(const agi::Context *c, WidthHelper &helper) const override {
-		return 5000;
-	}
+    GridColumnText()
+        : override_mode(OPT_GET("Subtitle/Grid/Hide Overrides"))
+        , replace_char(to_wx(OPT_GET("Subtitle/Grid/Hide Overrides Char")->GetString()))
+        , replace_char_connection(OPT_SUB("Subtitle/Grid/Hide Overrides Char",
+                                          [ & ](agi::OptionValue const & v) { replace_char = to_wx(v.GetString()); })) {
+    }
+
+    COLUMN_HEADER(_("Text"))
+    COLUMN_DESCRIPTION(_("Text"))
+    bool Centered() const override { return false; }
+    bool CanHide() const override { return false; }
+    bool RefreshOnTextChange() const override { return true; }
+
+    wxString Value(const AssDialogue *d, const agi::Context *) const override {
+        wxString str;
+        int mode = override_mode->GetInt();
+
+        // Show overrides
+        if (mode == 0)
+            str = to_wx(d->Text);
+        // Hidden overrides
+        else {
+            auto const &text = d->Text.get();
+            str.reserve(text.size());
+            size_t start = 0, pos;
+            while ((pos = text.find('{', start)) != std::string::npos) {
+                str += to_wx(text.substr(start, pos - start));
+                if (mode == 1)
+                    str += replace_char;
+                start = text.find('}', pos);
+                if (start != std::string::npos) ++start;
+            }
+            if (start != std::string::npos)
+                str += to_wx(text.substr(start));
+        }
+
+        // Cap length and set text
+        if (str.size() > 512)
+            str = str.Left(512) + "...";
+        return str;
+    }
+
+    int Width(const agi::Context *c, WidthHelper &helper) const override {
+        return 5000;
+    }
 };
 
 template<typename T>
-std::unique_ptr<GridColumn> make() {
-	return std::unique_ptr<GridColumn>(new T);
+std::unique_ptr<GridColumn> make()
+{
+    return std::unique_ptr<GridColumn>(new T);
 }
 
 }
 
-std::vector<std::unique_ptr<GridColumn>> GetGridColumns() {
-	std::vector<std::unique_ptr<GridColumn>> ret;
-	ret.push_back(make<GridColumnLineNumber>());
-	ret.push_back(make<GridColumnLayer>());
-	ret.push_back(make<GridColumnStartTime>());
-	ret.push_back(make<GridColumnEndTime>());
-	ret.push_back(make<GridColumnCPS>());
-	ret.push_back(make<GridColumnStyle>());
-	ret.push_back(make<GridColumnActor>());
-	ret.push_back(make<GridColumnEffect>());
-	ret.push_back(make<GridColumnMarginLeft>());
-	ret.push_back(make<GridColumnMarginRight>());
-	ret.push_back(make<GridColumnMarginVert>());
-	ret.push_back(make<GridColumnText>());
-	return ret;
+std::vector<std::unique_ptr<GridColumn>> GetGridColumns()
+{
+    std::vector<std::unique_ptr<GridColumn>> ret;
+    ret.push_back(make<GridColumnLineNumber>());
+    ret.push_back(make<GridColumnLayer>());
+    ret.push_back(make<GridColumnStartTime>());
+    ret.push_back(make<GridColumnEndTime>());
+    ret.push_back(make<GridColumnCPS>());
+    ret.push_back(make<GridColumnStyle>());
+    ret.push_back(make<GridColumnActor>());
+    ret.push_back(make<GridColumnEffect>());
+    ret.push_back(make<GridColumnMarginLeft>());
+    ret.push_back(make<GridColumnMarginRight>());
+    ret.push_back(make<GridColumnMarginVert>());
+    ret.push_back(make<GridColumnText>());
+    return ret;
 }
diff --git a/src/grid_column.h b/src/grid_column.h
index 16b70f2a915e7a35be11b26688be6cce25ccd3c7..24255139ee576da44b1d1c81e7942f366a156b0a 100644
--- a/src/grid_column.h
+++ b/src/grid_column.h
@@ -27,53 +27,53 @@ class wxString;
 namespace agi { struct Context; }
 
 class WidthHelper {
-	struct Entry {
-		int width;
-		int age;
-	};
-	int age = 0;
-	wxDC *dc = nullptr;
-	std::unordered_map<boost::flyweight<std::string>, Entry> widths;
+    struct Entry {
+        int width;
+        int age;
+    };
+    int age = 0;
+    wxDC *dc = nullptr;
+    std::unordered_map<boost::flyweight<std::string>, Entry> widths;
 #ifdef _WIN32
-	wxString scratch;
+    wxString scratch;
 #endif
 
 public:
-	void SetDC(wxDC *dc) { this->dc = dc; }
-	void Age();
+    void SetDC(wxDC *dc) { this->dc = dc; }
+    void Age();
 
-	int operator()(boost::flyweight<std::string> const& str);
-	int operator()(std::string const& str);
-	int operator()(wxString const& str);
-	int operator()(const char *str);
-	int operator()(const wchar_t *str);
+    int operator()(boost::flyweight<std::string> const &str);
+    int operator()(std::string const &str);
+    int operator()(wxString const &str);
+    int operator()(const char *str);
+    int operator()(const wchar_t *str);
 };
 
 class GridColumn {
 protected:
-	int width = 0;
-	bool visible = true;
+    int width = 0;
+    bool visible = true;
 
-	virtual int Width(const agi::Context *c, WidthHelper &helper) const = 0;
-	virtual wxString Value(const AssDialogue *d, const agi::Context *c) const = 0;
+    virtual int Width(const agi::Context *c, WidthHelper &helper) const = 0;
+    virtual wxString Value(const AssDialogue *d, const agi::Context *c) const = 0;
 
 public:
-	virtual ~GridColumn() = default;
+    virtual ~GridColumn() = default;
 
-	virtual bool Centered() const { return false; }
-	virtual bool CanHide() const { return true; }
-	virtual bool RefreshOnTextChange() const { return false; }
+    virtual bool Centered() const { return false; }
+    virtual bool CanHide() const { return true; }
+    virtual bool RefreshOnTextChange() const { return false; }
 
-	virtual wxString const& Header() const = 0;
-	virtual wxString const& Description() const = 0;
-	virtual void Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *c) const;
+    virtual wxString const &Header() const = 0;
+    virtual wxString const &Description() const = 0;
+    virtual void Paint(wxDC &dc, int x, int y, const AssDialogue *d, const agi::Context *c) const;
 
-	int Width() const { return width; }
-	bool Visible() const { return visible; }
+    int Width() const { return width; }
+    bool Visible() const { return visible; }
 
-	virtual void UpdateWidth(const agi::Context *c, WidthHelper &helper);
-	virtual void SetByFrame(bool /* by_frame */) { }
-	void SetVisible(bool new_value) { visible = new_value; }
+    virtual void UpdateWidth(const agi::Context *c, WidthHelper &helper);
+    virtual void SetByFrame(bool /* by_frame */) { }
+    void SetVisible(bool new_value) { visible = new_value; }
 };
 
 std::vector<std::unique_ptr<GridColumn>> GetGridColumns();
diff --git a/src/help_button.cpp b/src/help_button.cpp
index 9c84686b79cafd2e5383e416e9f55747624a7748..67ccb50f82a5e42868b8fa47fb733fbc95e35edc 100644
--- a/src/help_button.cpp
+++ b/src/help_button.cpp
@@ -36,52 +36,54 @@
 #include <algorithm>
 
 static const char *pages[][2] = {
-	{"Attachment Manager", "Attachment_Manager"},
-	{"Automation Manager", "Automation/Manager"},
-	{"Colour Picker", "Colour_Picker"},
-	{"Dummy Video", "Video#dummy-video"},
-	{"Export", "Exporting"},
-	{"Fonts Collector", "Fonts_Collector"},
-	{"Kanji Timer", "Kanji_Timer"},
-	{"Main", "Main_Page"},
-	{"Options", "Options"},
-	{"Paste Over", "Paste_Over"},
-	{"Properties", "Properties"},
-	{"Resample resolution", "Resolution_Resampler"},
-	{"Resolution mismatch", "Script_Resolution#automatic-resolution-change"},
-	{"Shift Times", "Shift_Times"},
-	{"Select Lines", "Select_Lines"},
-	{"Spell Checker", "Spell_Checker"},
-	{"Style Editor", "Styles"},
-	{"Styles Manager", "Styles"},
-	{"Styling Assistant", "Styling_Assistant"},
-	{"Timing Processor", "Timing_Post-Processor"},
-	{"Translation Assistant", "Translation_Assistant"},
-	{"Visual Typesetting", "Visual_Typesetting"},
+    {"Attachment Manager", "Attachment_Manager"},
+    {"Automation Manager", "Automation/Manager"},
+    {"Colour Picker", "Colour_Picker"},
+    {"Dummy Video", "Video#dummy-video"},
+    {"Export", "Exporting"},
+    {"Fonts Collector", "Fonts_Collector"},
+    {"Kanji Timer", "Kanji_Timer"},
+    {"Main", "Main_Page"},
+    {"Options", "Options"},
+    {"Paste Over", "Paste_Over"},
+    {"Properties", "Properties"},
+    {"Resample resolution", "Resolution_Resampler"},
+    {"Resolution mismatch", "Script_Resolution#automatic-resolution-change"},
+    {"Shift Times", "Shift_Times"},
+    {"Select Lines", "Select_Lines"},
+    {"Spell Checker", "Spell_Checker"},
+    {"Style Editor", "Styles"},
+    {"Styles Manager", "Styles"},
+    {"Styling Assistant", "Styling_Assistant"},
+    {"Timing Processor", "Timing_Post-Processor"},
+    {"Translation Assistant", "Translation_Assistant"},
+    {"Visual Typesetting", "Visual_Typesetting"},
 };
 
 namespace {
-	const char *url(const char *page) {
-		auto it = std::lower_bound(std::begin(pages), std::end(pages), page, [](const char *pair[], const char *page) {
-			return strcmp(pair[0], page) < 0;
-		});
-		return it == std::end(pages) ? nullptr : (*it)[1];
-	}
+const char *url(const char *page)
+{
+    auto it = std::lower_bound(std::begin(pages), std::end(pages), page, [](const char *pair[], const char *page) {
+        return strcmp(pair[0], page) < 0;
+    });
+    return it == std::end(pages) ? nullptr : (*it)[1];
+}
 }
 
 HelpButton::HelpButton(wxWindow *parent, const char *page, wxPoint position, wxSize size)
-: wxButton(parent, wxID_HELP, "", position, size)
+    : wxButton(parent, wxID_HELP, "", position, size)
 {
-	Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { OpenPage(page); });
-	if (!url(page))
-		throw agi::InternalError("Invalid help page");
+    Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { OpenPage(page); });
+    if (!url(page))
+        throw agi::InternalError("Invalid help page");
 }
 
-void HelpButton::OpenPage(const char *pageID) {
-	auto page = url(pageID);
-	auto sep = strchr(page, '#');
-	if (sep)
-		wxLaunchDefaultBrowser(fmt_wx("http://docs.aegisub.org/3.2/%.*s/%s", sep - page, page, sep));
-	else
-		wxLaunchDefaultBrowser(fmt_wx("http://docs.aegisub.org/3.2/%s/", page));
+void HelpButton::OpenPage(const char *pageID)
+{
+    auto page = url(pageID);
+    auto sep = strchr(page, '#');
+    if (sep)
+        wxLaunchDefaultBrowser(fmt_wx("http://docs.aegisub.org/3.2/%.*s/%s", sep - page, page, sep));
+    else
+        wxLaunchDefaultBrowser(fmt_wx("http://docs.aegisub.org/3.2/%s/", page));
 }
diff --git a/src/help_button.h b/src/help_button.h
index 8590d35867883754e4abafbaf5c0768a85eb3a5e..49ccd8f99ad3fdd39417f5fa92b3cc7d489cc830 100644
--- a/src/help_button.h
+++ b/src/help_button.h
@@ -31,6 +31,6 @@
 
 class HelpButton final : public wxButton {
 public:
-	HelpButton(wxWindow *parent, const char *page, wxPoint position=wxDefaultPosition, wxSize size=wxDefaultSize);
-	static void OpenPage(const char *page);
+    HelpButton(wxWindow *parent, const char *page, wxPoint position = wxDefaultPosition, wxSize size = wxDefaultSize);
+    static void OpenPage(const char *page);
 };
diff --git a/src/hotkey.cpp b/src/hotkey.cpp
index 45cbcdeb5ec6a6e0e1ced9790375a168f0bfb074..dd4cf7d58740001333760b10a881e4965b736813 100644
--- a/src/hotkey.cpp
+++ b/src/hotkey.cpp
@@ -29,251 +29,260 @@
 #include <wx/msgdlg.h>
 
 namespace {
-	const char *added_hotkeys_7035[][3] = {
-		{"audio/play/line", "Audio", "R"},
-		{nullptr}
-	};
-
-	const char *added_hotkeys_7070[][3] = {
-		{"edit/color/primary", "Subtitle Edit Box", "Alt-1"},
-		{"edit/color/secondary", "Subtitle Edit Box", "Alt-2"},
-		{"edit/color/outline", "Subtitle Edit Box", "Alt-3"},
-		{"edit/color/shadow", "Subtitle Edit Box", "Alt-4"},
-		{nullptr}
-	};
-
-	const char *added_hotkeys_shift_back[][3] = {
-		{"edit/line/duplicate/shift_back", "Default", "Ctrl-Shift-D"},
-		{nullptr}
-	};
-
-	const char *added_hotkeys_time_tap[][3] = {
-		{"time/tap/connect", "Audio", "I"},
-		{"time/tap/no_connect", "Audio", "O"},
-		{nullptr}
-	};
-
-	void migrate_hotkeys(const char *added[][3]) {
-		auto hk_map = hotkey::inst->GetHotkeyMap();
-		bool changed = false;
-
-		for (size_t i = 0; added[i] && added[i][0]; ++i) {
-			agi::hotkey::Combo combo(added[i][1], added[i][0], added[i][2]);
-
-			if (hotkey::inst->HasHotkey(combo.Context(), combo.Str()))
-				continue;
-
-			hk_map.insert(make_pair(std::string(added[i][0]), std::move(combo)));
-			changed = true;
-		}
-
-		if (changed)
-			hotkey::inst->SetHotkeyMap(std::move(hk_map));
-	}
+const char *added_hotkeys_7035[][3] = {
+    {"audio/play/line", "Audio", "R"},
+    {nullptr}
+};
+
+const char *added_hotkeys_7070[][3] = {
+    {"edit/color/primary", "Subtitle Edit Box", "Alt-1"},
+    {"edit/color/secondary", "Subtitle Edit Box", "Alt-2"},
+    {"edit/color/outline", "Subtitle Edit Box", "Alt-3"},
+    {"edit/color/shadow", "Subtitle Edit Box", "Alt-4"},
+    {nullptr}
+};
+
+const char *added_hotkeys_shift_back[][3] = {
+    {"edit/line/duplicate/shift_back", "Default", "Ctrl-Shift-D"},
+    {nullptr}
+};
+
+const char *added_hotkeys_time_tap[][3] = {
+    {"time/tap/connect", "Audio", "I"},
+    {"time/tap/no_connect", "Audio", "O"},
+    {nullptr}
+};
+
+void migrate_hotkeys(const char *added[][3])
+{
+    auto hk_map = hotkey::inst->GetHotkeyMap();
+    bool changed = false;
+
+    for (size_t i = 0; added[i] && added[i][0]; ++i) {
+        agi::hotkey::Combo combo(added[i][1], added[i][0], added[i][2]);
+
+        if (hotkey::inst->HasHotkey(combo.Context(), combo.Str()))
+            continue;
+
+        hk_map.insert(make_pair(std::string(added[i][0]), std::move(combo)));
+        changed = true;
+    }
+
+    if (changed)
+        hotkey::inst->SetHotkeyMap(std::move(hk_map));
+}
 }
 
 namespace hotkey {
 
 agi::hotkey::Hotkey *inst = nullptr;
-void init() {
-	inst = new agi::hotkey::Hotkey(
-		config::path->Decode("?user/hotkey.json"),
-		GET_DEFAULT_CONFIG(default_hotkey));
-
-	auto migrations = OPT_GET("App/Hotkey Migrations")->GetListString();
-
-	if (boost::find(migrations, "7035") == end(migrations)) {
-		migrate_hotkeys(added_hotkeys_7035);
-		migrations.emplace_back("7035");
-	}
-
-	if (boost::find(migrations, "7070") == end(migrations)) {
-		migrate_hotkeys(added_hotkeys_7070);
-		migrations.emplace_back("7070");
-	}
-
-	if (boost::find(migrations, "edit/line/duplicate/shift_back") == end(migrations)) {
-		migrate_hotkeys(added_hotkeys_shift_back);
-		migrations.emplace_back("edit/line/duplicate/shift_back");
-	}
-
-	if (boost::find(migrations, "duplicate -> split") == end(migrations)) {
-		auto hk_map = hotkey::inst->GetHotkeyMap();
-		for (auto const& hotkey : boost::make_iterator_range(hk_map.equal_range("edit/line/duplicate/shift"))) {
-			auto combo = agi::hotkey::Combo(hotkey.second.Context(), "edit/line/split/before", hotkey.second.Str());
-			hk_map.insert({combo.CmdName(), combo});
-		}
-		for (auto const& hotkey : boost::make_iterator_range(hk_map.equal_range("edit/line/duplicate/shift_back"))) {
-			auto combo = agi::hotkey::Combo(hotkey.second.Context(), "edit/line/split/after", hotkey.second.Str());
-			hk_map.insert({combo.CmdName(), combo});
-		}
-
-		hk_map.erase("edit/line/duplicate/shift");
-		hk_map.erase("edit/line/duplicate/shift_back");
-
-		hotkey::inst->SetHotkeyMap(std::move(hk_map));
-		migrations.emplace_back("duplicate -> split");
-	}
+void init()
+{
+    inst = new agi::hotkey::Hotkey(
+        config::path->Decode("?user/hotkey.json"),
+        GET_DEFAULT_CONFIG(default_hotkey));
+
+    auto migrations = OPT_GET("App/Hotkey Migrations")->GetListString();
+
+    if (boost::find(migrations, "7035") == end(migrations)) {
+        migrate_hotkeys(added_hotkeys_7035);
+        migrations.emplace_back("7035");
+    }
+
+    if (boost::find(migrations, "7070") == end(migrations)) {
+        migrate_hotkeys(added_hotkeys_7070);
+        migrations.emplace_back("7070");
+    }
+
+    if (boost::find(migrations, "edit/line/duplicate/shift_back") == end(migrations)) {
+        migrate_hotkeys(added_hotkeys_shift_back);
+        migrations.emplace_back("edit/line/duplicate/shift_back");
+    }
+
+    if (boost::find(migrations, "duplicate -> split") == end(migrations)) {
+        auto hk_map = hotkey::inst->GetHotkeyMap();
+        for (auto const &hotkey : boost::make_iterator_range(hk_map.equal_range("edit/line/duplicate/shift"))) {
+            auto combo = agi::hotkey::Combo(hotkey.second.Context(), "edit/line/split/before", hotkey.second.Str());
+            hk_map.insert({combo.CmdName(), combo});
+        }
+        for (auto const &hotkey : boost::make_iterator_range(hk_map.equal_range("edit/line/duplicate/shift_back"))) {
+            auto combo = agi::hotkey::Combo(hotkey.second.Context(), "edit/line/split/after", hotkey.second.Str());
+            hk_map.insert({combo.CmdName(), combo});
+        }
+
+        hk_map.erase("edit/line/duplicate/shift");
+        hk_map.erase("edit/line/duplicate/shift_back");
+
+        hotkey::inst->SetHotkeyMap(std::move(hk_map));
+        migrations.emplace_back("duplicate -> split");
+    }
 
 #ifdef __WXMAC__
-	if (boost::find(migrations, "app/minimize") == end(migrations)) {
-		migrate_hotkeys(added_hotkeys_minimize);
-		migrations.emplace_back("app/minimize");
-	}
+    if (boost::find(migrations, "app/minimize") == end(migrations)) {
+        migrate_hotkeys(added_hotkeys_minimize);
+        migrations.emplace_back("app/minimize");
+    }
 #endif
 
-	if (boost::find(migrations, "time/tap") == end(migrations)) {
-		migrate_hotkeys(added_hotkeys_time_tap);
-		migrations.emplace_back("time/tap");
-	}
+    if (boost::find(migrations, "time/tap") == end(migrations)) {
+        migrate_hotkeys(added_hotkeys_time_tap);
+        migrations.emplace_back("time/tap");
+    }
 
-	OPT_SET("App/Hotkey Migrations")->SetListString(std::move(migrations));
+    OPT_SET("App/Hotkey Migrations")->SetListString(std::move(migrations));
 }
 
-void clear() {
-	delete inst;
+void clear()
+{
+    delete inst;
 }
 
-static const char *keycode_name(int code) {
-	switch (code) {
-	case WXK_BACK:            return "Backspace";
-	case WXK_TAB:             return "Tab";
-	case WXK_RETURN:          return "Enter";
-	case WXK_ESCAPE:          return "Escape";
-	case WXK_SPACE:           return "Space";
-	case WXK_DELETE:          return "Delete";
-	case WXK_SHIFT:           return "Shift";
-	case WXK_ALT:             return "Alt";
-	case WXK_CONTROL:         return "Control";
-	case WXK_PAUSE:           return "Pause";
-	case WXK_END:             return "End";
-	case WXK_HOME:            return "Home";
-	case WXK_LEFT:            return "Left";
-	case WXK_UP:              return "Up";
-	case WXK_RIGHT:           return "Right";
-	case WXK_DOWN:            return "Down";
-	case WXK_PRINT:           return "Print";
-	case WXK_INSERT:          return "Insert";
-	case WXK_NUMPAD0:         return "KP_0";
-	case WXK_NUMPAD1:         return "KP_1";
-	case WXK_NUMPAD2:         return "KP_2";
-	case WXK_NUMPAD3:         return "KP_3";
-	case WXK_NUMPAD4:         return "KP_4";
-	case WXK_NUMPAD5:         return "KP_5";
-	case WXK_NUMPAD6:         return "KP_6";
-	case WXK_NUMPAD7:         return "KP_7";
-	case WXK_NUMPAD8:         return "KP_8";
-	case WXK_NUMPAD9:         return "KP_9";
-	case WXK_MULTIPLY:        return "Asterisk";
-	case WXK_ADD:             return "Plus";
-	case WXK_SUBTRACT:        return "Hyphen";
-	case WXK_DECIMAL:         return "Period";
-	case WXK_DIVIDE:          return "Slash";
-	case WXK_F1:              return "F1";
-	case WXK_F2:              return "F2";
-	case WXK_F3:              return "F3";
-	case WXK_F4:              return "F4";
-	case WXK_F5:              return "F5";
-	case WXK_F6:              return "F6";
-	case WXK_F7:              return "F7";
-	case WXK_F8:              return "F8";
-	case WXK_F9:              return "F9";
-	case WXK_F10:             return "F10";
-	case WXK_F11:             return "F11";
-	case WXK_F12:             return "F12";
-	case WXK_F13:             return "F13";
-	case WXK_F14:             return "F14";
-	case WXK_F15:             return "F15";
-	case WXK_F16:             return "F16";
-	case WXK_F17:             return "F17";
-	case WXK_F18:             return "F18";
-	case WXK_F19:             return "F19";
-	case WXK_F20:             return "F20";
-	case WXK_F21:             return "F21";
-	case WXK_F22:             return "F22";
-	case WXK_F23:             return "F23";
-	case WXK_F24:             return "F24";
-	case WXK_NUMLOCK:         return "Num_Lock";
-	case WXK_SCROLL:          return "Scroll_Lock";
-	case WXK_PAGEUP:          return "PageUp";
-	case WXK_PAGEDOWN:        return "PageDown";
-	case WXK_NUMPAD_SPACE:    return "KP_Space";
-	case WXK_NUMPAD_TAB:      return "KP_Tab";
-	case WXK_NUMPAD_ENTER:    return "KP_Enter";
-	case WXK_NUMPAD_F1:       return "KP_F1";
-	case WXK_NUMPAD_F2:       return "KP_F2";
-	case WXK_NUMPAD_F3:       return "KP_F3";
-	case WXK_NUMPAD_F4:       return "KP_F4";
-	case WXK_NUMPAD_HOME:     return "KP_Home";
-	case WXK_NUMPAD_LEFT:     return "KP_Left";
-	case WXK_NUMPAD_UP:       return "KP_Up";
-	case WXK_NUMPAD_RIGHT:    return "KP_Right";
-	case WXK_NUMPAD_DOWN:     return "KP_Down";
-	case WXK_NUMPAD_PAGEUP:   return "KP_PageUp";
-	case WXK_NUMPAD_PAGEDOWN: return "KP_PageDown";
-	case WXK_NUMPAD_END:      return "KP_End";
-	case WXK_NUMPAD_BEGIN:    return "KP_Begin";
-	case WXK_NUMPAD_INSERT:   return "KP_insert";
-	case WXK_NUMPAD_DELETE:   return "KP_Delete";
-	case WXK_NUMPAD_EQUAL:    return "KP_Equal";
-	case WXK_NUMPAD_MULTIPLY: return "KP_Multiply";
-	case WXK_NUMPAD_ADD:      return "KP_Add";
-	case WXK_NUMPAD_SUBTRACT: return "KP_Subtract";
-	case WXK_NUMPAD_DECIMAL:  return "KP_Decimal";
-	case WXK_NUMPAD_DIVIDE:   return "KP_Divide";
-	default: return "";
-	}
+static const char *keycode_name(int code)
+{
+    switch (code) {
+    case WXK_BACK:            return "Backspace";
+    case WXK_TAB:             return "Tab";
+    case WXK_RETURN:          return "Enter";
+    case WXK_ESCAPE:          return "Escape";
+    case WXK_SPACE:           return "Space";
+    case WXK_DELETE:          return "Delete";
+    case WXK_SHIFT:           return "Shift";
+    case WXK_ALT:             return "Alt";
+    case WXK_CONTROL:         return "Control";
+    case WXK_PAUSE:           return "Pause";
+    case WXK_END:             return "End";
+    case WXK_HOME:            return "Home";
+    case WXK_LEFT:            return "Left";
+    case WXK_UP:              return "Up";
+    case WXK_RIGHT:           return "Right";
+    case WXK_DOWN:            return "Down";
+    case WXK_PRINT:           return "Print";
+    case WXK_INSERT:          return "Insert";
+    case WXK_NUMPAD0:         return "KP_0";
+    case WXK_NUMPAD1:         return "KP_1";
+    case WXK_NUMPAD2:         return "KP_2";
+    case WXK_NUMPAD3:         return "KP_3";
+    case WXK_NUMPAD4:         return "KP_4";
+    case WXK_NUMPAD5:         return "KP_5";
+    case WXK_NUMPAD6:         return "KP_6";
+    case WXK_NUMPAD7:         return "KP_7";
+    case WXK_NUMPAD8:         return "KP_8";
+    case WXK_NUMPAD9:         return "KP_9";
+    case WXK_MULTIPLY:        return "Asterisk";
+    case WXK_ADD:             return "Plus";
+    case WXK_SUBTRACT:        return "Hyphen";
+    case WXK_DECIMAL:         return "Period";
+    case WXK_DIVIDE:          return "Slash";
+    case WXK_F1:              return "F1";
+    case WXK_F2:              return "F2";
+    case WXK_F3:              return "F3";
+    case WXK_F4:              return "F4";
+    case WXK_F5:              return "F5";
+    case WXK_F6:              return "F6";
+    case WXK_F7:              return "F7";
+    case WXK_F8:              return "F8";
+    case WXK_F9:              return "F9";
+    case WXK_F10:             return "F10";
+    case WXK_F11:             return "F11";
+    case WXK_F12:             return "F12";
+    case WXK_F13:             return "F13";
+    case WXK_F14:             return "F14";
+    case WXK_F15:             return "F15";
+    case WXK_F16:             return "F16";
+    case WXK_F17:             return "F17";
+    case WXK_F18:             return "F18";
+    case WXK_F19:             return "F19";
+    case WXK_F20:             return "F20";
+    case WXK_F21:             return "F21";
+    case WXK_F22:             return "F22";
+    case WXK_F23:             return "F23";
+    case WXK_F24:             return "F24";
+    case WXK_NUMLOCK:         return "Num_Lock";
+    case WXK_SCROLL:          return "Scroll_Lock";
+    case WXK_PAGEUP:          return "PageUp";
+    case WXK_PAGEDOWN:        return "PageDown";
+    case WXK_NUMPAD_SPACE:    return "KP_Space";
+    case WXK_NUMPAD_TAB:      return "KP_Tab";
+    case WXK_NUMPAD_ENTER:    return "KP_Enter";
+    case WXK_NUMPAD_F1:       return "KP_F1";
+    case WXK_NUMPAD_F2:       return "KP_F2";
+    case WXK_NUMPAD_F3:       return "KP_F3";
+    case WXK_NUMPAD_F4:       return "KP_F4";
+    case WXK_NUMPAD_HOME:     return "KP_Home";
+    case WXK_NUMPAD_LEFT:     return "KP_Left";
+    case WXK_NUMPAD_UP:       return "KP_Up";
+    case WXK_NUMPAD_RIGHT:    return "KP_Right";
+    case WXK_NUMPAD_DOWN:     return "KP_Down";
+    case WXK_NUMPAD_PAGEUP:   return "KP_PageUp";
+    case WXK_NUMPAD_PAGEDOWN: return "KP_PageDown";
+    case WXK_NUMPAD_END:      return "KP_End";
+    case WXK_NUMPAD_BEGIN:    return "KP_Begin";
+    case WXK_NUMPAD_INSERT:   return "KP_insert";
+    case WXK_NUMPAD_DELETE:   return "KP_Delete";
+    case WXK_NUMPAD_EQUAL:    return "KP_Equal";
+    case WXK_NUMPAD_MULTIPLY: return "KP_Multiply";
+    case WXK_NUMPAD_ADD:      return "KP_Add";
+    case WXK_NUMPAD_SUBTRACT: return "KP_Subtract";
+    case WXK_NUMPAD_DECIMAL:  return "KP_Decimal";
+    case WXK_NUMPAD_DIVIDE:   return "KP_Divide";
+    default: return "";
+    }
 }
 
-std::string keypress_to_str(int key_code, int modifier) {
-	std::string combo;
-	if ((modifier != wxMOD_NONE)) {
-		if ((modifier & wxMOD_CMD) != 0) combo.append("Ctrl-");
-		if ((modifier & wxMOD_ALT) != 0) combo.append("Alt-");
-		if ((modifier & wxMOD_SHIFT) != 0) combo.append("Shift-");
-	}
-
-	if (key_code > 32 && key_code < 127)
-		combo += (char)key_code;
-	else
-		combo += keycode_name(key_code);
-
-	return combo;
+std::string keypress_to_str(int key_code, int modifier)
+{
+    std::string combo;
+    if ((modifier != wxMOD_NONE)) {
+        if ((modifier & wxMOD_CMD) != 0) combo.append("Ctrl-");
+        if ((modifier & wxMOD_ALT) != 0) combo.append("Alt-");
+        if ((modifier & wxMOD_SHIFT) != 0) combo.append("Shift-");
+    }
+
+    if (key_code > 32 && key_code < 127)
+        combo += (char)key_code;
+    else
+        combo += keycode_name(key_code);
+
+    return combo;
 }
 
-static bool check(std::string const& context, agi::Context *c, int key_code, int modifier) {
-	std::string combo = keypress_to_str(key_code, modifier);
-	if (combo.empty()) return false;
-
-	std::string command = inst->Scan(context, combo, OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool());
-	if (!command.empty()) {
-		cmd::call(command, c);
-		return true;
-	}
-	return false;
+static bool check(std::string const &context, agi::Context *c, int key_code, int modifier)
+{
+    std::string combo = keypress_to_str(key_code, modifier);
+    if (combo.empty()) return false;
+
+    std::string command = inst->Scan(context, combo, OPT_GET("Audio/Medusa Timing Hotkeys")->GetBool());
+    if (!command.empty()) {
+        cmd::call(command, c);
+        return true;
+    }
+    return false;
 }
 
-bool check(std::string const& context, agi::Context *c, wxKeyEvent &evt) {
-	try {
-		if (!check(context, c, evt.GetKeyCode(), evt.GetModifiers())) {
-			evt.Skip();
-			return false;
-		}
-		return true;
-	}
-	catch (cmd::CommandNotFound const& e) {
-		wxMessageBox(to_wx(e.GetMessage()), _("Invalid command name for hotkey"),
-			wxOK | wxICON_ERROR | wxCENTER | wxSTAY_ON_TOP);
-		return true;
-	}
+bool check(std::string const &context, agi::Context *c, wxKeyEvent &evt)
+{
+    try {
+        if (!check(context, c, evt.GetKeyCode(), evt.GetModifiers())) {
+            evt.Skip();
+            return false;
+        }
+        return true;
+    }
+    catch (cmd::CommandNotFound const &e) {
+        wxMessageBox(to_wx(e.GetMessage()), _("Invalid command name for hotkey"),
+                     wxOK | wxICON_ERROR | wxCENTER | wxSTAY_ON_TOP);
+        return true;
+    }
 }
 
-std::vector<std::string> get_hotkey_strs(std::string const& context, std::string const& command) {
-	return inst->GetHotkeys(context, command);
+std::vector<std::string> get_hotkey_strs(std::string const &context, std::string const &command)
+{
+    return inst->GetHotkeys(context, command);
 }
 
-std::string get_hotkey_str_first(std::string const& context, std::string const& command) {
-	return inst->GetHotkey(context, command);
+std::string get_hotkey_str_first(std::string const &context, std::string const &command)
+{
+    return inst->GetHotkey(context, command);
 }
 
 } // namespace hotkey
diff --git a/src/hotkey_data_view_model.cpp b/src/hotkey_data_view_model.cpp
index 0c4924a824dfade9a5097112b703dc4b9be4c23b..0b688dfd2ae89fc419e5049dd76678b81f11052d 100644
--- a/src/hotkey_data_view_model.cpp
+++ b/src/hotkey_data_view_model.cpp
@@ -37,13 +37,13 @@ using namespace agi::hotkey;
 /// @brief A base class for things exposed by HotkeyDataViewModel
 class HotkeyModelItem {
 protected:
-	~HotkeyModelItem() = default;
+    ~HotkeyModelItem() = default;
 public:
-	virtual unsigned int GetChildren(wxDataViewItemArray &children) const=0;
-	virtual wxDataViewItem GetParent() const=0;
-	virtual void GetValue(wxVariant &variant, unsigned int col) const=0;
-	virtual bool IsContainer() const=0;
-	virtual bool SetValue(wxVariant const& variant, unsigned int col)=0;
+    virtual unsigned int GetChildren(wxDataViewItemArray &children) const = 0;
+    virtual wxDataViewItem GetParent() const = 0;
+    virtual void GetValue(wxVariant &variant, unsigned int col) const = 0;
+    virtual bool IsContainer() const = 0;
+    virtual bool SetValue(wxVariant const &variant, unsigned int col) = 0;
 };
 
 class HotkeyModelRoot;
@@ -54,279 +54,288 @@ class HotkeyModelCategory;
 ///
 /// All actual mutation of hotkeys happens through this class
 class HotkeyModelCombo final : public HotkeyModelItem {
-	HotkeyModelCategory *parent; ///< The containing category
-	Combo combo;				 ///< The actual hotkey
-	std::string cmd_name;
-	std::string cmd_str;
+    HotkeyModelCategory *parent; ///< The containing category
+    Combo combo;				 ///< The actual hotkey
+    std::string cmd_name;
+    std::string cmd_str;
 public:
-	HotkeyModelCombo(HotkeyModelCategory *parent, Combo const& combo)
-	: parent(parent)
-	, combo(combo)
-	, cmd_name(combo.CmdName())
-	, cmd_str(combo.Str())
-	{
-	}
-
-	bool IsVisible(std::string const& filter) const {
-		return boost::to_lower_copy(cmd_name).find(filter) != std::string::npos
-			|| boost::to_lower_copy(cmd_str).find(filter) != std::string::npos;
-	}
-
-	void Apply(Hotkey::HotkeyMap *hk_map) {
-		if (combo.CmdName().size() || combo.Str().size())
-			hk_map->insert(make_pair(combo.CmdName(), combo));
-	}
-
-	unsigned int GetChildren(wxDataViewItemArray &) const override { return 0; }
-	wxDataViewItem GetParent() const override { return wxDataViewItem(parent); }
-	bool IsContainer() const override { return false; }
-
-	void GetValue(wxVariant &variant, unsigned int col) const override {
-		if (col == 0)
-			variant = to_wx(combo.Str());
-		else if (col == 1) {
-			wxIcon icon;
-			try {
-				auto icon_bmp = cmd::get(combo.CmdName())->Icon(16);
-				if (icon_bmp.IsOk())
-					icon.CopyFromBitmap(icon_bmp);
-			}
-			catch (agi::Exception const&) {
-				// Just use no icon; error is reported in the description column
-			}
-			variant << wxDataViewIconText(to_wx(combo.CmdName()), icon);
-		}
-		else if (col == 2) {
-			try {
-				variant = cmd::get(combo.CmdName())->StrHelp();
-			}
-			catch (agi::Exception const& e) {
-				variant = to_wx(e.GetMessage());
-			}
-		}
-		else
-			throw agi::InternalError("HotkeyDataViewModel asked for an invalid column number");
-	}
-
-	bool SetValue(wxVariant const& variant, unsigned int col) override {
-		if (col == 0) {
-			combo = Combo(combo.Context(), combo.CmdName(), from_wx(variant.GetString()));
-			cmd_str = combo.Str();
-			return true;
-		}
-		else if (col == 1) {
-			wxDataViewIconText text;
-			text << variant;
-			cmd_name = from_wx(text.GetText());
-			combo = Combo(combo.Context(), cmd_name, combo.Str());
-			return true;
-		}
-		return false;
-	}
+    HotkeyModelCombo(HotkeyModelCategory *parent, Combo const &combo)
+        : parent(parent)
+        , combo(combo)
+        , cmd_name(combo.CmdName())
+        , cmd_str(combo.Str()) {
+    }
+
+    bool IsVisible(std::string const &filter) const {
+        return boost::to_lower_copy(cmd_name).find(filter) != std::string::npos
+               || boost::to_lower_copy(cmd_str).find(filter) != std::string::npos;
+    }
+
+    void Apply(Hotkey::HotkeyMap *hk_map) {
+        if (combo.CmdName().size() || combo.Str().size())
+            hk_map->insert(make_pair(combo.CmdName(), combo));
+    }
+
+    unsigned int GetChildren(wxDataViewItemArray &) const override { return 0; }
+    wxDataViewItem GetParent() const override { return wxDataViewItem(parent); }
+    bool IsContainer() const override { return false; }
+
+    void GetValue(wxVariant &variant, unsigned int col) const override {
+        if (col == 0)
+            variant = to_wx(combo.Str());
+        else if (col == 1) {
+            wxIcon icon;
+            try {
+                auto icon_bmp = cmd::get(combo.CmdName())->Icon(16);
+                if (icon_bmp.IsOk())
+                    icon.CopyFromBitmap(icon_bmp);
+            }
+            catch (agi::Exception const &) {
+                // Just use no icon; error is reported in the description column
+            }
+            variant << wxDataViewIconText(to_wx(combo.CmdName()), icon);
+        }
+        else if (col == 2) {
+            try {
+                variant = cmd::get(combo.CmdName())->StrHelp();
+            }
+            catch (agi::Exception const &e) {
+                variant = to_wx(e.GetMessage());
+            }
+        }
+        else
+            throw agi::InternalError("HotkeyDataViewModel asked for an invalid column number");
+    }
+
+    bool SetValue(wxVariant const &variant, unsigned int col) override {
+        if (col == 0) {
+            combo = Combo(combo.Context(), combo.CmdName(), from_wx(variant.GetString()));
+            cmd_str = combo.Str();
+            return true;
+        }
+        else if (col == 1) {
+            wxDataViewIconText text;
+            text << variant;
+            cmd_name = from_wx(text.GetText());
+            combo = Combo(combo.Context(), cmd_name, combo.Str());
+            return true;
+        }
+        return false;
+    }
 };
 
 /// A hotkey context exposed in the data view
 class HotkeyModelCategory final : public HotkeyModelItem {
-	std::list<HotkeyModelCombo> children;
-	wxDataViewModel *model;
-	std::string name;
-	wxString translated_name;
-	wxDataViewItemArray visible_items;
+    std::list<HotkeyModelCombo> children;
+    wxDataViewModel *model;
+    std::string name;
+    wxString translated_name;
+    wxDataViewItemArray visible_items;
 public:
-	HotkeyModelCategory(wxDataViewModel *model, std::string const& name)
-	: model(model)
-	, name(name)
-	, translated_name(wxGetTranslation(to_wx(name)))
-	{
-	}
-
-	wxDataViewItem AddChild(Combo const& combo) {
-		children.emplace_back(this, combo);
-		visible_items.push_back(wxDataViewItem(&children.back()));
-		model->ItemAdded(wxDataViewItem(this), wxDataViewItem(&children.back()));
-		return wxDataViewItem(&children.back());
-	}
-
-	void Delete(wxDataViewItem const& item) {
-		for (auto it = children.begin(); it != children.end(); ++it) {
-			if (&*it == item.GetID()) {
-				model->ItemDeleted(wxDataViewItem(this), wxDataViewItem((void*)&*it));
-				children.erase(it);
-				return;
-			}
-		}
-	}
-
-	void Apply(Hotkey::HotkeyMap *hk_map) {
-		for (auto& combo : children)
-			combo.Apply(hk_map);
-	}
-
-	void SetFilter(std::string const& new_filter) {
-		std::vector<HotkeyModelCombo *> old_visible;
-		for (auto item : visible_items)
-			old_visible.push_back(static_cast<HotkeyModelCombo*>(item.GetID()));
-		sort(begin(old_visible), end(old_visible));
-
-		visible_items.clear();
-
-		wxDataViewItemArray added;
-		wxDataViewItemArray removed;
-
-		for (auto& combo : children) {
-			bool was_visible = binary_search(begin(old_visible), end(old_visible), &combo);
-			bool is_visible = combo.IsVisible(new_filter);
-
-			if (is_visible)
-				visible_items.push_back(wxDataViewItem(&combo));
-			if (was_visible && !is_visible)
-				removed.push_back(wxDataViewItem(&combo));
-			if (is_visible && !was_visible)
-				added.push_back(wxDataViewItem(&combo));
-		}
-
-		if (!added.empty())
-			model->ItemsAdded(wxDataViewItem(this), added);
-		if (!removed.empty())
-			model->ItemsDeleted(wxDataViewItem(this), removed);
-	}
-
-	std::string const& GetName() const { return name; }
-
-	wxDataViewItem GetParent() const override { return wxDataViewItem(nullptr); }
-	bool IsContainer() const override { return true; }
-	bool SetValue(wxVariant const&, unsigned int) override { return false; }
-	void GetValue(wxVariant &variant, unsigned int col) const override {
-		if (col == 1)
-			variant << wxDataViewIconText(translated_name);
-		else
-			variant = translated_name;
-	}
-
-	unsigned int GetChildren(wxDataViewItemArray &out) const override {
-		out = visible_items;
-		return out.size();
-	}
+    HotkeyModelCategory(wxDataViewModel *model, std::string const &name)
+        : model(model)
+        , name(name)
+        , translated_name(wxGetTranslation(to_wx(name))) {
+    }
+
+    wxDataViewItem AddChild(Combo const &combo) {
+        children.emplace_back(this, combo);
+        visible_items.push_back(wxDataViewItem(&children.back()));
+        model->ItemAdded(wxDataViewItem(this), wxDataViewItem(&children.back()));
+        return wxDataViewItem(&children.back());
+    }
+
+    void Delete(wxDataViewItem const &item) {
+        for (auto it = children.begin(); it != children.end(); ++it) {
+            if (&*it == item.GetID()) {
+                model->ItemDeleted(wxDataViewItem(this), wxDataViewItem((void *)&*it));
+                children.erase(it);
+                return;
+            }
+        }
+    }
+
+    void Apply(Hotkey::HotkeyMap *hk_map) {
+        for (auto &combo : children)
+            combo.Apply(hk_map);
+    }
+
+    void SetFilter(std::string const &new_filter) {
+        std::vector<HotkeyModelCombo *> old_visible;
+        for (auto item : visible_items)
+            old_visible.push_back(static_cast<HotkeyModelCombo *>(item.GetID()));
+        sort(begin(old_visible), end(old_visible));
+
+        visible_items.clear();
+
+        wxDataViewItemArray added;
+        wxDataViewItemArray removed;
+
+        for (auto &combo : children) {
+            bool was_visible = binary_search(begin(old_visible), end(old_visible), &combo);
+            bool is_visible = combo.IsVisible(new_filter);
+
+            if (is_visible)
+                visible_items.push_back(wxDataViewItem(&combo));
+            if (was_visible && !is_visible)
+                removed.push_back(wxDataViewItem(&combo));
+            if (is_visible && !was_visible)
+                added.push_back(wxDataViewItem(&combo));
+        }
+
+        if (!added.empty())
+            model->ItemsAdded(wxDataViewItem(this), added);
+        if (!removed.empty())
+            model->ItemsDeleted(wxDataViewItem(this), removed);
+    }
+
+    std::string const &GetName() const { return name; }
+
+    wxDataViewItem GetParent() const override { return wxDataViewItem(nullptr); }
+    bool IsContainer() const override { return true; }
+    bool SetValue(wxVariant const &, unsigned int) override { return false; }
+    void GetValue(wxVariant &variant, unsigned int col) const override {
+        if (col == 1)
+            variant << wxDataViewIconText(translated_name);
+        else
+            variant = translated_name;
+    }
+
+    unsigned int GetChildren(wxDataViewItemArray &out) const override {
+        out = visible_items;
+        return out.size();
+    }
 };
 
 /// The root containing the hotkey contexts
 class HotkeyModelRoot final : public HotkeyModelItem {
-	std::list<HotkeyModelCategory> categories;
+    std::list<HotkeyModelCategory> categories;
 public:
-	HotkeyModelRoot(wxDataViewModel *model) {
-		Hotkey::HotkeyMap const& hk_map = hotkey::inst->GetHotkeyMap();
-		std::map<std::string, HotkeyModelCategory*> cat_map;
-
-		for (auto const& category : hk_map) {
-			std::string const& cat_name = category.second.Context();
-			HotkeyModelCategory *cat;
-			auto cat_it = cat_map.find(cat_name);
-			if (cat_it != cat_map.end())
-				cat = cat_it->second;
-			else {
-				categories.emplace_back(model, cat_name);
-				cat = cat_map[cat_name] = &categories.back();
-			}
-
-			cat->AddChild(category.second);
-		}
-	}
-
-	void Apply(Hotkey::HotkeyMap *hk_map) {
-		for (auto& category : categories)
-			category.Apply(hk_map);
-	}
-
-	void SetFilter(std::string const& filter) {
-		for (auto& category : categories)
-			category.SetFilter(filter);
-	}
-
-	wxDataViewItem GetParent() const override { return wxDataViewItem(nullptr); }
-	bool IsContainer() const override { return true; }
-	bool SetValue(wxVariant const&, unsigned int) override { return false; }
-	void GetValue(wxVariant &, unsigned int) const override { }
-
-	unsigned int GetChildren(wxDataViewItemArray &out) const override {
-		out.reserve(categories.size());
-		for (auto const& category : categories)
-			out.push_back(wxDataViewItem((void*)&category));
-		return out.size();
-	}
+    HotkeyModelRoot(wxDataViewModel *model) {
+        Hotkey::HotkeyMap const &hk_map = hotkey::inst->GetHotkeyMap();
+        std::map<std::string, HotkeyModelCategory *> cat_map;
+
+        for (auto const &category : hk_map) {
+            std::string const &cat_name = category.second.Context();
+            HotkeyModelCategory *cat;
+            auto cat_it = cat_map.find(cat_name);
+            if (cat_it != cat_map.end())
+                cat = cat_it->second;
+            else {
+                categories.emplace_back(model, cat_name);
+                cat = cat_map[cat_name] = &categories.back();
+            }
+
+            cat->AddChild(category.second);
+        }
+    }
+
+    void Apply(Hotkey::HotkeyMap *hk_map) {
+        for (auto &category : categories)
+            category.Apply(hk_map);
+    }
+
+    void SetFilter(std::string const &filter) {
+        for (auto &category : categories)
+            category.SetFilter(filter);
+    }
+
+    wxDataViewItem GetParent() const override { return wxDataViewItem(nullptr); }
+    bool IsContainer() const override { return true; }
+    bool SetValue(wxVariant const &, unsigned int) override { return false; }
+    void GetValue(wxVariant &, unsigned int) const override { }
+
+    unsigned int GetChildren(wxDataViewItemArray &out) const override {
+        out.reserve(categories.size());
+        for (auto const &category : categories)
+            out.push_back(wxDataViewItem((void *)&category));
+        return out.size();
+    }
 };
 
 HotkeyDataViewModel::HotkeyDataViewModel(Preferences *parent)
-: root(agi::make_unique<HotkeyModelRoot>(this))
-, parent(parent)
+    : root(agi::make_unique<HotkeyModelRoot>(this))
+    , parent(parent)
 {
 }
 
-const HotkeyModelItem * HotkeyDataViewModel::get(wxDataViewItem const& item) const {
-	if (item.IsOk())
-		return static_cast<HotkeyModelItem*>(item.GetID());
-	return root.get();
+const HotkeyModelItem *HotkeyDataViewModel::get(wxDataViewItem const &item) const
+{
+    if (item.IsOk())
+        return static_cast<HotkeyModelItem *>(item.GetID());
+    return root.get();
 }
 
-HotkeyModelItem * HotkeyDataViewModel::get(wxDataViewItem const& item) {
-	if (item.IsOk())
-		return static_cast<HotkeyModelItem*>(item.GetID());
-	return root.get();
+HotkeyModelItem *HotkeyDataViewModel::get(wxDataViewItem const &item)
+{
+    if (item.IsOk())
+        return static_cast<HotkeyModelItem *>(item.GetID());
+    return root.get();
 }
 
-unsigned int HotkeyDataViewModel::GetChildren(wxDataViewItem const& item, wxDataViewItemArray &children) const {
-	return get(item)->GetChildren(children);
+unsigned int HotkeyDataViewModel::GetChildren(wxDataViewItem const &item, wxDataViewItemArray &children) const
+{
+    return get(item)->GetChildren(children);
 }
 
-wxDataViewItem HotkeyDataViewModel::GetParent(wxDataViewItem const& item) const {
-	return get(item)->GetParent();
+wxDataViewItem HotkeyDataViewModel::GetParent(wxDataViewItem const &item) const
+{
+    return get(item)->GetParent();
 }
 
-void HotkeyDataViewModel::GetValue(wxVariant &variant, wxDataViewItem const& item, unsigned int col) const {
-	get(item)->GetValue(variant, col);
+void HotkeyDataViewModel::GetValue(wxVariant &variant, wxDataViewItem const &item, unsigned int col) const
+{
+    get(item)->GetValue(variant, col);
 }
 
-bool HotkeyDataViewModel::IsContainer(wxDataViewItem const& item) const {
-	return get(item)->IsContainer();
+bool HotkeyDataViewModel::IsContainer(wxDataViewItem const &item) const
+{
+    return get(item)->IsContainer();
 }
 
-bool HotkeyDataViewModel::SetValue(wxVariant const& variant, wxDataViewItem const& item, unsigned int col) {
-	if (!has_pending_changes) {
-		has_pending_changes = true;
-		parent->AddPendingChange([=] { Apply(); });
-	}
-	return get(item)->SetValue(variant, col);
+bool HotkeyDataViewModel::SetValue(wxVariant const &variant, wxDataViewItem const &item, unsigned int col)
+{
+    if (!has_pending_changes) {
+        has_pending_changes = true;
+        parent->AddPendingChange([ = ] { Apply(); });
+    }
+    return get(item)->SetValue(variant, col);
 }
 
-wxDataViewItem HotkeyDataViewModel::New(wxDataViewItem item) {
-	if (!item.IsOk()) return wxDataViewItem();
+wxDataViewItem HotkeyDataViewModel::New(wxDataViewItem item)
+{
+    if (!item.IsOk()) return wxDataViewItem();
 
-	if (!IsContainer(item))
-		item = GetParent(item);
+    if (!IsContainer(item))
+        item = GetParent(item);
 
-	HotkeyModelCategory *ctx = static_cast<HotkeyModelCategory*>(item.GetID());
-	return ctx->AddChild(Combo(ctx->GetName(), "", ""));
+    HotkeyModelCategory *ctx = static_cast<HotkeyModelCategory *>(item.GetID());
+    return ctx->AddChild(Combo(ctx->GetName(), "", ""));
 }
 
-void HotkeyDataViewModel::Delete(wxDataViewItem const& item) {
-	if (!item.IsOk() || IsContainer(item)) return;
+void HotkeyDataViewModel::Delete(wxDataViewItem const &item)
+{
+    if (!item.IsOk() || IsContainer(item)) return;
 
-	static_cast<HotkeyModelCategory*>(GetParent(item).GetID())->Delete(item);
+    static_cast<HotkeyModelCategory *>(GetParent(item).GetID())->Delete(item);
 
-	if (!has_pending_changes) {
-		has_pending_changes = true;
-		parent->AddPendingChange([=] { Apply(); });
-	}
+    if (!has_pending_changes) {
+        has_pending_changes = true;
+        parent->AddPendingChange([ = ] { Apply(); });
+    }
 }
 
-void HotkeyDataViewModel::Apply() {
-	Hotkey::HotkeyMap hk_map;
-	root->Apply(&hk_map);
-	hotkey::inst->SetHotkeyMap(hk_map);
-	has_pending_changes = false;
+void HotkeyDataViewModel::Apply()
+{
+    Hotkey::HotkeyMap hk_map;
+    root->Apply(&hk_map);
+    hotkey::inst->SetHotkeyMap(hk_map);
+    has_pending_changes = false;
 }
 
-void HotkeyDataViewModel::SetFilter(wxString const& filter) {
-	auto str = from_wx(filter);
-	boost::to_lower(str);
-	root->SetFilter(str);
+void HotkeyDataViewModel::SetFilter(wxString const &filter)
+{
+    auto str = from_wx(filter);
+    boost::to_lower(str);
+    root->SetFilter(str);
 }
diff --git a/src/hotkey_data_view_model.h b/src/hotkey_data_view_model.h
index 39cf7c021a857b03a25db3cf4b81bd2a301c89a3..27d3e88a42fdce2acd26172594fd9d84fa5cf182 100644
--- a/src/hotkey_data_view_model.h
+++ b/src/hotkey_data_view_model.h
@@ -24,35 +24,35 @@ class Preferences;
 /// @class HotkeyDataViewModel
 /// @brief A wxDataViewModel for hotkeys
 class HotkeyDataViewModel final : public wxDataViewModel {
-	std::unique_ptr<HotkeyModelRoot> root;
-	Preferences *parent;
-	bool has_pending_changes = false;
-
-	/// Get the real item from the wrapper, or root if it's wrapping nullptr
-	const HotkeyModelItem *get(wxDataViewItem const& item) const;
-	/// Get the real item from the wrapper, or root if it's wrapping nullptr
-	HotkeyModelItem *get(wxDataViewItem const& item);
+    std::unique_ptr<HotkeyModelRoot> root;
+    Preferences *parent;
+    bool has_pending_changes = false;
+
+    /// Get the real item from the wrapper, or root if it's wrapping nullptr
+    const HotkeyModelItem *get(wxDataViewItem const &item) const;
+    /// Get the real item from the wrapper, or root if it's wrapping nullptr
+    HotkeyModelItem *get(wxDataViewItem const &item);
 public:
-	HotkeyDataViewModel(Preferences *parent);
-
-	/// Create a new hotkey in the current context
-	/// @param item A context or hotkey entry
-	/// @return The new hotkey
-	wxDataViewItem New(wxDataViewItem item);
-	/// Delete the currently selected hotkey
-	void Delete(wxDataViewItem const& item);
-	/// Update the hotkeys with changes made to the model
-	void Apply();
-
-	/// Only display hotkeys containing filter, or all if filter is empty
-	void SetFilter(wxString const& filter);
-
-	unsigned int GetColumnCount() const override { return 3; }
-	wxString GetColumnType(unsigned int) const override { return "string"; }
-
-	unsigned int GetChildren(wxDataViewItem const& item, wxDataViewItemArray &children) const override;
-	wxDataViewItem GetParent(wxDataViewItem const& item) const override;
-	void GetValue(wxVariant &variant, wxDataViewItem const& item, unsigned int col) const override;
-	bool IsContainer(wxDataViewItem const& item) const override;
-	bool SetValue(wxVariant const& variant, wxDataViewItem const& item, unsigned int col) override;
+    HotkeyDataViewModel(Preferences *parent);
+
+    /// Create a new hotkey in the current context
+    /// @param item A context or hotkey entry
+    /// @return The new hotkey
+    wxDataViewItem New(wxDataViewItem item);
+    /// Delete the currently selected hotkey
+    void Delete(wxDataViewItem const &item);
+    /// Update the hotkeys with changes made to the model
+    void Apply();
+
+    /// Only display hotkeys containing filter, or all if filter is empty
+    void SetFilter(wxString const &filter);
+
+    unsigned int GetColumnCount() const override { return 3; }
+    wxString GetColumnType(unsigned int) const override { return "string"; }
+
+    unsigned int GetChildren(wxDataViewItem const &item, wxDataViewItemArray &children) const override;
+    wxDataViewItem GetParent(wxDataViewItem const &item) const override;
+    void GetValue(wxVariant &variant, wxDataViewItem const &item, unsigned int col) const override;
+    bool IsContainer(wxDataViewItem const &item) const override;
+    bool SetValue(wxVariant const &variant, wxDataViewItem const &item, unsigned int col) override;
 };
diff --git a/src/include/aegisub/audio_player.h b/src/include/aegisub/audio_player.h
index 8e29e16281c03a288a39513a7ab9871936ae2379..2c22aad56934cc9bd4c345570c0c69f00344f944 100644
--- a/src/include/aegisub/audio_player.h
+++ b/src/include/aegisub/audio_player.h
@@ -46,26 +46,26 @@ class wxWindow;
 
 class AudioPlayer {
 protected:
-	agi::AudioProvider *provider;
+    agi::AudioProvider *provider;
 
 public:
-	AudioPlayer(agi::AudioProvider *provider) : provider(provider) { }
-	virtual ~AudioPlayer() = default;
+    AudioPlayer(agi::AudioProvider *provider) : provider(provider) { }
+    virtual ~AudioPlayer() = default;
 
-	virtual void Play(int64_t start,int64_t count)=0;	// Play sample range
-	virtual void Stop()=0;			// Stop playing
-	virtual bool IsPlaying()=0;
+    virtual void Play(int64_t start, int64_t count) = 0;	// Play sample range
+    virtual void Stop() = 0;			// Stop playing
+    virtual bool IsPlaying() = 0;
 
-	virtual void SetVolume(double volume)=0;
+    virtual void SetVolume(double volume) = 0;
 
-	virtual int64_t GetEndPosition()=0;
-	virtual int64_t GetCurrentPosition()=0;
-	virtual void SetEndPosition(int64_t pos)=0;
+    virtual int64_t GetEndPosition() = 0;
+    virtual int64_t GetCurrentPosition() = 0;
+    virtual void SetEndPosition(int64_t pos) = 0;
 };
 
 struct AudioPlayerFactory {
-	static std::vector<std::string> GetClasses();
-	static std::unique_ptr<AudioPlayer> GetAudioPlayer(agi::AudioProvider *provider, wxWindow *window);
+    static std::vector<std::string> GetClasses();
+    static std::unique_ptr<AudioPlayer> GetAudioPlayer(agi::AudioProvider *provider, wxWindow *window);
 };
 
 DEFINE_EXCEPTION(AudioPlayerOpenError, agi::Exception);
diff --git a/src/include/aegisub/context.h b/src/include/aegisub/context.h
index 361dcf9c7ae5a342f47ee0bced666d381d39e570..5345b9a77590b24c40d275a72e51f3a3537facb6 100644
--- a/src/include/aegisub/context.h
+++ b/src/include/aegisub/context.h
@@ -39,35 +39,35 @@ namespace agi {
 class Path;
 
 struct Context {
-	// Note: order here matters quite a bit, as things need to be set up and
+    // Note: order here matters quite a bit, as things need to be set up and
     // torn down in the correct order
-	std::unique_ptr<AssFile> ass;
-	std::unique_ptr<TextSelectionController> textSelectionController;
-	std::unique_ptr<SubsController> subsController;
-	std::unique_ptr<Project> project;
-	std::unique_ptr<Automation4::ScriptManager> local_scripts;
-	std::unique_ptr<SelectionController> selectionController;
-	std::unique_ptr<VideoController> videoController;
-	std::unique_ptr<AudioController> audioController;
-	std::unique_ptr<InitialLineState> initialLineState;
-	std::unique_ptr<SearchReplaceEngine> search;
-	std::unique_ptr<Path> path;
+    std::unique_ptr<AssFile> ass;
+    std::unique_ptr<TextSelectionController> textSelectionController;
+    std::unique_ptr<SubsController> subsController;
+    std::unique_ptr<Project> project;
+    std::unique_ptr<Automation4::ScriptManager> local_scripts;
+    std::unique_ptr<SelectionController> selectionController;
+    std::unique_ptr<VideoController> videoController;
+    std::unique_ptr<AudioController> audioController;
+    std::unique_ptr<InitialLineState> initialLineState;
+    std::unique_ptr<SearchReplaceEngine> search;
+    std::unique_ptr<Path> path;
 
-	// Things that should probably be in some sort of UI-context-model
-	wxWindow *parent = nullptr;
-	wxWindow *previousFocus = nullptr;
-	wxWindow *videoSlider = nullptr;
+    // Things that should probably be in some sort of UI-context-model
+    wxWindow *parent = nullptr;
+    wxWindow *previousFocus = nullptr;
+    wxWindow *videoSlider = nullptr;
 
-	// Views (i.e. things that should eventually not be here at all)
-	AudioBox *audioBox = nullptr;
-	AudioKaraoke *karaoke = nullptr;
-	BaseGrid *subsGrid = nullptr;
-	std::unique_ptr<DialogManager> dialog;
-	FrameMain *frame = nullptr;
-	VideoDisplay *videoDisplay = nullptr;
+    // Views (i.e. things that should eventually not be here at all)
+    AudioBox *audioBox = nullptr;
+    AudioKaraoke *karaoke = nullptr;
+    BaseGrid *subsGrid = nullptr;
+    std::unique_ptr<DialogManager> dialog;
+    FrameMain *frame = nullptr;
+    VideoDisplay *videoDisplay = nullptr;
 
-	Context();
-	~Context();
+    Context();
+    ~Context();
 };
 
 }
diff --git a/src/include/aegisub/hotkey.h b/src/include/aegisub/hotkey.h
index 4b8721ca59afcd2dfffb43ef8347a49199895a81..6abd8ec45a646ab0fb80c78109cb7827b8a816cb 100644
--- a/src/include/aegisub/hotkey.h
+++ b/src/include/aegisub/hotkey.h
@@ -22,8 +22,8 @@
 #include <wx/event.h>
 
 namespace agi {
-	struct Context;
-	namespace hotkey { class Hotkey; }
+struct Context;
+namespace hotkey { class Hotkey; }
 }
 
 namespace hotkey {
@@ -33,10 +33,10 @@ extern agi::hotkey::Hotkey *inst;
 void init();
 void clear();
 
-bool check(std::string const& context, agi::Context *c, wxKeyEvent &evt);
+bool check(std::string const &context, agi::Context *c, wxKeyEvent &evt);
 std::string keypress_to_str(int key_code, int modifier);
-std::string get_hotkey_str_first(std::string const& context, std::string const& command);
-std::vector<std::string> get_hotkey_strs(std::string const& context, std::string const& command);
+std::string get_hotkey_str_first(std::string const &context, std::string const &command);
+std::vector<std::string> get_hotkey_strs(std::string const &context, std::string const &command);
 
 
 } // namespace hotkey
diff --git a/src/include/aegisub/menu.h b/src/include/aegisub/menu.h
index 266a3c669a83af65da917c61f88ffa7df2361f24..47d67d3e9d8e0761898c20022da3ddae00bff7e0 100644
--- a/src/include/aegisub/menu.h
+++ b/src/include/aegisub/menu.h
@@ -29,33 +29,33 @@ class wxMenuBar;
 class wxWindow;
 
 namespace menu {
-	DEFINE_EXCEPTION(Error, agi::Exception);
-	DEFINE_EXCEPTION(UnknownMenu, Error);
-	DEFINE_EXCEPTION(InvalidMenu, Error);
-
-	/// @brief Get the menu with the specified name as a wxMenuBar
-	/// @param name Name of the menu
-	///
-	/// Throws:
-	///     UnknownMenu if no menu with the given name was found
-	///     BadMenu if there is a menu with the given name, but it is invalid
-	void GetMenuBar(std::string const& name, wxFrame *window, agi::Context *c);
-
-	/// @brief Get the menu with the specified name as a wxMenu
-	/// @param name Name of the menu
-	///
-	/// Throws:
-	///     UnknownMenu if no menu with the given name was found
-	///     BadMenu if there is a menu with the given name, but it is invalid
-	std::unique_ptr<wxMenu> GetMenu(std::string const& name, agi::Context *c);
-
-	/// @brief Open a popup menu at the mouse
-	/// @param menu Menu to open
-	/// @param parent_window Parent window for the menu; cannot be NULL
-	///
-	/// This function should be used rather than wxWindow::PopupMenu due to
-	/// that PopupMenu does not trigger menu open events and triggers update
-	/// ui events on the opening window rather than the menu for some bizarre
-	/// reason
-	void OpenPopupMenu(wxMenu *menu, wxWindow *parent_window);
+DEFINE_EXCEPTION(Error, agi::Exception);
+DEFINE_EXCEPTION(UnknownMenu, Error);
+DEFINE_EXCEPTION(InvalidMenu, Error);
+
+/// @brief Get the menu with the specified name as a wxMenuBar
+/// @param name Name of the menu
+///
+/// Throws:
+///     UnknownMenu if no menu with the given name was found
+///     BadMenu if there is a menu with the given name, but it is invalid
+void GetMenuBar(std::string const &name, wxFrame *window, agi::Context *c);
+
+/// @brief Get the menu with the specified name as a wxMenu
+/// @param name Name of the menu
+///
+/// Throws:
+///     UnknownMenu if no menu with the given name was found
+///     BadMenu if there is a menu with the given name, but it is invalid
+std::unique_ptr<wxMenu> GetMenu(std::string const &name, agi::Context *c);
+
+/// @brief Open a popup menu at the mouse
+/// @param menu Menu to open
+/// @param parent_window Parent window for the menu; cannot be NULL
+///
+/// This function should be used rather than wxWindow::PopupMenu due to
+/// that PopupMenu does not trigger menu open events and triggers update
+/// ui events on the opening window rather than the menu for some bizarre
+/// reason
+void OpenPopupMenu(wxMenu *menu, wxWindow *parent_window);
 }
diff --git a/src/include/aegisub/spellchecker.h b/src/include/aegisub/spellchecker.h
index f5eb4b4f5c7e6daf7fc8c83a740f680cff54e7b8..6adfdd9faa6bc5281cac963c9a488490b1381f9f 100644
--- a/src/include/aegisub/spellchecker.h
+++ b/src/include/aegisub/spellchecker.h
@@ -24,5 +24,5 @@
 namespace agi { class SpellChecker; }
 
 struct SpellCheckerFactory {
-	static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
+    static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
 };
diff --git a/src/include/aegisub/subtitles_provider.h b/src/include/aegisub/subtitles_provider.h
index c2b42ddb4b745f5d8cd1cafa6d109a1ce6a9bb92..fe9a64476f6ea544e5932fbb0546acd337d9db08 100644
--- a/src/include/aegisub/subtitles_provider.h
+++ b/src/include/aegisub/subtitles_provider.h
@@ -42,19 +42,19 @@ class AssFile;
 struct VideoFrame;
 
 class SubtitlesProvider {
-	std::vector<char> buffer;
-	virtual void LoadSubtitles(const char *data, size_t len)=0;
+    std::vector<char> buffer;
+    virtual void LoadSubtitles(const char *data, size_t len) = 0;
 
 public:
-	virtual ~SubtitlesProvider() = default;
-	void LoadSubtitles(AssFile *subs, int time = -1);
-	virtual void DrawSubtitles(VideoFrame &dst, double time)=0;
-	virtual void Reinitialize() { }
+    virtual ~SubtitlesProvider() = default;
+    void LoadSubtitles(AssFile *subs, int time = -1);
+    virtual void DrawSubtitles(VideoFrame &dst, double time) = 0;
+    virtual void Reinitialize() { }
 };
 
 namespace agi { class BackgroundRunner; }
 
 struct SubtitlesProviderFactory {
-	static std::unique_ptr<SubtitlesProvider> GetProvider(agi::BackgroundRunner *br);
-	static std::vector<std::string> GetClasses();
+    static std::unique_ptr<SubtitlesProvider> GetProvider(agi::BackgroundRunner *br);
+    static std::vector<std::string> GetClasses();
 };
diff --git a/src/include/aegisub/toolbar.h b/src/include/aegisub/toolbar.h
index e7a64b8365b6d583089ba5c565e0953fbc9cff0e..4b28d2b53694a2ab29375760546d1278522afa2e 100644
--- a/src/include/aegisub/toolbar.h
+++ b/src/include/aegisub/toolbar.h
@@ -24,11 +24,11 @@ class wxToolBar;
 class wxWindow;
 
 namespace toolbar {
-	/// Add the named toolbar to a window
-	/// @param frame Frame to attach the toolbar to
-	/// @param name Name of the toolbar
-	/// @param context Project context
-	/// @param hotkey Hotkey context for the tooltip
-	void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *context, std::string const& hotkey);
-	wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *context, std::string const& hotkey, bool vertical = false);
+/// Add the named toolbar to a window
+/// @param frame Frame to attach the toolbar to
+/// @param name Name of the toolbar
+/// @param context Project context
+/// @param hotkey Hotkey context for the tooltip
+void AttachToolbar(wxFrame *frame, std::string const &name, agi::Context *context, std::string const &hotkey);
+wxToolBar *GetToolbar(wxWindow *parent, std::string const &name, agi::Context *context, std::string const &hotkey, bool vertical = false);
 }
diff --git a/src/include/aegisub/video_provider.h b/src/include/aegisub/video_provider.h
index 43ff2454597c2a2de794bb1246ea120f50b30f30..3bca0027730afd0ec7aebb055c661d889f776b49 100644
--- a/src/include/aegisub/video_provider.h
+++ b/src/include/aegisub/video_provider.h
@@ -43,46 +43,46 @@ struct VideoFrame;
 
 class VideoProvider {
 public:
-	virtual ~VideoProvider() = default;
-
-	/// Override this method to actually get frames
-	virtual void GetFrame(int n, VideoFrame &frame)=0;
-
-	/// Set the YCbCr matrix to the specified one
-	///
-	/// Providers are free to disregard this, and should if the requested
-	/// matrix makes no sense or the input isn't YCbCr.
-	virtual void SetColorSpace(std::string const& matrix)=0;
-
-	// Override the following methods to get video information:
-	virtual int GetFrameCount() const=0;			///< Get total number of frames
-	virtual int GetWidth() const=0;					///< Returns the video width in pixels
-	virtual int GetHeight() const=0;				///< Returns the video height in pixels
-	virtual double GetDAR() const=0;				///< Returns the video display aspect ratio
-	virtual agi::vfr::Framerate GetFPS() const=0;	///< Get frame rate
-	virtual std::vector<int> GetKeyFrames() const=0;///< Returns list of keyframes
-
-	/// 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 std::string GetColorSpace() const = 0;
-	virtual std::string GetRealColorSpace() const { return GetColorSpace(); }
-
-	/// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
-	virtual std::string GetWarning() const { return ""; }
-
-	/// @brief Name of decoder, e.g. "Avisynth/FFMpegSource"
-	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.
-	virtual bool WantsCaching() const { return false; }
-
-	/// Should the video properties in the script be set to this video's property if they already have values?
-	virtual bool ShouldSetVideoProperties() const { return true; }
-
-	/// Does the file which this provider is reading have an audio track?
-	virtual bool HasAudio() const { return false; }
+    virtual ~VideoProvider() = default;
+
+    /// Override this method to actually get frames
+    virtual void GetFrame(int n, VideoFrame &frame) = 0;
+
+    /// Set the YCbCr matrix to the specified one
+    ///
+    /// Providers are free to disregard this, and should if the requested
+    /// matrix makes no sense or the input isn't YCbCr.
+    virtual void SetColorSpace(std::string const &matrix) = 0;
+
+    // Override the following methods to get video information:
+    virtual int GetFrameCount() const = 0;			///< Get total number of frames
+    virtual int GetWidth() const = 0;					///< Returns the video width in pixels
+    virtual int GetHeight() const = 0;				///< Returns the video height in pixels
+    virtual double GetDAR() const = 0;				///< Returns the video display aspect ratio
+    virtual agi::vfr::Framerate GetFPS() const = 0;	///< Get frame rate
+    virtual std::vector<int> GetKeyFrames() const = 0; ///< Returns list of keyframes
+
+    /// 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 std::string GetColorSpace() const = 0;
+    virtual std::string GetRealColorSpace() const { return GetColorSpace(); }
+
+    /// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
+    virtual std::string GetWarning() const { return ""; }
+
+    /// @brief Name of decoder, e.g. "Avisynth/FFMpegSource"
+    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.
+    virtual bool WantsCaching() const { return false; }
+
+    /// Should the video properties in the script be set to this video's property if they already have values?
+    virtual bool ShouldSetVideoProperties() const { return true; }
+
+    /// Does the file which this provider is reading have an audio track?
+    virtual bool HasAudio() const { return false; }
 };
 
 DEFINE_EXCEPTION(VideoProviderError, agi::Exception);
diff --git a/src/initial_line_state.cpp b/src/initial_line_state.cpp
index 387cb5eaf7755006c6e0b9a773887e417c994d99..0f62c841bea523ed82e32f9aa9e9b35c6d98462b 100644
--- a/src/initial_line_state.cpp
+++ b/src/initial_line_state.cpp
@@ -19,21 +19,22 @@
 #include "selection_controller.h"
 
 InitialLineState::InitialLineState(agi::Context *c)
-: active_line_connection(c->selectionController->AddActiveLineListener(&InitialLineState::OnActiveLineChanged, this))
+    : active_line_connection(c->selectionController->AddActiveLineListener(&InitialLineState::OnActiveLineChanged, this))
 {
-	OnActiveLineChanged(c->selectionController->GetActiveLine());
+    OnActiveLineChanged(c->selectionController->GetActiveLine());
 }
 
-void InitialLineState::OnActiveLineChanged(AssDialogue *new_line) {
-	if (new_line) {
-		if (new_line->Id == line_id) return;
-		line_id = new_line->Id;
-		initial_text = new_line->Text;
-	}
-	else {
-		line_id = 0;
-		initial_text.clear();
-	}
+void InitialLineState::OnActiveLineChanged(AssDialogue *new_line)
+{
+    if (new_line) {
+        if (new_line->Id == line_id) return;
+        line_id = new_line->Id;
+        initial_text = new_line->Text;
+    }
+    else {
+        line_id = 0;
+        initial_text.clear();
+    }
 
-	InitialStateChanged(initial_text);
+    InitialStateChanged(initial_text);
 }
diff --git a/src/initial_line_state.h b/src/initial_line_state.h
index a92724b34772848bdc39331fafd829cd18c7d591..663b67f3a1d3fc0c0469dcee0dff4f86b35281aa 100644
--- a/src/initial_line_state.h
+++ b/src/initial_line_state.h
@@ -20,16 +20,16 @@ namespace agi { struct Context; }
 class AssDialogue;
 
 class InitialLineState {
-	agi::signal::Connection active_line_connection;
-	std::string initial_text;
-	int line_id;
+    agi::signal::Connection active_line_connection;
+    std::string initial_text;
+    int line_id;
 
-	agi::signal::Signal<std::string const&> InitialStateChanged;
-	void OnActiveLineChanged(AssDialogue *new_line);
+    agi::signal::Signal<std::string const &> InitialStateChanged;
+    void OnActiveLineChanged(AssDialogue *new_line);
 
 public:
-	InitialLineState(agi::Context *c);
+    InitialLineState(agi::Context *c);
 
-	std::string const& GetInitialText() const { return initial_text; }
-	DEFINE_SIGNAL_ADDERS(InitialStateChanged, AddChangeListener);
+    std::string const &GetInitialText() const { return initial_text; }
+    DEFINE_SIGNAL_ADDERS(InitialStateChanged, AddChangeListener);
 };
diff --git a/src/libass_gdi_fontselect.cpp b/src/libass_gdi_fontselect.cpp
index 3623968e57f127bf0dcb1e05da17a29a6543cb97..209649c7740bc512a7897e7af7465bc2730a5d00 100644
--- a/src/libass_gdi_fontselect.cpp
+++ b/src/libass_gdi_fontselect.cpp
@@ -28,93 +28,96 @@ extern "C" {
 
 namespace {
 class GdiFont {
-	HFONT font;
-	std::shared_ptr<HDC__> dc;
+    HFONT font;
+    std::shared_ptr<HDC__> dc;
 
-	size_t size = 0;
-	std::unique_ptr<char[]> font_data;
+    size_t size = 0;
+    std::unique_ptr<char[]> font_data;
 
 public:
-	GdiFont(HFONT font, std::shared_ptr<HDC__> dc) : font(font), dc(dc) { }
-	~GdiFont() { DeleteObject(font); }
+    GdiFont(HFONT font, std::shared_ptr<HDC__> dc) : font(font), dc(dc) { }
+    ~GdiFont() { DeleteObject(font); }
 
-	size_t GetData(unsigned char *data, size_t offset, size_t len);
-	bool CheckPostscript() { return false; }
-	bool CheckGlyph(uint32_t codepoint) { return true; }
-	void Destroy() { delete this; }
+    size_t GetData(unsigned char *data, size_t offset, size_t len);
+    bool CheckPostscript() { return false; }
+    bool CheckGlyph(uint32_t codepoint) { return true; }
+    void Destroy() { delete this; }
 };
 
-size_t GdiFont::GetData(unsigned char *data, size_t offset, size_t len) {
-	if (!font_data) {
-		SelectObject(dc.get(), font);
-		size = GetFontData(dc.get(), 0, 0, 0, 0);
-		if (size == GDI_ERROR)
-			return 0;
-		font_data.reset(new char[size]);
-		GetFontData(dc.get(), 0, 0, font_data.get(), size);
-	}
-
-	if (!data)
-		return size;
-	memcpy(data, font_data.get() + offset, len);
-	return len;
+size_t GdiFont::GetData(unsigned char *data, size_t offset, size_t len)
+{
+    if (!font_data) {
+        SelectObject(dc.get(), font);
+        size = GetFontData(dc.get(), 0, 0, 0, 0);
+        if (size == GDI_ERROR)
+            return 0;
+        font_data.reset(new char[size]);
+        GetFontData(dc.get(), 0, 0, font_data.get(), size);
+    }
+
+    if (!data)
+        return size;
+    memcpy(data, font_data.get() + offset, len);
+    return len;
 }
 
-void match_fonts(ASS_Library *lib, ASS_FontProvider *provider, char *name) {
-	std::shared_ptr<HDC__> dc(CreateCompatibleDC(nullptr), [](HDC dc) { DeleteDC(dc); });
-
-	LOGFONTW lf{};
-	lf.lfCharSet = DEFAULT_CHARSET;
-	MultiByteToWideChar(CP_UTF8, 0, name, -1, lf.lfFaceName, LF_FACESIZE);
-	auto cb = [=](LOGFONTW const& lf) {
-		ASS_FontProviderMetaData meta{};
-		meta.weight = lf.lfWeight;
-		meta.slant = lf.lfItalic ? FONT_SLANT_ITALIC : FONT_SLANT_NONE;
-		meta.width = FONT_WIDTH_NORMAL;
-
-		meta.families= static_cast<char **>(malloc(sizeof(char *)));
-		meta.n_family = 1;
-
-		auto name = static_cast<char *>(malloc(LF_FACESIZE * 4));
-		auto len = wcsnlen(lf.lfFaceName, LF_FACESIZE);
-		auto written = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, len,
-		                                   name, LF_FACESIZE * 4, nullptr, nullptr);
-		name[written] = 0;
-		meta.families[0] = name;
-
-		auto hfont = CreateFontIndirectW(&lf);
-		ass_font_provider_add_font(provider, &meta, nullptr, 0, new GdiFont(hfont, dc));
-	};
-	using type = decltype(cb);
-	EnumFontFamiliesEx(dc.get(), &lf, [](const LOGFONT *lf, const TEXTMETRIC *, DWORD, LPARAM lParam) -> int {
-		(*reinterpret_cast<type*>(lParam))(*lf);
-		return 1;
-	}, (LPARAM)&cb, 0);
+void match_fonts(ASS_Library *lib, ASS_FontProvider *provider, char *name)
+{
+    std::shared_ptr<HDC__> dc(CreateCompatibleDC(nullptr), [](HDC dc) { DeleteDC(dc); });
+
+    LOGFONTW lf{};
+    lf.lfCharSet = DEFAULT_CHARSET;
+    MultiByteToWideChar(CP_UTF8, 0, name, -1, lf.lfFaceName, LF_FACESIZE);
+    auto cb = [ = ](LOGFONTW const & lf) {
+        ASS_FontProviderMetaData meta{};
+        meta.weight = lf.lfWeight;
+        meta.slant = lf.lfItalic ? FONT_SLANT_ITALIC : FONT_SLANT_NONE;
+        meta.width = FONT_WIDTH_NORMAL;
+
+        meta.families = static_cast<char **>(malloc(sizeof(char *)));
+        meta.n_family = 1;
+
+        auto name = static_cast<char *>(malloc(LF_FACESIZE * 4));
+        auto len = wcsnlen(lf.lfFaceName, LF_FACESIZE);
+        auto written = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, len,
+                                           name, LF_FACESIZE * 4, nullptr, nullptr);
+        name[written] = 0;
+        meta.families[0] = name;
+
+        auto hfont = CreateFontIndirectW(&lf);
+        ass_font_provider_add_font(provider, &meta, nullptr, 0, new GdiFont(hfont, dc));
+    };
+    using type = decltype(cb);
+    EnumFontFamiliesEx(dc.get(), &lf, [](const LOGFONT * lf, const TEXTMETRIC *, DWORD, LPARAM lParam) -> int {
+        (*reinterpret_cast<type *>(lParam))(*lf);
+        return 1;
+    }, (LPARAM)&cb, 0);
 }
 
 template <typename T, T> struct wrapper;
 template <typename T, typename R, typename... Args, R (T::*method)(Args...)>
 struct wrapper<R (T::*)(Args...), method> {
-	static R call(void *obj, Args... args) {
-		return (static_cast<T*>(obj)->*method)(args...);
-	}
+    static R call(void *obj, Args... args) {
+        return (static_cast<T *>(obj)->*method)(args...);
+    }
 };
 }
 
 extern "C"
 ASS_FontProvider *ass_directwrite_add_provider(ASS_Library *,
-                                               ASS_FontSelector *selector,
-                                               const char *) {
+        ASS_FontSelector *selector,
+        const char *)
+{
 #define WRAP(method) &wrapper<decltype(&method), &method>::call
-	static ASS_FontProviderFuncs callbacks = {
-		WRAP(GdiFont::GetData),
-		WRAP(GdiFont::CheckPostscript),
-		WRAP(GdiFont::CheckGlyph),
-		WRAP(GdiFont::Destroy),
-		nullptr, // destroy_provider
-		&match_fonts,
-		nullptr, // get_substitution
-		nullptr, // get_fallback
-	};
-	return ass_font_provider_new(selector, &callbacks, nullptr);
+    static ASS_FontProviderFuncs callbacks = {
+        WRAP(GdiFont::GetData),
+        WRAP(GdiFont::CheckPostscript),
+        WRAP(GdiFont::CheckGlyph),
+        WRAP(GdiFont::Destroy),
+        nullptr, // destroy_provider
+        &match_fonts,
+        nullptr, // get_substitution
+        nullptr, // get_fallback
+    };
+    return ass_font_provider_new(selector, &callbacks, nullptr);
 }
diff --git a/src/libresrc/libresrc.cpp b/src/libresrc/libresrc.cpp
index cb204ecb278cd12086670f4ce2f81a62b2d15160..1a99bc7731382c8b92bc6b78457c7af05e25a55b 100644
--- a/src/libresrc/libresrc.cpp
+++ b/src/libresrc/libresrc.cpp
@@ -20,16 +20,18 @@
 #include <wx/intl.h>
 #include <wx/mstream.h>
 
-wxBitmap libresrc_getimage(const unsigned char *buff, size_t size, int dir) {
-	wxMemoryInputStream mem(buff, size);
-	if (dir != wxLayout_RightToLeft)
-		return wxBitmap(wxImage(mem));
-	return wxBitmap(wxImage(mem).Mirror());
+wxBitmap libresrc_getimage(const unsigned char *buff, size_t size, int dir)
+{
+    wxMemoryInputStream mem(buff, size);
+    if (dir != wxLayout_RightToLeft)
+        return wxBitmap(wxImage(mem));
+    return wxBitmap(wxImage(mem).Mirror());
 }
 
-wxIcon libresrc_geticon(const unsigned char *buff, size_t size) {
-	wxMemoryInputStream mem(buff, size);
-	wxIcon icon;
-	icon.CopyFromBitmap(wxBitmap(wxImage(mem)));
-	return icon;
+wxIcon libresrc_geticon(const unsigned char *buff, size_t size)
+{
+    wxMemoryInputStream mem(buff, size);
+    wxIcon icon;
+    icon.CopyFromBitmap(wxBitmap(wxImage(mem)));
+    return icon;
 }
diff --git a/src/libresrc/libresrc.h b/src/libresrc/libresrc.h
index 020b8ccd88e82e87a56d2d66a8af82b50f70b0c7..2fa5b4be2aaa008c5ff89b33d61a22771c9bd2a1 100644
--- a/src/libresrc/libresrc.h
+++ b/src/libresrc/libresrc.h
@@ -21,7 +21,7 @@
 class wxBitmap;
 class wxIcon;
 
-wxBitmap libresrc_getimage(const unsigned char *image, size_t size, int dir=0);
+wxBitmap libresrc_getimage(const unsigned char *image, size_t size, int dir = 0);
 wxIcon libresrc_geticon(const unsigned char *image, size_t size);
 #define GETIMAGE(a) libresrc_getimage(a, sizeof(a))
 #define GETIMAGEDIR(a, d) libresrc_getimage(a, sizeof(a), d)
diff --git a/src/main.cpp b/src/main.cpp
index 801a3eec8cc31a7bbbddf980f208f19bb37db818..228410a250eddfaa91060a804205f0ada822bb99 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -74,10 +74,10 @@
 #include <wx/utils.h>
 
 namespace config {
-	agi::Options *opt = nullptr;
-	agi::MRUManager *mru = nullptr;
-	agi::Path *path = nullptr;
-	Automation4::AutoloadScriptManager *global_scripts;
+agi::Options *opt = nullptr;
+agi::MRUManager *mru = nullptr;
+agi::Path *path = nullptr;
+Automation4::AutoloadScriptManager *global_scripts;
 }
 
 wxIMPLEMENT_APP(AegisubApp);
@@ -90,14 +90,16 @@ static const char *LastStartupState = nullptr;
 #define StartupLog(a) LastStartupState = a
 #endif
 
-void AegisubApp::OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg) {
-	LOG_A("wx/assert") << file << ":" << line << ":" << func << "() " << cond << ": " << msg;
-	wxApp::OnAssertFailure(file, line, func, cond, msg);
+void AegisubApp::OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg)
+{
+    LOG_A("wx/assert") << file << ":" << line << ":" << func << "() " << cond << ": " << msg;
+    wxApp::OnAssertFailure(file, line, func, cond, msg);
 }
 
-AegisubApp::AegisubApp() {
-	// http://trac.wxwidgets.org/ticket/14302
-	wxSetEnv("UBUNTU_MENUPROXY", "0");
+AegisubApp::AegisubApp()
+{
+    // http://trac.wxwidgets.org/ticket/14302
+    wxSetEnv("UBUNTU_MENUPROXY", "0");
 }
 
 namespace {
@@ -109,380 +111,394 @@ static wxString exception_message = "Oops, Aegisub has crashed!\n\nAn attempt ha
 
 /// @brief Gets called when application starts.
 /// @return bool
-bool AegisubApp::OnInit() {
-	// App name (yeah, this is a little weird to get rid of an odd warning)
+bool AegisubApp::OnInit()
+{
+    // App name (yeah, this is a little weird to get rid of an odd warning)
 #if defined(__WXMSW__) || defined(__WXMAC__)
-	SetAppName("Aegisub");
+    SetAppName("Aegisub");
 #else
-	SetAppName("aegisub");
+    SetAppName("aegisub");
 #endif
 
-	// The logger isn't created on demand on background threads, so force it to
-	// be created now
-	(void)wxLog::GetActiveTarget();
-
-	{
-		// Try to get the UTF-8 version of the current locale
-		auto locale = boost::locale::generator().generate("");
-
-		// Check if we actually got a UTF-8 locale
-		using codecvt = std::codecvt<wchar_t, char, std::mbstate_t>;
-		int result = std::codecvt_base::error;
-		if (std::has_facet<codecvt>(locale)) {
-			wchar_t test[] = L"\xFFFE";
-			char buff[8];
-			auto mb = std::mbstate_t();
-			const wchar_t* from_next;
-			char* to_next;
-			result = std::use_facet<codecvt>(locale).out(mb,
-				test, std::end(test), from_next,
-				buff, std::end(buff), to_next);
-		}
-
-		// If we didn't get a UTF-8 locale, force it to a known one
-		if (result != std::codecvt_base::ok)
-			locale = boost::locale::generator().generate("en_US.UTF-8");
-		std::locale::global(locale);
-	}
-
-	boost::filesystem::path::imbue(std::locale());
-
-	// Pointless `this` capture required due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494
+    // The logger isn't created on demand on background threads, so force it to
+    // be created now
+    (void)wxLog::GetActiveTarget();
+
+    {
+        // Try to get the UTF-8 version of the current locale
+        auto locale = boost::locale::generator().generate("");
+
+        // Check if we actually got a UTF-8 locale
+        using codecvt = std::codecvt<wchar_t, char, std::mbstate_t>;
+        int result = std::codecvt_base::error;
+        if (std::has_facet<codecvt>(locale)) {
+            wchar_t test[] = L"\xFFFE";
+            char buff[8];
+            auto mb = std::mbstate_t();
+            const wchar_t *from_next;
+            char *to_next;
+            result = std::use_facet<codecvt>(locale).out(mb,
+                     test, std::end(test), from_next,
+                     buff, std::end(buff), to_next);
+        }
+
+        // If we didn't get a UTF-8 locale, force it to a known one
+        if (result != std::codecvt_base::ok)
+            locale = boost::locale::generator().generate("en_US.UTF-8");
+        std::locale::global(locale);
+    }
+
+    boost::filesystem::path::imbue(std::locale());
+
+    // Pointless `this` capture required due to http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51494
     // Because GCC 4.8 is VEARY OLD, the pointless capture as been removed...
-	agi::dispatch::Init([](agi::dispatch::Thunk f) {
-		auto evt = new ValueEvent<agi::dispatch::Thunk>(EVT_CALL_THUNK, -1, std::move(f));
-		wxTheApp->QueueEvent(evt);
-	});
-
-	wxTheApp->Bind(EVT_CALL_THUNK, [this](ValueEvent<agi::dispatch::Thunk>& evt) {
-		try {
-			evt.Get()();
-		}
-		catch (...) {
-			OnExceptionInMainLoop();
-		}
-	});
-
-	config::path = new agi::Path;
-	crash_writer::Initialize(config::path->Decode("?user"));
-
-	agi::log::log = new agi::log::LogSink;
+    agi::dispatch::Init([](agi::dispatch::Thunk f) {
+        auto evt = new ValueEvent<agi::dispatch::Thunk>(EVT_CALL_THUNK, -1, std::move(f));
+        wxTheApp->QueueEvent(evt);
+    });
+
+    wxTheApp->Bind(EVT_CALL_THUNK, [this](ValueEvent<agi::dispatch::Thunk> &evt) {
+        try {
+            evt.Get()();
+        }
+        catch (...) {
+            OnExceptionInMainLoop();
+        }
+    });
+
+    config::path = new agi::Path;
+    crash_writer::Initialize(config::path->Decode("?user"));
+
+    agi::log::log = new agi::log::LogSink;
 #ifdef _DEBUG
-	agi::log::log->Subscribe(agi::make_unique<agi::log::EmitSTDOUT>());
+    agi::log::log->Subscribe(agi::make_unique<agi::log::EmitSTDOUT>());
 #endif
 
-	// Set config file
-	StartupLog("Load local configuration");
+    // Set config file
+    StartupLog("Load local configuration");
 #ifdef __WXMSW__
-	// Try loading configuration from the install dir if one exists there
-	try {
-		auto conf_local(config::path->Decode("?data/config.json"));
-		std::unique_ptr<std::istream> 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
-		config::path->SetToken("?user", config::path->Decode("?data"));
-		config::path->SetToken("?local", config::path->Decode("?data"));
-		crash_writer::Initialize(config::path->Decode("?user"));
-	} 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
-	}
+    // Try loading configuration from the install dir if one exists there
+    try {
+        auto conf_local(config::path->Decode("?data/config.json"));
+        std::unique_ptr<std::istream> 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
+        config::path->SetToken("?user", config::path->Decode("?data"));
+        config::path->SetToken("?local", config::path->Decode("?data"));
+        crash_writer::Initialize(config::path->Decode("?user"));
+    }
+    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");
-	auto path_log = config::path->Decode("?user/log/");
-	agi::fs::CreateDirectory(path_log);
-	agi::log::log->Subscribe(agi::make_unique<agi::log::JsonEmitter>(path_log));
-	CleanCache(path_log, "*.json", 10, 100);
-
-	StartupLog("Load user configuration");
-	try {
-		if (!config::opt)
-			config::opt = new agi::Options(config::path->Decode("?user/config.json"), GET_DEFAULT_CONFIG(default_config));
-		boost::interprocess::ibufferstream stream((const char *)default_config_platform, sizeof(default_config_platform));
-		config::opt->ConfigNext(stream);
-	} catch (agi::Exception& e) {
-		LOG_E("config/init") << "Caught exception: " << e.GetMessage();
-	}
-
-	try {
-		config::opt->ConfigUser();
-	}
-	catch (agi::Exception const& err) {
-		wxMessageBox("Configuration file is invalid. Error reported:\n" + to_wx(err.GetMessage()), "Error");
-	}
+    StartupLog("Create log writer");
+    auto path_log = config::path->Decode("?user/log/");
+    agi::fs::CreateDirectory(path_log);
+    agi::log::log->Subscribe(agi::make_unique<agi::log::JsonEmitter>(path_log));
+    CleanCache(path_log, "*.json", 10, 100);
+
+    StartupLog("Load user configuration");
+    try {
+        if (!config::opt)
+            config::opt = new agi::Options(config::path->Decode("?user/config.json"), GET_DEFAULT_CONFIG(default_config));
+        boost::interprocess::ibufferstream stream((const char *)default_config_platform, sizeof(default_config_platform));
+        config::opt->ConfigNext(stream);
+    }
+    catch (agi::Exception &e) {
+        LOG_E("config/init") << "Caught exception: " << e.GetMessage();
+    }
+
+    try {
+        config::opt->ConfigUser();
+    }
+    catch (agi::Exception const &err) {
+        wxMessageBox("Configuration file is invalid. Error reported:\n" + to_wx(err.GetMessage()), "Error");
+    }
 
 #ifdef _WIN32
-	StartupLog("Load installer configuration");
-	if (OPT_GET("App/First Start")->GetBool()) {
-		try {
-			auto installer_config = agi::io::Open(config::path->Decode("?data/installer_config.json"));
-			config::opt->ConfigNext(*installer_config.get());
-		} catch (agi::fs::FileSystemError const&) {
-			// Not an error obviously as the user may not have used the installer
-		}
-	}
+    StartupLog("Load installer configuration");
+    if (OPT_GET("App/First Start")->GetBool()) {
+        try {
+            auto installer_config = agi::io::Open(config::path->Decode("?data/installer_config.json"));
+            config::opt->ConfigNext(*installer_config.get());
+        }
+        catch (agi::fs::FileSystemError const &) {
+            // Not an error obviously as the user may not have used the installer
+        }
+    }
 #endif
 
-	// Init commands.
-	cmd::init_builtin_commands();
+    // Init commands.
+    cmd::init_builtin_commands();
 
-	// Init hotkeys
-	hotkey::init();
+    // Init hotkeys
+    hotkey::init();
 
-	StartupLog("Load MRU");
-	config::mru = new agi::MRUManager(config::path->Decode("?user/mru.json"), GET_DEFAULT_CONFIG(default_mru), config::opt);
+    StartupLog("Load MRU");
+    config::mru = new agi::MRUManager(config::path->Decode("?user/mru.json"), GET_DEFAULT_CONFIG(default_mru), config::opt);
 
-	agi::util::SetThreadName("AegiMain");
+    agi::util::SetThreadName("AegiMain");
 
-	StartupLog("Inside OnInit");
-	try {
-		// Initialize randomizer
-		StartupLog("Initialize random generator");
-		srand(time(nullptr));
+    StartupLog("Inside OnInit");
+    try {
+        // Initialize randomizer
+        StartupLog("Initialize random generator");
+        srand(time(nullptr));
 
-		// locale for loading options
-		StartupLog("Set initial locale");
-		setlocale(LC_NUMERIC, "C");
-		setlocale(LC_CTYPE, "C");
+        // locale for loading options
+        StartupLog("Set initial locale");
+        setlocale(LC_NUMERIC, "C");
+        setlocale(LC_CTYPE, "C");
 
-		// Crash handling
+        // Crash handling
 #if (!defined(_DEBUG) || defined(WITH_EXCEPTIONS)) && (wxUSE_ON_FATAL_EXCEPTION+0)
-		StartupLog("Install exception handler");
-		wxHandleFatalExceptions(true);
+        StartupLog("Install exception handler");
+        wxHandleFatalExceptions(true);
 #endif
 
-		StartupLog("Store options back");
-		OPT_SET("Version/Last Version")->SetInt(GetSVNRevision());
+        StartupLog("Store options back");
+        OPT_SET("Version/Last Version")->SetInt(GetSVNRevision());
 
-		StartupLog("Initialize final locale");
+        StartupLog("Initialize final locale");
 
-		// Set locale
-		auto lang = OPT_GET("App/Language")->GetString();
-		if (lang.empty() || (lang != "en_US" && !locale.HasLanguage(lang))) {
-			lang = locale.PickLanguage();
-			OPT_SET("App/Language")->SetString(lang);
-		}
-		locale.Init(lang);
+        // Set locale
+        auto lang = OPT_GET("App/Language")->GetString();
+        if (lang.empty() || (lang != "en_US" && !locale.HasLanguage(lang))) {
+            lang = locale.PickLanguage();
+            OPT_SET("App/Language")->SetString(lang);
+        }
+        locale.Init(lang);
 
 #ifdef __APPLE__
-		// When run from an app bundle, LC_CTYPE defaults to "C", which breaks on
-		// anything involving unicode and in some cases number formatting.
-		// The right thing to do here would be to query CoreFoundation for the user's
-		// locale and add .UTF-8 to that, but :effort:
-		setlocale(LC_CTYPE, "en_US.UTF-8");
+        // When run from an app bundle, LC_CTYPE defaults to "C", which breaks on
+        // anything involving unicode and in some cases number formatting.
+        // The right thing to do here would be to query CoreFoundation for the user's
+        // locale and add .UTF-8 to that, but :effort:
+        setlocale(LC_CTYPE, "en_US.UTF-8");
 #endif
 
-		exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close.");
+        exception_message = _("Oops, Aegisub has crashed!\n\nAn attempt has been made to save a copy of your file to:\n\n%s\n\nAegisub will now close.");
 
-		// Load plugins
-		Automation4::ScriptFactory::Register(agi::make_unique<Automation4::LuaScriptFactory>());
-		libass::CacheFonts();
+        // Load plugins
+        Automation4::ScriptFactory::Register(agi::make_unique<Automation4::LuaScriptFactory>());
+        libass::CacheFonts();
 
-		// Load Automation scripts
-		StartupLog("Load global Automation scripts");
-		config::global_scripts = new Automation4::AutoloadScriptManager(OPT_GET("Path/Automation/Autoload")->GetString());
+        // Load Automation scripts
+        StartupLog("Load global Automation scripts");
+        config::global_scripts = new Automation4::AutoloadScriptManager(OPT_GET("Path/Automation/Autoload")->GetString());
 
-		// Load export filters
-		StartupLog("Register export filters");
-		AssExportFilterChain::Register(agi::make_unique<AssFixStylesFilter>());
-		AssExportFilterChain::Register(agi::make_unique<AssTransformFramerateFilter>());
+        // Load export filters
+        StartupLog("Register export filters");
+        AssExportFilterChain::Register(agi::make_unique<AssFixStylesFilter>());
+        AssExportFilterChain::Register(agi::make_unique<AssTransformFramerateFilter>());
 
-		StartupLog("Install PNG handler");
-		wxImage::AddHandler(new wxPNGHandler);
+        StartupLog("Install PNG handler");
+        wxImage::AddHandler(new wxPNGHandler);
 
-		// Open main frame
-		StartupLog("Create main window");
-		NewProjectContext();
+        // Open main frame
+        StartupLog("Create main window");
+        NewProjectContext();
 
-		// Version checker
-		StartupLog("Possibly perform automatic updates check");
-		if (OPT_GET("App/First Start")->GetBool()) {
-			OPT_SET("App/First Start")->SetBool(false);
+        // Version checker
+        StartupLog("Possibly perform automatic updates check");
+        if (OPT_GET("App/First Start")->GetBool()) {
+            OPT_SET("App/First Start")->SetBool(false);
 #ifdef WITH_UPDATE_CHECKER
-			int result = wxMessageBox(_("Do you want Aegisub to check for updates whenever it starts? You can still do it manually via the Help menu."),_("Check for updates?"), wxYES_NO | wxCENTER);
-			OPT_SET("App/Auto/Check For Updates")->SetBool(result == wxYES);
-			try {
-				config::opt->Flush();
-			}
-			catch (agi::fs::FileSystemError const& e) {
-				wxMessageBox(to_wx(e.GetMessage()), "Error saving config file", wxOK | wxICON_ERROR | wxCENTER);
-			}
+            int result = wxMessageBox(_("Do you want Aegisub to check for updates whenever it starts? You can still do it manually via the Help menu."), _("Check for updates?"), wxYES_NO | wxCENTER);
+            OPT_SET("App/Auto/Check For Updates")->SetBool(result == wxYES);
+            try {
+                config::opt->Flush();
+            }
+            catch (agi::fs::FileSystemError const &e) {
+                wxMessageBox(to_wx(e.GetMessage()), "Error saving config file", wxOK | wxICON_ERROR | wxCENTER);
+            }
 #endif
-		}
+        }
 
 #ifdef WITH_UPDATE_CHECKER
-		PerformVersionCheck(false);
+        PerformVersionCheck(false);
 #endif
 
-		// Get parameter subs
-		StartupLog("Parse command line");
-		auto const& args = argv.GetArguments();
-		if (args.size() > 1)
-			OpenFiles(wxArrayStringsAdapter(args.size() - 1, &args[1]));
-	}
-	catch (agi::Exception const& e) {
-		wxMessageBox(to_wx(e.GetMessage()), "Fatal error while initializing");
-		return false;
-	}
-	catch (std::exception const& e) {
-		wxMessageBox(to_wx(e.what()), "Fatal error while initializing");
-		return false;
-	}
+        // Get parameter subs
+        StartupLog("Parse command line");
+        auto const &args = argv.GetArguments();
+        if (args.size() > 1)
+            OpenFiles(wxArrayStringsAdapter(args.size() - 1, &args[1]));
+    }
+    catch (agi::Exception const &e) {
+        wxMessageBox(to_wx(e.GetMessage()), "Fatal error while initializing");
+        return false;
+    }
+    catch (std::exception const &e) {
+        wxMessageBox(to_wx(e.what()), "Fatal error while initializing");
+        return false;
+    }
 #ifndef _DEBUG
-	catch (...) {
-		wxMessageBox("Unhandled exception","Fatal error while initializing");
-		return false;
-	}
+    catch (...) {
+        wxMessageBox("Unhandled exception", "Fatal error while initializing");
+        return false;
+    }
 #endif
 
-	StartupLog("Clean old autosave files");
-	CleanCache(config::path->Decode(OPT_GET("Path/Auto/Save")->GetString()), "*.AUTOSAVE.ass", 100, 1000);
+    StartupLog("Clean old autosave files");
+    CleanCache(config::path->Decode(OPT_GET("Path/Auto/Save")->GetString()), "*.AUTOSAVE.ass", 100, 1000);
 
-	StartupLog("Initialization complete");
-	return true;
+    StartupLog("Initialization complete");
+    return true;
 }
 
-int AegisubApp::OnExit() {
-	for (auto frame : frames)
-		delete frame;
-	frames.clear();
+int AegisubApp::OnExit()
+{
+    for (auto frame : frames)
+        delete frame;
+    frames.clear();
 
-	if (wxTheClipboard->Open()) {
-		wxTheClipboard->Flush();
-		wxTheClipboard->Close();
-	}
+    if (wxTheClipboard->Open()) {
+        wxTheClipboard->Flush();
+        wxTheClipboard->Close();
+    }
 
-	delete config::opt;
-	delete config::mru;
-	hotkey::clear();
-	cmd::clear();
+    delete config::opt;
+    delete config::mru;
+    hotkey::clear();
+    cmd::clear();
 
-	delete config::global_scripts;
+    delete config::global_scripts;
 
-	AssExportFilterChain::Clear();
+    AssExportFilterChain::Clear();
 
-	// Keep this last!
-	delete agi::log::log;
-	crash_writer::Cleanup();
+    // Keep this last!
+    delete agi::log::log;
+    crash_writer::Cleanup();
 
-	return wxApp::OnExit();
+    return wxApp::OnExit();
 }
 
-agi::Context& AegisubApp::NewProjectContext() {
-	auto frame = new FrameMain;
-	frame->Bind(wxEVT_DESTROY, [=](wxWindowDestroyEvent& evt) {
-		if (evt.GetWindow() != frame) {
-			evt.Skip();
-			return;
-		}
-
-		frames.erase(remove(begin(frames), end(frames), frame), end(frames));
-		if (frames.empty()) {
-			ExitMainLoop();
-		}
-	});
-	frames.push_back(frame);
-	return *frame->context;
+agi::Context &AegisubApp::NewProjectContext()
+{
+    auto frame = new FrameMain;
+    frame->Bind(wxEVT_DESTROY, [ = ](wxWindowDestroyEvent & evt) {
+        if (evt.GetWindow() != frame) {
+            evt.Skip();
+            return;
+        }
+
+        frames.erase(remove(begin(frames), end(frames), frame), end(frames));
+        if (frames.empty()) {
+            ExitMainLoop();
+        }
+    });
+    frames.push_back(frame);
+    return *frame->context;
 }
 
-void AegisubApp::CloseAll() {
-	for (auto frame : frames) {
-		if (!frame->Close())
-			break;
-	}
+void AegisubApp::CloseAll()
+{
+    for (auto frame : frames) {
+        if (!frame->Close())
+            break;
+    }
 }
 
-void AegisubApp::UnhandledException(bool stackWalk) {
+void AegisubApp::UnhandledException(bool stackWalk)
+{
 #if (!defined(_DEBUG) || defined(WITH_EXCEPTIONS)) && (wxUSE_ON_FATAL_EXCEPTION+0)
-	bool any = false;
-	agi::fs::path path;
-	for (auto& frame : frames) {
-		auto c = frame->context.get();
-		if (!c || !c->ass || !c->subsController) continue;
-
-		path = config::path->Decode("?user/recovered");
-		agi::fs::CreateDirectory(path);
-
-		auto filename = c->subsController->Filename().stem();
-		filename.replace_extension(agi::format("%s.ass", agi::util::strftime("%Y-%m-%d-%H-%M-%S")));
-		path /= filename;
-		c->subsController->Save(path);
-
-		any = true;
-	}
-
-	if (stackWalk)
-		crash_writer::Write();
-
-	if (any) {
-		// Inform user of crash.
-		wxMessageBox(agi::wxformat(exception_message, path), _("Program error"), wxOK | wxICON_ERROR | wxCENTER, nullptr);
-	}
-	else if (LastStartupState) {
-		wxMessageBox(fmt_wx("Aegisub has crashed while starting up!\n\nThe last startup step attempted was: %s.", LastStartupState), _("Program error"), wxOK | wxICON_ERROR | wxCENTER);
-	}
+    bool any = false;
+    agi::fs::path path;
+    for (auto &frame : frames) {
+        auto c = frame->context.get();
+        if (!c || !c->ass || !c->subsController) continue;
+
+        path = config::path->Decode("?user/recovered");
+        agi::fs::CreateDirectory(path);
+
+        auto filename = c->subsController->Filename().stem();
+        filename.replace_extension(agi::format("%s.ass", agi::util::strftime("%Y-%m-%d-%H-%M-%S")));
+        path /= filename;
+        c->subsController->Save(path);
+
+        any = true;
+    }
+
+    if (stackWalk)
+        crash_writer::Write();
+
+    if (any) {
+        // Inform user of crash.
+        wxMessageBox(agi::wxformat(exception_message, path), _("Program error"), wxOK | wxICON_ERROR | wxCENTER, nullptr);
+    }
+    else if (LastStartupState) {
+        wxMessageBox(fmt_wx("Aegisub has crashed while starting up!\n\nThe last startup step attempted was: %s.", LastStartupState), _("Program error"), wxOK | wxICON_ERROR | wxCENTER);
+    }
 #endif
 }
 
-void AegisubApp::OnUnhandledException() {
-	UnhandledException(false);
+void AegisubApp::OnUnhandledException()
+{
+    UnhandledException(false);
 }
 
-void AegisubApp::OnFatalException() {
-	UnhandledException(true);
+void AegisubApp::OnFatalException()
+{
+    UnhandledException(true);
 }
 
 #define SHOW_EXCEPTION(str) \
 	wxMessageBox(fmt_tl("An unexpected error has occurred. Please save your work and restart Aegisub.\n\nError Message: %s", str), \
 				"Exception in event handler", wxOK | wxICON_ERROR | wxCENTER | wxSTAY_ON_TOP)
-bool AegisubApp::OnExceptionInMainLoop() {
-	try {
-		throw;
-	}
-	catch (const agi::Exception &e) {
-		SHOW_EXCEPTION(to_wx(e.GetMessage()));
-	}
-	catch (const std::exception &e) {
-		SHOW_EXCEPTION(to_wx(e.what()));
-	}
-	catch (...) {
-		SHOW_EXCEPTION("Unknown error");
-	}
-	return true;
+bool AegisubApp::OnExceptionInMainLoop()
+{
+    try {
+        throw;
+    }
+    catch (const agi::Exception &e) {
+        SHOW_EXCEPTION(to_wx(e.GetMessage()));
+    }
+    catch (const std::exception &e) {
+        SHOW_EXCEPTION(to_wx(e.what()));
+    }
+    catch (...) {
+        SHOW_EXCEPTION("Unknown error");
+    }
+    return true;
 }
 
 #undef SHOW_EXCEPTION
 
-int AegisubApp::OnRun() {
-	std::string error;
-
-	try {
-		return MainLoop();
-	}
-	catch (const std::exception &e) { error = std::string("std::exception: ") + e.what(); }
-	catch (const agi::Exception &e) { error = "agi::exception: " + e.GetMessage(); }
-	catch (...) { error = "Program terminated in error."; }
-
-	// Report errors
-	if (!error.empty()) {
-		crash_writer::Write(error);
-		OnUnhandledException();
-	}
-
-	ExitMainLoop();
-	return 1;
+int AegisubApp::OnRun()
+{
+    std::string error;
+
+    try {
+        return MainLoop();
+    }
+    catch (const std::exception &e) { error = std::string("std::exception: ") + e.what(); }
+    catch (const agi::Exception &e) { error = "agi::exception: " + e.GetMessage(); }
+    catch (...) { error = "Program terminated in error."; }
+
+    // Report errors
+    if (!error.empty()) {
+        crash_writer::Write(error);
+        OnUnhandledException();
+    }
+
+    ExitMainLoop();
+    return 1;
 }
 
-void AegisubApp::MacOpenFiles(wxArrayString const& filenames) {
-	OpenFiles(filenames);
+void AegisubApp::MacOpenFiles(wxArrayString const &filenames)
+{
+    OpenFiles(filenames);
 }
 
-void AegisubApp::OpenFiles(wxArrayStringsAdapter filenames) {
-	std::vector<agi::fs::path> files;
-	for (size_t i = 0; i < filenames.GetCount(); ++i)
-		files.push_back(from_wx(filenames[i]));
-	if (!files.empty())
-		frames[0]->context->project->LoadList(files);
+void AegisubApp::OpenFiles(wxArrayStringsAdapter filenames)
+{
+    std::vector<agi::fs::path> files;
+    for (size_t i = 0; i < filenames.GetCount(); ++i)
+        files.push_back(from_wx(filenames[i]));
+    if (!files.empty())
+        frames[0]->context->project->LoadList(files);
 }
diff --git a/src/main.h b/src/main.h
index de65b65da0ebfc3c1d5daabfd017d3ce2562d94a..c42689d4039653f3e276dd73292c357683b91cd2 100644
--- a/src/main.h
+++ b/src/main.h
@@ -37,40 +37,40 @@
 
 class FrameMain;
 namespace agi {
-	struct Context;
+struct Context;
 }
 
 class AegisubApp : public wxApp {
-	friend class FrameMain;
+    friend class FrameMain;
 
-	bool OnInit() override;
-	int OnExit() override;
-	int OnRun() override;
+    bool OnInit() override;
+    int OnExit() override;
+    int OnRun() override;
 
-	void OnUnhandledException() override;
-	void OnFatalException() override;
-	bool OnExceptionInMainLoop() override;
+    void OnUnhandledException() override;
+    void OnFatalException() override;
+    bool OnExceptionInMainLoop() override;
 
-	void OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg) override;
+    void OnAssertFailure(const wxChar *file, int line, const wxChar *func, const wxChar *cond, const wxChar *msg) override;
 
-	void UnhandledException(bool);
+    void UnhandledException(bool);
 
-	void OpenFiles(wxArrayStringsAdapter filenames);
+    void OpenFiles(wxArrayStringsAdapter filenames);
 
-	std::vector<FrameMain *> frames;
+    std::vector<FrameMain *> frames;
 public:
-	AegisubApp();
-	AegisubLocale locale;
+    AegisubApp();
+    AegisubLocale locale;
 
-	agi::Context& NewProjectContext();
-	void CloseAll();
+    agi::Context &NewProjectContext();
+    void CloseAll();
 
-	// Apple events
-	void MacOpenFiles(wxArrayString const& filenames)
+    // Apple events
+    void MacOpenFiles(wxArrayString const &filenames)
 #ifdef __APPLE__
-		override
+    override
 #endif
-	;
+    ;
 };
 
 wxDECLARE_APP(AegisubApp);
diff --git a/src/menu.cpp b/src/menu.cpp
index 99edbcf7d86d96a818e190449e9036bca14eba16..6449022405d6dd7b36c74aadf633a3174597e63d 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -55,59 +55,58 @@ namespace {
 static const int MENU_ID_BASE = 10000;
 
 class MruMenu final : public wxMenu {
-	std::string type;
-	std::vector<wxMenuItem *> items;
-	std::vector<std::string> *cmds;
-
-	void Resize(size_t new_size) {
-		for (size_t i = GetMenuItemCount(); i > new_size; --i) {
-			Remove(FindItemByPosition(i - 1));
-		}
-
-		for (size_t i = GetMenuItemCount(); i < new_size; ++i) {
-			if (i >= items.size()) {
-				items.push_back(new wxMenuItem(this, MENU_ID_BASE + cmds->size(), "_"));
-				cmds->push_back(agi::format("recent/%s/%d", boost::to_lower_copy(type), i));
-			}
-			Append(items[i]);
-		}
-	}
+    std::string type;
+    std::vector<wxMenuItem *> items;
+    std::vector<std::string> *cmds;
+
+    void Resize(size_t new_size) {
+        for (size_t i = GetMenuItemCount(); i > new_size; --i) {
+            Remove(FindItemByPosition(i - 1));
+        }
+
+        for (size_t i = GetMenuItemCount(); i < new_size; ++i) {
+            if (i >= items.size()) {
+                items.push_back(new wxMenuItem(this, MENU_ID_BASE + cmds->size(), "_"));
+                cmds->push_back(agi::format("recent/%s/%d", boost::to_lower_copy(type), i));
+            }
+            Append(items[i]);
+        }
+    }
 
 public:
-	MruMenu(std::string type, std::vector<std::string> *cmds)
-	: type(std::move(type))
-	, cmds(cmds)
-	{
-	}
-
-	~MruMenu() {
-		// Append all items to ensure that they're all cleaned up
-		Resize(items.size());
-	}
-
-	void Update() {
-		const auto mru = config::mru->Get(type.c_str());
-
-		Resize(mru->size());
-
-		if (mru->empty()) {
-			Resize(1);
-			items[0]->Enable(false);
-			items[0]->SetItemLabel(_("Empty"));
-			return;
-		}
-
-		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(fmt_wx("%s%d %s",
-				i <= 9 ? "&" : "", i + 1,
-				name));
-			items[i]->Enable(true);
-		}
-	}
+    MruMenu(std::string type, std::vector<std::string> *cmds)
+        : type(std::move(type))
+        , cmds(cmds) {
+    }
+
+    ~MruMenu() {
+        // Append all items to ensure that they're all cleaned up
+        Resize(items.size());
+    }
+
+    void Update() {
+        const auto mru = config::mru->Get(type.c_str());
+
+        Resize(mru->size());
+
+        if (mru->empty()) {
+            Resize(1);
+            items[0]->Enable(false);
+            items[0]->SetItemLabel(_("Empty"));
+            return;
+        }
+
+        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(fmt_wx("%s%d %s",
+                                          i <= 9 ? "&" : "", i + 1,
+                                          name));
+            items[i]->Enable(true);
+        }
+    }
 };
 
 /// @class CommandManager
@@ -118,171 +117,170 @@ public:
 /// on submenus in many cases, and registering large numbers of wxEVT_UPDATE_UI
 /// handlers makes everything involves events unusably slow.
 class CommandManager {
-	/// Menu items which need to do something on menu open
-	std::vector<std::pair<std::string, wxMenuItem*>> dynamic_items;
-	/// Menu items which need to be updated only when hotkeys change
-	std::vector<std::pair<std::string, wxMenuItem*>> static_items;
-	/// window id -> command map
-	std::vector<std::string> items;
-	/// MRU menus which need to be updated on menu open
-	std::vector<MruMenu*> mru;
-
-	/// Project context
-	agi::Context *context;
-
-	/// Connection for hotkey change signal
-	agi::signal::Connection hotkeys_changed;
-
-	/// Update a single dynamic menu item
-	void UpdateItem(std::pair<std::string, wxMenuItem*> const& item) {
-		cmd::Command *c = cmd::get(item.first);
-		int flags = c->Type();
-		if (flags & cmd::COMMAND_VALIDATE) {
-			item.second->Enable(c->Validate(context));
-			flags = c->Type();
-		}
-		if (flags & cmd::COMMAND_DYNAMIC_NAME)
-			UpdateItemName(item);
-		if (flags & cmd::COMMAND_DYNAMIC_HELP)
-			item.second->SetHelp(c->StrHelp());
-		if (flags & cmd::COMMAND_RADIO || flags & cmd::COMMAND_TOGGLE) {
-			bool check = c->IsActive(context);
-			// Don't call Check(false) on radio items as this causes wxGtk to
-			// send a menu clicked event, and it should be a no-op anyway
-			if (check || flags & cmd::COMMAND_TOGGLE)
-				item.second->Check(check);
-		}
-	}
-
-	void UpdateItemName(std::pair<std::string, wxMenuItem*> const& item) {
-		cmd::Command *c = cmd::get(item.first);
-		wxString text;
-		if (c->Type() & cmd::COMMAND_DYNAMIC_NAME)
-			text = c->StrMenu(context);
-		else
-			text = item.second->GetItemLabel().BeforeFirst('\t');
-		item.second->SetItemLabel(text + to_wx("\t" + hotkey::get_hotkey_str_first("Default", c->name())));
-	}
+    /// Menu items which need to do something on menu open
+    std::vector<std::pair<std::string, wxMenuItem *>> dynamic_items;
+    /// Menu items which need to be updated only when hotkeys change
+    std::vector<std::pair<std::string, wxMenuItem *>> static_items;
+    /// window id -> command map
+    std::vector<std::string> items;
+    /// MRU menus which need to be updated on menu open
+    std::vector<MruMenu *> mru;
+
+    /// Project context
+    agi::Context *context;
+
+    /// Connection for hotkey change signal
+    agi::signal::Connection hotkeys_changed;
+
+    /// Update a single dynamic menu item
+    void UpdateItem(std::pair<std::string, wxMenuItem *> const &item) {
+        cmd::Command *c = cmd::get(item.first);
+        int flags = c->Type();
+        if (flags & cmd::COMMAND_VALIDATE) {
+            item.second->Enable(c->Validate(context));
+            flags = c->Type();
+        }
+        if (flags & cmd::COMMAND_DYNAMIC_NAME)
+            UpdateItemName(item);
+        if (flags & cmd::COMMAND_DYNAMIC_HELP)
+            item.second->SetHelp(c->StrHelp());
+        if (flags & cmd::COMMAND_RADIO || flags & cmd::COMMAND_TOGGLE) {
+            bool check = c->IsActive(context);
+            // Don't call Check(false) on radio items as this causes wxGtk to
+            // send a menu clicked event, and it should be a no-op anyway
+            if (check || flags & cmd::COMMAND_TOGGLE)
+                item.second->Check(check);
+        }
+    }
+
+    void UpdateItemName(std::pair<std::string, wxMenuItem *> const &item) {
+        cmd::Command *c = cmd::get(item.first);
+        wxString text;
+        if (c->Type() & cmd::COMMAND_DYNAMIC_NAME)
+            text = c->StrMenu(context);
+        else
+            text = item.second->GetItemLabel().BeforeFirst('\t');
+        item.second->SetItemLabel(text + to_wx("\t" + hotkey::get_hotkey_str_first("Default", c->name())));
+    }
 
 public:
-	CommandManager(agi::Context *context)
-	: context(context)
-	, hotkeys_changed(hotkey::inst->AddHotkeyChangeListener(&CommandManager::OnHotkeysChanged, this))
-	{
-	}
-
-	void SetContext(agi::Context *c) {
-		context = c;
-	}
-
-	int AddCommand(cmd::Command *co, wxMenu *parent, std::string const& text = "") {
-		return AddCommand(co, parent, text.empty() ? co->StrMenu(context) : _(to_wx(text)));
-	}
-
-	// because wxString doesn't have a move constructor
-	int AddCommand(cmd::Command *co, wxMenu *parent, wxString const& menu_text) {
-		return AddCommand(co, parent, wxString(menu_text));
-	}
-
-	/// Append a command to a menu and register the needed handlers
-	int AddCommand(cmd::Command *co, wxMenu *parent, wxString&& menu_text) {
-		int flags = co->Type();
-		wxItemKind kind =
-			flags & cmd::COMMAND_RADIO ? wxITEM_RADIO :
-			flags & cmd::COMMAND_TOGGLE ? wxITEM_CHECK :
-			wxITEM_NORMAL;
-
-		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);
+    CommandManager(agi::Context *context)
+        : context(context)
+        , hotkeys_changed(hotkey::inst->AddHotkeyChangeListener(&CommandManager::OnHotkeysChanged, this)) {
+    }
+
+    void SetContext(agi::Context *c) {
+        context = c;
+    }
+
+    int AddCommand(cmd::Command *co, wxMenu *parent, std::string const &text = "") {
+        return AddCommand(co, parent, text.empty() ? co->StrMenu(context) : _(to_wx(text)));
+    }
+
+    // because wxString doesn't have a move constructor
+    int AddCommand(cmd::Command *co, wxMenu *parent, wxString const &menu_text) {
+        return AddCommand(co, parent, wxString(menu_text));
+    }
+
+    /// Append a command to a menu and register the needed handlers
+    int AddCommand(cmd::Command *co, wxMenu *parent, wxString &&menu_text) {
+        int flags = co->Type();
+        wxItemKind kind =
+            flags & cmd::COMMAND_RADIO ? wxITEM_RADIO :
+            flags & cmd::COMMAND_TOGGLE ? wxITEM_CHECK :
+            wxITEM_NORMAL;
+
+        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__
-		/// @todo Maybe make this a configuration option instead?
-		if (kind == wxITEM_NORMAL)
-			item->SetBitmap(co->Icon(16));
+        /// @todo Maybe make this a configuration option instead?
+        if (kind == wxITEM_NORMAL)
+            item->SetBitmap(co->Icon(16));
 #endif
-		parent->Append(item);
-		items.push_back(co->name());
-
-		if (flags != cmd::COMMAND_NORMAL)
-			dynamic_items.emplace_back(co->name(), item);
-		else
-			static_items.emplace_back(co->name(), item);
-
-		return item->GetId();
-	}
-
-	/// Unregister a dynamic menu item
-	void Remove(wxMenuItem *item) {
-		auto pred = [=](std::pair<std::string, wxMenuItem*> const& o) {
-			return o.second == item;
-		};
-
-		auto it = find_if(dynamic_items.begin(), dynamic_items.end(), pred);
-		if (it != dynamic_items.end())
-			dynamic_items.erase(it);
-		it = find_if(static_items.begin(), static_items.end(), pred);
-		if (it != static_items.end())
-			static_items.erase(it);
-	}
-
-	/// Create a MRU menu and register the needed handlers
-	/// @param name MRU type
-	/// @param parent Menu to append the new MRU menu to
-	void AddRecent(std::string const& name, wxMenu *parent) {
-		mru.push_back(new MruMenu(name, &items));
-		parent->AppendSubMenu(mru.back(), _("&Recent"));
-	}
-
-	void OnMenuOpen(wxMenuEvent &) {
-		if (!context)
-			return;
-		for (auto const& item : dynamic_items) UpdateItem(item);
-		for (auto item : mru) item->Update();
-	}
-
-	void OnMenuClick(wxCommandEvent &evt) {
-		// This also gets clicks on unrelated things such as the toolbar, so
-		// the window ID ranges really need to be unique
-		size_t id = static_cast<size_t>(evt.GetId() - MENU_ID_BASE);
-		if (id < items.size() && context)
-			cmd::call(items[id], context);
+        parent->Append(item);
+        items.push_back(co->name());
+
+        if (flags != cmd::COMMAND_NORMAL)
+            dynamic_items.emplace_back(co->name(), item);
+        else
+            static_items.emplace_back(co->name(), item);
+
+        return item->GetId();
+    }
+
+    /// Unregister a dynamic menu item
+    void Remove(wxMenuItem *item) {
+        auto pred = [ = ](std::pair<std::string, wxMenuItem *> const & o) {
+            return o.second == item;
+        };
+
+        auto it = find_if(dynamic_items.begin(), dynamic_items.end(), pred);
+        if (it != dynamic_items.end())
+            dynamic_items.erase(it);
+        it = find_if(static_items.begin(), static_items.end(), pred);
+        if (it != static_items.end())
+            static_items.erase(it);
+    }
+
+    /// Create a MRU menu and register the needed handlers
+    /// @param name MRU type
+    /// @param parent Menu to append the new MRU menu to
+    void AddRecent(std::string const &name, wxMenu *parent) {
+        mru.push_back(new MruMenu(name, &items));
+        parent->AppendSubMenu(mru.back(), _("&Recent"));
+    }
+
+    void OnMenuOpen(wxMenuEvent &) {
+        if (!context)
+            return;
+        for (auto const &item : dynamic_items) UpdateItem(item);
+        for (auto item : mru) item->Update();
+    }
+
+    void OnMenuClick(wxCommandEvent &evt) {
+        // This also gets clicks on unrelated things such as the toolbar, so
+        // the window ID ranges really need to be unique
+        size_t id = static_cast<size_t>(evt.GetId() - MENU_ID_BASE);
+        if (id < items.size() && context)
+            cmd::call(items[id], context);
 
 #ifdef __WXMAC__
-		else {
-			switch (evt.GetId()) {
-				case wxID_ABOUT:
-					cmd::call("app/about", context);
-					break;
-				case wxID_PREFERENCES:
-					cmd::call("app/options", context);
-					break;
-				case wxID_EXIT:
-					cmd::call("app/exit", context);
-					break;
-				default:
-					break;
-			}
-		}
+        else {
+            switch (evt.GetId()) {
+            case wxID_ABOUT:
+                cmd::call("app/about", context);
+                break;
+            case wxID_PREFERENCES:
+                cmd::call("app/options", context);
+                break;
+            case wxID_EXIT:
+                cmd::call("app/exit", context);
+                break;
+            default:
+                break;
+            }
+        }
 #endif
-	}
+    }
 
-	/// Update the hotkeys for all menu items
-	void OnHotkeysChanged() {
-		for (auto const& item : dynamic_items) UpdateItemName(item);
-		for (auto const& item : static_items) UpdateItemName(item);
-	}
+    /// Update the hotkeys for all menu items
+    void OnHotkeysChanged() {
+        for (auto const &item : dynamic_items) UpdateItemName(item);
+        for (auto const &item : static_items) UpdateItemName(item);
+    }
 };
 
 /// Wrapper for wxMenu to add a command manager
 struct CommandMenu final : public wxMenu {
-	CommandManager cm;
-	CommandMenu(agi::Context *c) : cm(c) { }
+    CommandManager cm;
+    CommandMenu(agi::Context *c) : cm(c) { }
 };
 
 /// Wrapper for wxMenuBar to add a command manager
 struct CommandMenuBar final : public wxMenuBar {
-	CommandManager cm;
-	CommandMenuBar(agi::Context *c) : cm(c) { }
+    CommandManager cm;
+    CommandMenuBar(agi::Context *c) : cm(c) { }
 };
 
 /// Read a string from a json object
@@ -290,275 +288,282 @@ struct CommandMenuBar final : public wxMenuBar {
 /// @param name Index to read from
 /// @param[out] value Output value to write to
 /// @return Was the requested index found
-bool read_entry(json::Object const& obj, const char *name, std::string *value) {
-	auto it = obj.find(name);
-	if (it == obj.end()) return false;
-	*value = static_cast<json::String const&>(it->second);
-	return true;
+bool read_entry(json::Object const &obj, const char *name, std::string *value)
+{
+    auto it = obj.find(name);
+    if (it == obj.end()) return false;
+    *value = static_cast<json::String const &>(it->second);
+    return true;
 }
 
 /// Get the root object of the menu configuration
-json::Object const& get_menus_root() {
-	static json::Object root;
-	if (!root.empty()) return root;
-
-	try {
-		root = std::move(static_cast<json::Object&>(agi::json_util::file(config::path->Decode("?user/menu.json"), GET_DEFAULT_CONFIG(default_menu))));
-		return root;
-	}
-	catch (json::Reader::ParseException const& e) {
-		LOG_E("menu/parse") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
-		throw;
-	}
-	catch (std::exception const& e) {
-		LOG_E("menu/parse") << e.what();
-		throw;
-	}
+json::Object const &get_menus_root()
+{
+    static json::Object root;
+    if (!root.empty()) return root;
+
+    try {
+        root = std::move(static_cast<json::Object &>(agi::json_util::file(config::path->Decode("?user/menu.json"), GET_DEFAULT_CONFIG(default_menu))));
+        return root;
+    }
+    catch (json::Reader::ParseException const &e) {
+        LOG_E("menu/parse") << "json::ParseException: " << e.what() << ", Line/offset: " << e.m_locTokenBegin.m_nLine + 1 << '/' << e.m_locTokenBegin.m_nLineOffset + 1;
+        throw;
+    }
+    catch (std::exception const &e) {
+        LOG_E("menu/parse") << e.what();
+        throw;
+    }
 }
 
 /// Get the menu with the specified name
 /// @param name Name of menu to get
 /// @return Array of menu items
-json::Array const& get_menu(std::string const& name) {
-	auto const& root = get_menus_root();
+json::Array const &get_menu(std::string const &name)
+{
+    auto const &root = get_menus_root();
 
-	auto it = root.find(name);
-	if (it == root.end()) throw menu::UnknownMenu("Menu named " + name + " not found");
-	return it->second;
+    auto it = root.find(name);
+    if (it == root.end()) throw menu::UnknownMenu("Menu named " + name + " not found");
+    return it->second;
 }
 
-wxMenu *build_menu(std::string const& name, agi::Context *c, CommandManager *cm, wxMenu *menu = nullptr);
+wxMenu *build_menu(std::string const &name, agi::Context *c, CommandManager *cm, wxMenu *menu = nullptr);
 
 /// Recursively process a single entry in the menu json
 /// @param parent Menu to add the item(s) from this entry to
 /// @param c Project context to bind the menu to
 /// @param ele json object to process
 /// @param cm Command manager for this menu
-void process_menu_item(wxMenu *parent, agi::Context *c, json::Object const& ele, CommandManager *cm) {
-	if (ele.empty()) {
-		parent->AppendSeparator();
-		return;
-	}
+void process_menu_item(wxMenu *parent, agi::Context *c, json::Object const &ele, CommandManager *cm)
+{
+    if (ele.empty()) {
+        parent->AppendSeparator();
+        return;
+    }
 
-	std::string submenu, recent, command, text, special;
-	read_entry(ele, "special", &special);
+    std::string submenu, recent, command, text, special;
+    read_entry(ele, "special", &special);
 
 #ifdef __WXMAC__
-	if (special == "window")
-		osx::make_windows_menu(parent);
+    if (special == "window")
+        osx::make_windows_menu(parent);
 #endif
 
-	if (read_entry(ele, "submenu", &submenu) && read_entry(ele, "text", &text)) {
-		wxString tl_text = _(to_wx(text));
-		parent->AppendSubMenu(build_menu(submenu, c, cm), tl_text);
+    if (read_entry(ele, "submenu", &submenu) && read_entry(ele, "text", &text)) {
+        wxString tl_text = _(to_wx(text));
+        parent->AppendSubMenu(build_menu(submenu, c, cm), tl_text);
 #ifdef __WXMAC__
-		if (special == "help")
-			wxApp::s_macHelpMenuTitleName = tl_text;
+        if (special == "help")
+            wxApp::s_macHelpMenuTitleName = tl_text;
 #endif
-		return;
-	}
+        return;
+    }
 
-	if (read_entry(ele, "recent", &recent)) {
-		cm->AddRecent(recent, parent);
-		return;
-	}
+    if (read_entry(ele, "recent", &recent)) {
+        cm->AddRecent(recent, parent);
+        return;
+    }
 
-	if (!read_entry(ele, "command", &command))
-		return;
+    if (!read_entry(ele, "command", &command))
+        return;
 
-	read_entry(ele, "text", &text);
+    read_entry(ele, "text", &text);
 
-	try {
-		int id = cm->AddCommand(cmd::get(command), parent, text);
+    try {
+        int id = cm->AddCommand(cmd::get(command), parent, text);
 #ifdef __WXMAC__
-		if (!special.empty()) {
-			if (special == "about")
-				wxApp::s_macAboutMenuItemId = id;
-			else if (special == "exit")
-				wxApp::s_macExitMenuItemId = id;
-			else if (special == "options")
-				wxApp::s_macPreferencesMenuItemId = id;
-		}
+        if (!special.empty()) {
+            if (special == "about")
+                wxApp::s_macAboutMenuItemId = id;
+            else if (special == "exit")
+                wxApp::s_macExitMenuItemId = id;
+            else if (special == "options")
+                wxApp::s_macPreferencesMenuItemId = id;
+        }
 #else
-		(void)id;
+        (void)id;
 #endif
-	}
-	catch (agi::Exception const& e) {
+    }
+    catch (agi::Exception const &e) {
 #ifdef _DEBUG
-		parent->Append(-1, to_wx(e.GetMessage()))->Enable(false);
+        parent->Append(-1, to_wx(e.GetMessage()))->Enable(false);
 #endif
-		LOG_W("menu/command/not_found") << "Skipping command " << command << ": " << e.GetMessage();
-	}
+        LOG_W("menu/command/not_found") << "Skipping command " << command << ": " << e.GetMessage();
+    }
 }
 
 /// Build the menu with the given name
 /// @param name Name of the menu
 /// @param c Project context to bind the menu to
-wxMenu *build_menu(std::string const& name, agi::Context *c, CommandManager *cm, wxMenu *menu) {
-	if (!menu) menu = new wxMenu;
-	for (auto const& item : get_menu(name))
-		process_menu_item(menu, c, item, cm);
-	return menu;
+wxMenu *build_menu(std::string const &name, agi::Context *c, CommandManager *cm, wxMenu *menu)
+{
+    if (!menu) menu = new wxMenu;
+    for (auto const &item : get_menu(name))
+        process_menu_item(menu, c, item, cm);
+    return menu;
 }
 
 class AutomationMenu final : public wxMenu {
-	agi::Context *c;
-	CommandManager *cm;
-	agi::signal::Connection global_slot;
-	agi::signal::Connection local_slot;
-	std::vector<wxMenuItem *> all_items;
-
-	struct WorkItem {
-		std::string displayname;
-		cmd::Command *command;
-		std::vector<WorkItem> subitems;
-
-		WorkItem(std::string const &displayname, cmd::Command *command = nullptr)
-		: displayname(displayname), command(command) { }
-
-		WorkItem *FindOrMakeSubitem(std::string const &name) {
-			auto sub = std::find_if(subitems.begin(), subitems.end(), [&](WorkItem const &item) { return item.displayname == name; });
-			if (sub != subitems.end()) return &*sub;
-
-			subitems.emplace_back(name);
-			return &subitems.back();
-		}
-
-		void Sort() {
-			if (command) return;
-			for (auto &sub : subitems)
-				sub.Sort();
-			auto comp = boost::locale::comparator<std::string::value_type>();
-			std::sort(subitems.begin(), subitems.end(), [&](WorkItem const &a, WorkItem const &b){
-				return comp(a.displayname, b.displayname);
-			});
-		}
-
-		void GenerateMenu(wxMenu *parent, AutomationMenu *am) {
-			for (auto item : subitems) {
-				if (item.command) {
-					am->cm->AddCommand(item.command, parent, item.displayname);
-					am->all_items.push_back(parent->GetMenuItems().back());
-				}
-				else {
-					auto submenu = new wxMenu;
-					parent->AppendSubMenu(submenu, to_wx(item.displayname));
-					item.GenerateMenu(submenu, am);
-				}
-			}
-		}
-	};
-
-	void Regenerate() {
-		for (auto item : all_items)
-			cm->Remove(item);
-
-		wxMenuItemList &items = GetMenuItems();
-		// Remove everything but automation manager and the separator
-		for (size_t i = items.size() - 1; i >= 2; --i)
-			Delete(items[i]);
-
-		auto macros = config::global_scripts->GetMacros();
-		boost::push_back(macros, c->local_scripts->GetMacros());
-		if (macros.empty()) {
-			Append(-1, _("No Automation macros loaded"))->Enable(false);
-			return;
-		}
-
-		WorkItem top("");
-		for (auto macro : macros) {
-			const auto name = from_wx(macro->StrMenu(c));
-			WorkItem *parent = &top;
-			for (auto section : agi::Split(name, wxS('/'))) {
-				std::string sectionname(section.begin(), section.end());
-
-				if (section.end() == name.end()) {
-					parent->subitems.emplace_back(sectionname, macro);
-				}
-				else {
-					parent = parent->FindOrMakeSubitem(sectionname);
-				}
-			}
-		}
-		top.Sort();
-		top.GenerateMenu(this, this);
-	}
+    agi::Context *c;
+    CommandManager *cm;
+    agi::signal::Connection global_slot;
+    agi::signal::Connection local_slot;
+    std::vector<wxMenuItem *> all_items;
+
+    struct WorkItem {
+        std::string displayname;
+        cmd::Command *command;
+        std::vector<WorkItem> subitems;
+
+        WorkItem(std::string const &displayname, cmd::Command *command = nullptr)
+            : displayname(displayname), command(command) { }
+
+        WorkItem *FindOrMakeSubitem(std::string const &name) {
+            auto sub = std::find_if(subitems.begin(), subitems.end(), [&](WorkItem const & item) { return item.displayname == name; });
+            if (sub != subitems.end()) return &*sub;
+
+            subitems.emplace_back(name);
+            return &subitems.back();
+        }
+
+        void Sort() {
+            if (command) return;
+            for (auto &sub : subitems)
+                sub.Sort();
+            auto comp = boost::locale::comparator<std::string::value_type>();
+            std::sort(subitems.begin(), subitems.end(), [&](WorkItem const & a, WorkItem const & b) {
+                return comp(a.displayname, b.displayname);
+            });
+        }
+
+        void GenerateMenu(wxMenu *parent, AutomationMenu *am) {
+            for (auto item : subitems) {
+                if (item.command) {
+                    am->cm->AddCommand(item.command, parent, item.displayname);
+                    am->all_items.push_back(parent->GetMenuItems().back());
+                }
+                else {
+                    auto submenu = new wxMenu;
+                    parent->AppendSubMenu(submenu, to_wx(item.displayname));
+                    item.GenerateMenu(submenu, am);
+                }
+            }
+        }
+    };
+
+    void Regenerate() {
+        for (auto item : all_items)
+            cm->Remove(item);
+
+        wxMenuItemList &items = GetMenuItems();
+        // Remove everything but automation manager and the separator
+        for (size_t i = items.size() - 1; i >= 2; --i)
+            Delete(items[i]);
+
+        auto macros = config::global_scripts->GetMacros();
+        boost::push_back(macros, c->local_scripts->GetMacros());
+        if (macros.empty()) {
+            Append(-1, _("No Automation macros loaded"))->Enable(false);
+            return;
+        }
+
+        WorkItem top("");
+        for (auto macro : macros) {
+            const auto name = from_wx(macro->StrMenu(c));
+            WorkItem *parent = &top;
+            for (auto section : agi::Split(name, wxS('/'))) {
+                std::string sectionname(section.begin(), section.end());
+
+                if (section.end() == name.end()) {
+                    parent->subitems.emplace_back(sectionname, macro);
+                }
+                else {
+                    parent = parent->FindOrMakeSubitem(sectionname);
+                }
+            }
+        }
+        top.Sort();
+        top.GenerateMenu(this, this);
+    }
 public:
-	AutomationMenu(agi::Context *c, CommandManager *cm)
-	: c(c)
-	, cm(cm)
-	, global_slot(config::global_scripts->AddScriptChangeListener(&AutomationMenu::Regenerate, this))
-	, local_slot(c->local_scripts->AddScriptChangeListener(&AutomationMenu::Regenerate, this))
-	{
-		cm->AddCommand(cmd::get("am/meta"), this);
-		AppendSeparator();
-		Regenerate();
-	}
+    AutomationMenu(agi::Context *c, CommandManager *cm)
+        : c(c)
+        , cm(cm)
+        , global_slot(config::global_scripts->AddScriptChangeListener(&AutomationMenu::Regenerate, this))
+        , local_slot(c->local_scripts->AddScriptChangeListener(&AutomationMenu::Regenerate, this)) {
+        cm->AddCommand(cmd::get("am/meta"), this);
+        AppendSeparator();
+        Regenerate();
+    }
 };
 }
 
 namespace menu {
-	void GetMenuBar(std::string const& name, wxFrame *window, agi::Context *c) {
+void GetMenuBar(std::string const &name, wxFrame *window, agi::Context *c)
+{
 #ifdef __WXMAC__
-		auto bind_events = [&](CommandMenuBar *menu) {
-			window->Bind(wxEVT_ACTIVATE, [=](wxActivateEvent&) { menu->cm.SetContext(c); });
-			window->Bind(wxEVT_DESTROY, [=](wxWindowDestroyEvent&) {
-				if (!osx::activate_top_window_other_than(window))
-					menu->cm.SetContext(nullptr);
-			});
-		};
-
-		if (wxMenuBar *menu = wxMenuBar::MacGetCommonMenuBar()) {
-			bind_events(static_cast<CommandMenuBar *>(menu));
-			return;
-		}
+    auto bind_events = [&](CommandMenuBar * menu) {
+        window->Bind(wxEVT_ACTIVATE, [ = ](wxActivateEvent &) { menu->cm.SetContext(c); });
+        window->Bind(wxEVT_DESTROY, [ = ](wxWindowDestroyEvent &) {
+            if (!osx::activate_top_window_other_than(window))
+                menu->cm.SetContext(nullptr);
+        });
+    };
+
+    if (wxMenuBar *menu = wxMenuBar::MacGetCommonMenuBar()) {
+        bind_events(static_cast<CommandMenuBar *>(menu));
+        return;
+    }
 #endif
 
-		auto menu = agi::make_unique<CommandMenuBar>(c);
-		for (auto const& item : get_menu(name)) {
-			std::string submenu, disp;
-			read_entry(item, "submenu", &submenu);
-			read_entry(item, "text", &disp);
-			if (!submenu.empty()) {
-				menu->Append(build_menu(submenu, c, &menu->cm), _(to_wx(disp)));
-			}
-			else {
-				read_entry(item, "special", &submenu);
-				if (submenu == "automation")
-					menu->Append(new AutomationMenu(c, &menu->cm), _(to_wx(disp)));
-			}
-		}
+    auto menu = agi::make_unique<CommandMenuBar>(c);
+    for (auto const &item : get_menu(name)) {
+        std::string submenu, disp;
+        read_entry(item, "submenu", &submenu);
+        read_entry(item, "text", &disp);
+        if (!submenu.empty()) {
+            menu->Append(build_menu(submenu, c, &menu->cm), _(to_wx(disp)));
+        }
+        else {
+            read_entry(item, "special", &submenu);
+            if (submenu == "automation")
+                menu->Append(new AutomationMenu(c, &menu->cm), _(to_wx(disp)));
+        }
+    }
 
 #ifdef __WXMAC__
-		menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
-		menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
+    menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
+    menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
 #else
-		window->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
-		window->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
+    window->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
+    window->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
 #endif
 
 #ifdef __WXMAC__
-		bind_events(menu.get());
-		wxMenuBar::MacSetCommonMenuBar(menu.get());
+    bind_events(menu.get());
+    wxMenuBar::MacSetCommonMenuBar(menu.get());
 #else
-		window->SetMenuBar(menu.get());
+    window->SetMenuBar(menu.get());
 #endif
 
-		menu.release();
-	}
-
-	std::unique_ptr<wxMenu> GetMenu(std::string const& name, agi::Context *c) {
-		auto menu = agi::make_unique<CommandMenu>(c);
-		build_menu(name, c, &menu->cm, menu.get());
-		menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
-		menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
-		return std::unique_ptr<wxMenu>(menu.release());
-	}
-
-	void OpenPopupMenu(wxMenu *menu, wxWindow *parent_window) {
-		wxMenuEvent evt;
-		evt.SetEventType(wxEVT_MENU_OPEN);
-		menu->ProcessEvent(evt);
-		parent_window->PopupMenu(menu);
-	}
+    menu.release();
+}
+
+std::unique_ptr<wxMenu> GetMenu(std::string const &name, agi::Context *c)
+{
+    auto menu = agi::make_unique<CommandMenu>(c);
+    build_menu(name, c, &menu->cm, menu.get());
+    menu->Bind(wxEVT_MENU_OPEN, &CommandManager::OnMenuOpen, &menu->cm);
+    menu->Bind(wxEVT_MENU, &CommandManager::OnMenuClick, &menu->cm);
+    return std::unique_ptr<wxMenu>(menu.release());
+}
+
+void OpenPopupMenu(wxMenu *menu, wxWindow *parent_window)
+{
+    wxMenuEvent evt;
+    evt.SetEventType(wxEVT_MENU_OPEN);
+    menu->ProcessEvent(evt);
+    parent_window->PopupMenu(menu);
+}
 }
diff --git a/src/mkv_wrap.cpp b/src/mkv_wrap.cpp
index 2564d902cf03d3c8b6cfb155cb8f940e318973dd..acb4be0e7c9732b0be224ea1f3ad2bdc0c26c282 100644
--- a/src/mkv_wrap.cpp
+++ b/src/mkv_wrap.cpp
@@ -56,224 +56,227 @@
 #include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
 
 struct MkvStdIO final : InputStream {
-	agi::read_file_mapping file;
-	std::string error;
-
-	static int Read(InputStream *st, uint64_t pos, void *buffer, int count) {
-		auto *self = static_cast<MkvStdIO*>(st);
-		if (pos >= self->file.size())
-			return 0;
-
-		auto remaining = self->file.size() - pos;
-		if (remaining < INT_MAX)
-			count = std::min(static_cast<int>(remaining), count);
-
-		try {
-			memcpy(buffer, self->file.read(pos, count), count);
-		}
-		catch (agi::Exception const& e) {
-			self->error = e.GetMessage();
-			return -1;
-		}
-
-		return count;
-	}
-
-	static int64_t Scan(InputStream *st, uint64_t start, unsigned signature) {
-		auto *self = static_cast<MkvStdIO*>(st);
-		try {
-			unsigned cmp = 0;
-			for (auto i : boost::irange(start, self->file.size())) {
-				int c = *self->file.read(i, 1);
-				cmp = ((cmp << 8) | c) & 0xffffffff;
-				if (cmp == signature)
-					return i - 4;
-			}
-		}
-		catch (agi::Exception const& e) {
-			self->error = e.GetMessage();
-		}
-
-		return -1;
-	}
-
-	static int64_t Size(InputStream *st) {
-		return static_cast<MkvStdIO*>(st)->file.size();
-	}
-
-	MkvStdIO(agi::fs::path const& filename) : file(filename) {
-		read = &MkvStdIO::Read;
-		scan = &MkvStdIO::Scan;
-		getcachesize = [](InputStream *) -> unsigned int { return 16 * 1024 * 1024; };
-		geterror = [](InputStream *st) -> const char * { return ((MkvStdIO *)st)->error.c_str(); };
-		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 *, uint64_t, uint64_t) { return 1; };
-		getfilesize = &MkvStdIO::Size;
-	}
+    agi::read_file_mapping file;
+    std::string error;
+
+    static int Read(InputStream *st, uint64_t pos, void *buffer, int count) {
+        auto *self = static_cast<MkvStdIO *>(st);
+        if (pos >= self->file.size())
+            return 0;
+
+        auto remaining = self->file.size() - pos;
+        if (remaining < INT_MAX)
+            count = std::min(static_cast<int>(remaining), count);
+
+        try {
+            memcpy(buffer, self->file.read(pos, count), count);
+        }
+        catch (agi::Exception const &e) {
+            self->error = e.GetMessage();
+            return -1;
+        }
+
+        return count;
+    }
+
+    static int64_t Scan(InputStream *st, uint64_t start, unsigned signature) {
+        auto *self = static_cast<MkvStdIO *>(st);
+        try {
+            unsigned cmp = 0;
+            for (auto i : boost::irange(start, self->file.size())) {
+                int c = *self->file.read(i, 1);
+                cmp = ((cmp << 8) | c) & 0xffffffff;
+                if (cmp == signature)
+                    return i - 4;
+            }
+        }
+        catch (agi::Exception const &e) {
+            self->error = e.GetMessage();
+        }
+
+        return -1;
+    }
+
+    static int64_t Size(InputStream *st) {
+        return static_cast<MkvStdIO *>(st)->file.size();
+    }
+
+    MkvStdIO(agi::fs::path const &filename) : file(filename) {
+        read = &MkvStdIO::Read;
+        scan = &MkvStdIO::Scan;
+        getcachesize = [](InputStream *) -> unsigned int { return 16 * 1024 * 1024; };
+        geterror = [](InputStream * st) -> const char * { return ((MkvStdIO *)st)->error.c_str(); };
+        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 *, uint64_t, uint64_t) { return 1; };
+        getfilesize = &MkvStdIO::Size;
+    }
 };
 
-static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser) {
-	std::vector<std::pair<int, std::string>> subList;
-
-	// Load blocks
-	uint64_t startTime, endTime, filePos;
-	unsigned int rt, frameSize, frameFlags;
-
-	while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) {
-		if (ps->IsCancelled()) return;
-		if (frameSize == 0) continue;
-
-		const auto readBuf = input->file.read(filePos, frameSize);
-		const auto readBufEnd = readBuf + frameSize;
-
-		// Get start and end times
-		int64_t timecodeScaleLow = 1000000;
-		agi::Time subStart = startTime / timecodeScaleLow;
-		agi::Time subEnd = endTime / timecodeScaleLow;
-
-		using str_range = boost::iterator_range<const char *>;
-
-		// Process SSA/ASS
-		if (!srt) {
-			auto first = std::find(readBuf, readBufEnd, ',');
-			if (first == readBufEnd) continue;
-			auto second = std::find(first + 1, readBufEnd, ',');
-			if (second == readBufEnd) continue;
-
-			subList.emplace_back(
-				boost::lexical_cast<int>(str_range(readBuf, first)),
-				agi::format("Dialogue: %d,%s,%s,%s"
-					, boost::lexical_cast<int>(str_range(first + 1, second))
-					, subStart.GetAssFormatted()
-					, subEnd.GetAssFormatted()
-					, str_range(second + 1, readBufEnd)));
-		}
-		// Process SRT
-		else {
-			auto line = agi::format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s"
-				, subStart.GetAssFormatted()
-				, subEnd.GetAssFormatted()
-				, str_range(readBuf, readBufEnd));
-			boost::replace_all(line, "\r\n", "\\N");
-			boost::replace_all(line, "\r", "\\N");
-			boost::replace_all(line, "\n", "\\N");
-
-			subList.emplace_back(subList.size(), std::move(line));
-		}
-
-		ps->SetProgress(startTime / timecodeScaleLow, totalTime);
-	}
-
-	// Insert into file
-	sort(begin(subList), end(subList));
-	for (auto order_value_pair : subList)
-		parser->AddLine(order_value_pair.second);
+static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO *input, bool srt, double totalTime, AssParser *parser)
+{
+    std::vector<std::pair<int, std::string>> subList;
+
+    // Load blocks
+    uint64_t startTime, endTime, filePos;
+    unsigned int rt, frameSize, frameFlags;
+
+    while (mkv_ReadFrame(file, 0, &rt, &startTime, &endTime, &filePos, &frameSize, &frameFlags) == 0) {
+        if (ps->IsCancelled()) return;
+        if (frameSize == 0) continue;
+
+        const auto readBuf = input->file.read(filePos, frameSize);
+        const auto readBufEnd = readBuf + frameSize;
+
+        // Get start and end times
+        int64_t timecodeScaleLow = 1000000;
+        agi::Time subStart = startTime / timecodeScaleLow;
+        agi::Time subEnd = endTime / timecodeScaleLow;
+
+        using str_range = boost::iterator_range<const char *>;
+
+        // Process SSA/ASS
+        if (!srt) {
+            auto first = std::find(readBuf, readBufEnd, ',');
+            if (first == readBufEnd) continue;
+            auto second = std::find(first + 1, readBufEnd, ',');
+            if (second == readBufEnd) continue;
+
+            subList.emplace_back(
+                boost::lexical_cast<int>(str_range(readBuf, first)),
+                agi::format("Dialogue: %d,%s,%s,%s"
+                            , boost::lexical_cast<int>(str_range(first + 1, second))
+                            , subStart.GetAssFormatted()
+                            , subEnd.GetAssFormatted()
+                            , str_range(second + 1, readBufEnd)));
+        }
+        // Process SRT
+        else {
+            auto line = agi::format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s"
+                                    , subStart.GetAssFormatted()
+                                    , subEnd.GetAssFormatted()
+                                    , str_range(readBuf, readBufEnd));
+            boost::replace_all(line, "\r\n", "\\N");
+            boost::replace_all(line, "\r", "\\N");
+            boost::replace_all(line, "\n", "\\N");
+
+            subList.emplace_back(subList.size(), std::move(line));
+        }
+
+        ps->SetProgress(startTime / timecodeScaleLow, totalTime);
+    }
+
+    // Insert into file
+    sort(begin(subList), end(subList));
+    for (auto order_value_pair : subList)
+        parser->AddLine(order_value_pair.second);
 }
 
-void MatroskaWrapper::GetSubtitles(agi::fs::path const& filename, AssFile *target) {
-	MkvStdIO input(filename);
-	char err[2048];
-	agi::scoped_holder<MatroskaFile*, decltype(&mkv_Close)> file(mkv_Open(&input, err, sizeof(err)), mkv_Close);
-	if (!file) throw MatroskaException(err);
-
-	// Get info
-	unsigned tracks = mkv_GetNumTracks(file);
-	std::vector<unsigned> tracksFound;
-	std::vector<std::string> tracksNames;
-
-	// Find tracks
-	for (auto track : boost::irange(0u, tracks)) {
-		auto trackInfo = mkv_GetTrackInfo(file, track);
-		if (trackInfo->Type != 0x11 || trackInfo->CompEnabled) continue;
-
-		// 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(agi::format("%d (%s %s)", track, CodecID, trackInfo->Language));
-			if (trackInfo->Name) {
-				tracksNames.back() += ": ";
-				tracksNames.back() += trackInfo->Name;
-			}
-		}
-	}
-
-	// No tracks found
-	if (tracksFound.empty())
-		throw MatroskaException("File has no recognised subtitle tracks.");
-
-	unsigned trackToRead;
-	// Only one track found
-	if (tracksFound.size() == 1)
-		trackToRead = tracksFound[0];
-	// Pick a track
-	else {
-		int choice = wxGetSingleChoiceIndex(_("Choose which track to read:"), _("Multiple subtitle tracks found"), to_wx(tracksNames));
-		if (choice == -1)
-			throw agi::UserCancelException("canceled");
-
-		trackToRead = tracksFound[choice];
-	}
-
-	// Picked track
-	mkv_SetTrackMask(file, ~(1 << trackToRead));
-	auto trackInfo = mkv_GetTrackInfo(file, trackToRead);
-	std::string CodecID(trackInfo->CodecID);
-	bool srt = CodecID == "S_TEXT/UTF8";
-	bool ssa = CodecID == "S_TEXT/SSA";
-
-	AssParser parser(target, !ssa);
-
-	// Read private data if it's ASS/SSA
-	if (!srt) {
-		// Read raw data
-		std::string priv((const char *)trackInfo->CodecPrivate, trackInfo->CodecPrivateSize);
-
-		// Load into file
-		boost::char_separator<char> sep("\r\n");
-		for (auto const& cur : boost::tokenizer<boost::char_separator<char>>(priv, sep))
-			parser.AddLine(cur);
-	}
-	// Load default if it's SRT
-	else
-		target->LoadDefault(false, OPT_GET("Subtitle Format/SRT/Default Style Catalog")->GetString());
-
-	parser.AddLine("[Events]");
-
-	// Read timecode scale
-	auto segInfo = mkv_GetFileInfo(file);
-	int64_t timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
-
-	// Progress bar
-	auto totalTime = double(segInfo->Duration) / timecodeScale;
-	DialogProgress progress(nullptr, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
-	progress.Run([&](agi::ProgressSink *ps) { read_subtitles(ps, file, &input, srt, totalTime, &parser); });
+void MatroskaWrapper::GetSubtitles(agi::fs::path const &filename, AssFile *target)
+{
+    MkvStdIO input(filename);
+    char err[2048];
+    agi::scoped_holder<MatroskaFile *, decltype(&mkv_Close)> file(mkv_Open(&input, err, sizeof(err)), mkv_Close);
+    if (!file) throw MatroskaException(err);
+
+    // Get info
+    unsigned tracks = mkv_GetNumTracks(file);
+    std::vector<unsigned> tracksFound;
+    std::vector<std::string> tracksNames;
+
+    // Find tracks
+    for (auto track : boost::irange(0u, tracks)) {
+        auto trackInfo = mkv_GetTrackInfo(file, track);
+        if (trackInfo->Type != 0x11 || trackInfo->CompEnabled) continue;
+
+        // 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(agi::format("%d (%s %s)", track, CodecID, trackInfo->Language));
+            if (trackInfo->Name) {
+                tracksNames.back() += ": ";
+                tracksNames.back() += trackInfo->Name;
+            }
+        }
+    }
+
+    // No tracks found
+    if (tracksFound.empty())
+        throw MatroskaException("File has no recognised subtitle tracks.");
+
+    unsigned trackToRead;
+    // Only one track found
+    if (tracksFound.size() == 1)
+        trackToRead = tracksFound[0];
+    // Pick a track
+    else {
+        int choice = wxGetSingleChoiceIndex(_("Choose which track to read:"), _("Multiple subtitle tracks found"), to_wx(tracksNames));
+        if (choice == -1)
+            throw agi::UserCancelException("canceled");
+
+        trackToRead = tracksFound[choice];
+    }
+
+    // Picked track
+    mkv_SetTrackMask(file, ~(1 << trackToRead));
+    auto trackInfo = mkv_GetTrackInfo(file, trackToRead);
+    std::string CodecID(trackInfo->CodecID);
+    bool srt = CodecID == "S_TEXT/UTF8";
+    bool ssa = CodecID == "S_TEXT/SSA";
+
+    AssParser parser(target, !ssa);
+
+    // Read private data if it's ASS/SSA
+    if (!srt) {
+        // Read raw data
+        std::string priv((const char *)trackInfo->CodecPrivate, trackInfo->CodecPrivateSize);
+
+        // Load into file
+        boost::char_separator<char> sep("\r\n");
+        for (auto const &cur : boost::tokenizer<boost::char_separator<char>>(priv, sep))
+            parser.AddLine(cur);
+    }
+    // Load default if it's SRT
+    else
+        target->LoadDefault(false, OPT_GET("Subtitle Format/SRT/Default Style Catalog")->GetString());
+
+    parser.AddLine("[Events]");
+
+    // Read timecode scale
+    auto segInfo = mkv_GetFileInfo(file);
+    int64_t timecodeScale = mkv_TruncFloat(trackInfo->TimecodeScale) * segInfo->TimecodeScale;
+
+    // Progress bar
+    auto totalTime = double(segInfo->Duration) / timecodeScale;
+    DialogProgress progress(nullptr, _("Parsing Matroska"), _("Reading subtitles from Matroska file."));
+    progress.Run([&](agi::ProgressSink * ps) { read_subtitles(ps, file, &input, srt, totalTime, &parser); });
 }
 
-bool MatroskaWrapper::HasSubtitles(agi::fs::path const& filename) {
-	char err[2048];
-	try {
-		MkvStdIO input(filename);
-		agi::scoped_holder<MatroskaFile*, decltype(&mkv_Close)> file(mkv_Open(&input, err, sizeof(err)), mkv_Close);
-		if (!file) return false;
-
-		// Find tracks
-		auto tracks = mkv_GetNumTracks(file);
-		for (auto track : boost::irange(0u, tracks)) {
-			auto trackInfo = mkv_GetTrackInfo(file, track);
-
-			if (trackInfo->Type == 0x11 && !trackInfo->CompEnabled) {
-				std::string CodecID(trackInfo->CodecID);
-				if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8")
-					return true;
-			}
-		}
-	}
-	catch (...) {
-		// We don't care about why we couldn't read subtitles here
-	}
-
-	return false;
+bool MatroskaWrapper::HasSubtitles(agi::fs::path const &filename)
+{
+    char err[2048];
+    try {
+        MkvStdIO input(filename);
+        agi::scoped_holder<MatroskaFile *, decltype(&mkv_Close)> file(mkv_Open(&input, err, sizeof(err)), mkv_Close);
+        if (!file) return false;
+
+        // Find tracks
+        auto tracks = mkv_GetNumTracks(file);
+        for (auto track : boost::irange(0u, tracks)) {
+            auto trackInfo = mkv_GetTrackInfo(file, track);
+
+            if (trackInfo->Type == 0x11 && !trackInfo->CompEnabled) {
+                std::string CodecID(trackInfo->CodecID);
+                if (CodecID == "S_TEXT/SSA" || CodecID == "S_TEXT/ASS" || CodecID == "S_TEXT/UTF8")
+                    return true;
+            }
+        }
+    }
+    catch (...) {
+        // We don't care about why we couldn't read subtitles here
+    }
+
+    return false;
 }
diff --git a/src/mkv_wrap.h b/src/mkv_wrap.h
index 88d21f6932f6d169d770051185552bae13287e98..cff748e5f52fd17ef907b2ef37de28c06b8d2c6c 100644
--- a/src/mkv_wrap.h
+++ b/src/mkv_wrap.h
@@ -28,8 +28,8 @@ class AssFile;
 
 class MatroskaWrapper {
 public:
-	/// Check if the file is a matroska file with at least one subtitle track
-	static bool HasSubtitles(agi::fs::path const& filename);
-	/// Load subtitles from a matroska file
-	static void GetSubtitles(agi::fs::path const& filename, AssFile *target);
+    /// Check if the file is a matroska file with at least one subtitle track
+    static bool HasSubtitles(agi::fs::path const &filename);
+    /// Load subtitles from a matroska file
+    static void GetSubtitles(agi::fs::path const &filename, AssFile *target);
 };
diff --git a/src/options.h b/src/options.h
index 77e6676a2c0eea4772918251a094c4bb8e6ae462..679bf959a6b6affdb500c28353594d0458a3ba9a 100644
--- a/src/options.h
+++ b/src/options.h
@@ -25,10 +25,10 @@ namespace Automation4 { class AutoloadScriptManager; }
 
 /// 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;
-	extern Automation4::AutoloadScriptManager *global_scripts;
+extern agi::Options *opt;    ///< Options
+extern agi::MRUManager *mru; ///< Most Recently Used
+extern agi::Path *path;
+extern Automation4::AutoloadScriptManager *global_scripts;
 }
 
 /// Macro to get OptionValue object
diff --git a/src/pen.cpp b/src/pen.cpp
index 0d92cf4bbfe0fc11bbac11ff678c7a89e479faf4..40a509838594813bc381b270a0f3351ff75007ae 100644
--- a/src/pen.cpp
+++ b/src/pen.cpp
@@ -19,24 +19,26 @@
 #include "compat.h"
 #include "options.h"
 
-void Pen::OnColourChanged(agi::OptionValue const& opt) {
-	impl.SetColour(to_wx(opt.GetColor()));
+void Pen::OnColourChanged(agi::OptionValue const &opt)
+{
+    impl.SetColour(to_wx(opt.GetColor()));
 }
 
-void Pen::OnWidthChanged(agi::OptionValue const& opt) {
-	impl.SetWidth(opt.GetInt());
+void Pen::OnWidthChanged(agi::OptionValue const &opt)
+{
+    impl.SetWidth(opt.GetInt());
 }
 
 Pen::Pen(const char *colour_opt, const char *width_opt, wxPenStyle style)
-: impl(to_wx(OPT_GET(colour_opt)->GetColor()), OPT_GET(width_opt)->GetInt(), style)
-, colour_con(OPT_SUB(colour_opt, &Pen::OnColourChanged, this))
-, width_con(OPT_SUB(width_opt, &Pen::OnWidthChanged, this))
+    : impl(to_wx(OPT_GET(colour_opt)->GetColor()), OPT_GET(width_opt)->GetInt(), style)
+    , colour_con(OPT_SUB(colour_opt, &Pen::OnColourChanged, this))
+    , width_con(OPT_SUB(width_opt, &Pen::OnWidthChanged, this))
 {
 }
 
 Pen::Pen(const char *colour_opt, int width, wxPenStyle style)
-: impl(to_wx(OPT_GET(colour_opt)->GetColor()), width, style)
-, colour_con(OPT_SUB(colour_opt, &Pen::OnColourChanged, this))
+    : impl(to_wx(OPT_GET(colour_opt)->GetColor()), width, style)
+    , colour_con(OPT_SUB(colour_opt, &Pen::OnColourChanged, this))
 {
 }
 
diff --git a/src/pen.h b/src/pen.h
index 44c3018039218e193897ea099305e30ca0c6f197..c829f7923859aa33f12ba02f596be0d3a355f48f 100644
--- a/src/pen.h
+++ b/src/pen.h
@@ -24,28 +24,28 @@ namespace agi { class OptionValue; }
 /// @brief A simple wrapper around wxPen to bind the colour and width to the
 /// value of an option
 class Pen {
-	wxPen impl;
-	agi::signal::Connection colour_con;
-	agi::signal::Connection width_con;
+    wxPen impl;
+    agi::signal::Connection colour_con;
+    agi::signal::Connection width_con;
 
-	void OnColourChanged(agi::OptionValue const& opt);
-	void OnWidthChanged(agi::OptionValue const& opt);
+    void OnColourChanged(agi::OptionValue const &opt);
+    void OnWidthChanged(agi::OptionValue const &opt);
 
 public:
-	/// Constructor
-	/// @param colour_opt Option name to get the colour from
-	/// @param width_opt Option name to get the width from
-	/// @param style Pen style
-	Pen(const char *colour_opt, const char *width_opt, wxPenStyle style = wxPENSTYLE_SOLID);
-
-	/// Constructor
-	/// @param colour_opt Option name to get the colour from
-	/// @param width Pen width
-	/// @param style Pen style
-	Pen(const char *colour_opt, int width = 1, wxPenStyle style = wxPENSTYLE_SOLID);
-
-	/// Implicit conversion to wxPen
-	operator wxPen const&() const { return impl; }
-
-	~Pen();
+    /// Constructor
+    /// @param colour_opt Option name to get the colour from
+    /// @param width_opt Option name to get the width from
+    /// @param style Pen style
+    Pen(const char *colour_opt, const char *width_opt, wxPenStyle style = wxPENSTYLE_SOLID);
+
+    /// Constructor
+    /// @param colour_opt Option name to get the colour from
+    /// @param width Pen width
+    /// @param style Pen style
+    Pen(const char *colour_opt, int width = 1, wxPenStyle style = wxPENSTYLE_SOLID);
+
+    /// Implicit conversion to wxPen
+    operator wxPen const &() const { return impl; }
+
+    ~Pen();
 };
diff --git a/src/persist_location.cpp b/src/persist_location.cpp
index 8b1498ced3f7c56ea4784904368d6de0b5382d10..89a8fb18aac2b35f933fe4c5cb9ac0506ad4bb8e 100644
--- a/src/persist_location.cpp
+++ b/src/persist_location.cpp
@@ -26,71 +26,73 @@
 #include <wx/display.h>
 
 PersistLocation::PersistLocation(wxDialog *dialog, std::string options_prefix, bool size_too)
-: x_opt(OPT_SET(options_prefix + "/Last/X"))
-, y_opt(OPT_SET(options_prefix + "/Last/Y"))
-, w_opt(size_too ? OPT_SET(options_prefix + "/Last/Width") : nullptr)
-, h_opt(size_too ? OPT_SET(options_prefix + "/Last/Height") : nullptr)
-, maximize_opt(OPT_SET(options_prefix + "/Maximized"))
-, dialog(dialog)
+    : x_opt(OPT_SET(options_prefix + "/Last/X"))
+    , y_opt(OPT_SET(options_prefix + "/Last/Y"))
+    , w_opt(size_too ? OPT_SET(options_prefix + "/Last/Width") : nullptr)
+    , h_opt(size_too ? OPT_SET(options_prefix + "/Last/Height") : nullptr)
+    , maximize_opt(OPT_SET(options_prefix + "/Maximized"))
+    , dialog(dialog)
 {
-	int x = x_opt->GetInt();
-	int y = y_opt->GetInt();
-	if (x == -1 && y == -1)
-		dialog->CenterOnParent();
-	else {
-		// First move to the saved place so that it ends up on the right monitor
-		dialog->Move(x, y);
-
-		if (size_too && w_opt->GetInt() > 0 && h_opt->GetInt() > 0)
-			dialog->SetSize(w_opt->GetInt(), h_opt->GetInt());
-
-		int display_index = wxDisplay::GetFromWindow(dialog);
-
-		// If it's moved offscreen center on the parent and try again
-		if (display_index == wxNOT_FOUND) {
-			dialog->CenterOnParent();
-			display_index = wxDisplay::GetFromWindow(dialog);
-		}
-
-		// If it's still offscreen just give up
-		if (display_index == wxNOT_FOUND) return;
-
-		wxRect display_area = wxDisplay(display_index).GetClientArea();
-		wxSize dialog_size = dialog->GetSize();
-
-		// Ensure that the top-left corner is onscreen
-		if (x < display_area.x) x = display_area.x;
-		if (y < display_area.y) y = display_area.y;
-
-		// Ensure that the bottom-right corner is onscreen as long as doing so
-		// wouldn't force the top-left corner offscreen
-		if (x + dialog_size.x > display_area.GetRight())
-			x = std::max(display_area.x, display_area.GetRight() - dialog_size.x);
-		if (y + dialog_size.y > display_area.GetBottom())
-			y = std::max(display_area.y, display_area.GetBottom() - dialog_size.y);
-
-		dialog->Move(x, y);
-	}
-
-	dialog->Bind(wxEVT_MOVE, &PersistLocation::OnMove, this);
-
-	dialog->Bind(wxEVT_SIZE, &PersistLocation::OnSize, this);
-	if ((dialog->GetWindowStyle() & wxMAXIMIZE_BOX) && maximize_opt->GetBool())
-		dialog->Maximize();
+    int x = x_opt->GetInt();
+    int y = y_opt->GetInt();
+    if (x == -1 && y == -1)
+        dialog->CenterOnParent();
+    else {
+        // First move to the saved place so that it ends up on the right monitor
+        dialog->Move(x, y);
+
+        if (size_too && w_opt->GetInt() > 0 && h_opt->GetInt() > 0)
+            dialog->SetSize(w_opt->GetInt(), h_opt->GetInt());
+
+        int display_index = wxDisplay::GetFromWindow(dialog);
+
+        // If it's moved offscreen center on the parent and try again
+        if (display_index == wxNOT_FOUND) {
+            dialog->CenterOnParent();
+            display_index = wxDisplay::GetFromWindow(dialog);
+        }
+
+        // If it's still offscreen just give up
+        if (display_index == wxNOT_FOUND) return;
+
+        wxRect display_area = wxDisplay(display_index).GetClientArea();
+        wxSize dialog_size = dialog->GetSize();
+
+        // Ensure that the top-left corner is onscreen
+        if (x < display_area.x) x = display_area.x;
+        if (y < display_area.y) y = display_area.y;
+
+        // Ensure that the bottom-right corner is onscreen as long as doing so
+        // wouldn't force the top-left corner offscreen
+        if (x + dialog_size.x > display_area.GetRight())
+            x = std::max(display_area.x, display_area.GetRight() - dialog_size.x);
+        if (y + dialog_size.y > display_area.GetBottom())
+            y = std::max(display_area.y, display_area.GetBottom() - dialog_size.y);
+
+        dialog->Move(x, y);
+    }
+
+    dialog->Bind(wxEVT_MOVE, &PersistLocation::OnMove, this);
+
+    dialog->Bind(wxEVT_SIZE, &PersistLocation::OnSize, this);
+    if ((dialog->GetWindowStyle() & wxMAXIMIZE_BOX) && maximize_opt->GetBool())
+        dialog->Maximize();
 }
 
-void PersistLocation::OnMove(wxMoveEvent &e) {
-	wxPoint pos = dialog->GetPosition();
-	x_opt->SetInt(pos.x);
-	y_opt->SetInt(pos.y);
-	e.Skip();
+void PersistLocation::OnMove(wxMoveEvent &e)
+{
+    wxPoint pos = dialog->GetPosition();
+    x_opt->SetInt(pos.x);
+    y_opt->SetInt(pos.y);
+    e.Skip();
 }
 
-void PersistLocation::OnSize(wxSizeEvent &e) {
-	maximize_opt->SetBool(dialog->IsMaximized());
-	if (w_opt) {
-		w_opt->SetInt(dialog->GetSize().GetWidth());
-		h_opt->SetInt(dialog->GetSize().GetHeight());
-	}
-	e.Skip();
+void PersistLocation::OnSize(wxSizeEvent &e)
+{
+    maximize_opt->SetBool(dialog->IsMaximized());
+    if (w_opt) {
+        w_opt->SetInt(dialog->GetSize().GetWidth());
+        h_opt->SetInt(dialog->GetSize().GetHeight());
+    }
+    e.Skip();
 }
diff --git a/src/persist_location.h b/src/persist_location.h
index cb71e57ddbf7dac2c172b29734612c2c534407f7..1c8c2e2e00d74c14ab2f9a2ca73d08b16cbc7613 100644
--- a/src/persist_location.h
+++ b/src/persist_location.h
@@ -33,20 +33,20 @@ class wxSizeEvent;
 /// recreated in the future. This class should always have lifetime equal to
 /// the associated dialog, as it does not unbind its events.
 class PersistLocation {
-	agi::OptionValue *x_opt;
-	agi::OptionValue *y_opt;
-	agi::OptionValue *w_opt;
-	agi::OptionValue *h_opt;
-	agi::OptionValue *maximize_opt;
-	wxDialog *dialog;
+    agi::OptionValue *x_opt;
+    agi::OptionValue *y_opt;
+    agi::OptionValue *w_opt;
+    agi::OptionValue *h_opt;
+    agi::OptionValue *maximize_opt;
+    wxDialog *dialog;
 
-	void OnMove(wxMoveEvent&);
-	void OnSize(wxSizeEvent&);
+    void OnMove(wxMoveEvent &);
+    void OnSize(wxSizeEvent &);
 
 public:
-	/// Persist the location of a dialog
-	/// @param dialog The dialog to save and restore the position of
-	/// @param options_prefix Prefix for the options names to store the location
-	/// @param size_too Save and restore the size in addition to position
-	PersistLocation(wxDialog *dialog, std::string options_prefix, bool size_too = false);
+    /// Persist the location of a dialog
+    /// @param dialog The dialog to save and restore the position of
+    /// @param options_prefix Prefix for the options names to store the location
+    /// @param size_too Save and restore the size in addition to position
+    PersistLocation(wxDialog *dialog, std::string options_prefix, bool size_too = false);
 };
diff --git a/src/placeholder_ctrl.h b/src/placeholder_ctrl.h
index 9e4bd372114aeafdde72b9ee97065c51e68766e4..a78c1a6b20fef7d830d0b532f8c5818441f5cb31 100644
--- a/src/placeholder_ctrl.h
+++ b/src/placeholder_ctrl.h
@@ -17,7 +17,7 @@
 #include <wx/settings.h>
 
 // Defined in osx_utils.mm
-void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
+void SetPlaceholderText(wxWindow *window, wxString const &placeholder);
 
 /// @class Placeholder
 /// @brief A wrapper around a control to add placeholder text
@@ -28,90 +28,89 @@ void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
 /// when the control loses focus and the value is empty
 template<class BaseCtrl>
 class Placeholder final : public BaseCtrl {
-	wxString placeholder; ///< Placeholder string
-	bool is_placeholder;  ///< Should the value be cleared on focus?
+    wxString placeholder; ///< Placeholder string
+    bool is_placeholder;  ///< Should the value be cleared on focus?
 
-	/// Wrapper around Create to make it possible to override it for specific
-	/// base classes
-	inline void Create(wxWindow *parent, wxSize const& size, long style) {
-		BaseCtrl::Create(parent, -1, placeholder, wxDefaultPosition, size, style);
-	}
+    /// Wrapper around Create to make it possible to override it for specific
+    /// base classes
+    inline void Create(wxWindow *parent, wxSize const &size, long style) {
+        BaseCtrl::Create(parent, -1, placeholder, wxDefaultPosition, size, style);
+    }
 
 #ifndef __WXOSX__
-	/// Focus gained event handler
-	void OnSetFocus(wxFocusEvent& evt) {
-		evt.Skip();
+    /// Focus gained event handler
+    void OnSetFocus(wxFocusEvent &evt) {
+        evt.Skip();
 
-		if (is_placeholder) {
-			BaseCtrl::ChangeValue("");
-			BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
-		}
-	}
+        if (is_placeholder) {
+            BaseCtrl::ChangeValue("");
+            BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+        }
+    }
 
-	/// Focus lost event handler
-	void OnKillFocus(wxFocusEvent& evt) {
-		evt.Skip();
-		ChangeValue(BaseCtrl::GetValue());
-	}
+    /// Focus lost event handler
+    void OnKillFocus(wxFocusEvent &evt) {
+        evt.Skip();
+        ChangeValue(BaseCtrl::GetValue());
+    }
 
 public:
-	/// Constructor
-	/// @param parent Parent window
-	/// @param placeholder Placeholder string
-	/// @param size Control size
-	/// @param style Style flags to pass to the base control
-	/// @param tooltip Tooltip string
-	Placeholder(wxWindow *parent, wxString const& placeholder, wxSize const& size, long style, wxString const& tooltip)
-	: placeholder(placeholder)
-	, is_placeholder(true)
-	{
-		Create(parent, size, style);
-		BaseCtrl::SetToolTip(tooltip);
-		BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+    /// Constructor
+    /// @param parent Parent window
+    /// @param placeholder Placeholder string
+    /// @param size Control size
+    /// @param style Style flags to pass to the base control
+    /// @param tooltip Tooltip string
+    Placeholder(wxWindow *parent, wxString const &placeholder, wxSize const &size, long style, wxString const &tooltip)
+        : placeholder(placeholder)
+        , is_placeholder(true) {
+        Create(parent, size, style);
+        BaseCtrl::SetToolTip(tooltip);
+        BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
 
-		BaseCtrl::Bind(wxEVT_SET_FOCUS, &Placeholder::OnSetFocus, this);
-		BaseCtrl::Bind(wxEVT_KILL_FOCUS, &Placeholder::OnKillFocus, this);
-	}
+        BaseCtrl::Bind(wxEVT_SET_FOCUS, &Placeholder::OnSetFocus, this);
+        BaseCtrl::Bind(wxEVT_KILL_FOCUS, &Placeholder::OnKillFocus, this);
+    }
 
-	/// @brief Change the value of the control without triggering events
-	/// @param new_value New value of the control
-	///
-	/// If new_value is empty, the control will switch to placeholder mode
-	void ChangeValue(wxString new_value) {
-		if (new_value.empty() && !this->HasFocus()) {
-			is_placeholder = true;
-			new_value = placeholder;
-			BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
-		}
-		else {
-			is_placeholder = false;
-			BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
-		}
+    /// @brief Change the value of the control without triggering events
+    /// @param new_value New value of the control
+    ///
+    /// If new_value is empty, the control will switch to placeholder mode
+    void ChangeValue(wxString new_value) {
+        if (new_value.empty() && !this->HasFocus()) {
+            is_placeholder = true;
+            new_value = placeholder;
+            BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+        }
+        else {
+            is_placeholder = false;
+            BaseCtrl::SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+        }
 
-		// This check should be pointless, but wxGTK is awesome and generates
-		// change events in wxComboBox::ChangeValue
-		if (new_value != BaseCtrl::GetValue())
-			BaseCtrl::ChangeValue(new_value);
-	}
+        // This check should be pointless, but wxGTK is awesome and generates
+        // change events in wxComboBox::ChangeValue
+        if (new_value != BaseCtrl::GetValue())
+            BaseCtrl::ChangeValue(new_value);
+    }
 
-	/// Override GetValue to return empty when in placeholder mode rather than the placeholder text
-	wxString GetValue() const {
-		if (is_placeholder && !this->HasFocus())
-			return "";
-		return BaseCtrl::GetValue();
-	}
+    /// Override GetValue to return empty when in placeholder mode rather than the placeholder text
+    wxString GetValue() const {
+        if (is_placeholder && !this->HasFocus())
+            return "";
+        return BaseCtrl::GetValue();
+    }
 #else
 public:
-	Placeholder(wxWindow *parent, wxString const& placeholder, wxSize const& size, long style, wxString const& tooltip)
-	: placeholder(placeholder)
-	{
-		Create(parent, size, style);
-		BaseCtrl::SetToolTip(tooltip);
-		SetPlaceholderText(this, placeholder);
-	}
+    Placeholder(wxWindow *parent, wxString const &placeholder, wxSize const &size, long style, wxString const &tooltip)
+        : placeholder(placeholder) {
+        Create(parent, size, style);
+        BaseCtrl::SetToolTip(tooltip);
+        SetPlaceholderText(this, placeholder);
+    }
 #endif
 };
 
-template<> inline void Placeholder<wxComboBox>::Create(wxWindow *parent, wxSize const& size, long style) {
-	wxComboBox::Create(parent, -1, "", wxDefaultPosition, size, 0, nullptr, style);
+template<> inline void Placeholder<wxComboBox>::Create(wxWindow *parent, wxSize const &size, long style)
+{
+    wxComboBox::Create(parent, -1, "", wxDefaultPosition, size, 0, nullptr, style);
 }
diff --git a/src/preferences.cpp b/src/preferences.cpp
index 7850af5f9bff001993d9f84b66eaebbfaa9e8446..84ed2d6c97c1e4b18eabe5f06cd5bc9d9a74e610 100644
--- a/src/preferences.cpp
+++ b/src/preferences.cpp
@@ -58,692 +58,713 @@
 
 namespace {
 /// General preferences page
-void General(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("General"));
-
-	auto general = p->PageSizer(_("General"));
-	p->OptionAdd(general, _("Check for updates on startup"), "App/Auto/Check For Updates");
-	p->OptionAdd(general, _("Show main toolbar"), "App/Show Toolbar");
-	p->OptionAdd(general, _("Save UI state in subtitles files"), "App/Save UI State");
-	p->CellSkip(general);
-
-	p->OptionAdd(general, _("Toolbar Icon Size"), "App/Toolbar Icon Size");
-	wxString autoload_modes[] = { _("Never"), _("Always"), _("Ask") };
-	wxArrayString autoload_modes_arr(3, autoload_modes);
-	p->OptionChoice(general, _("Automatically load linked files"), autoload_modes_arr, "App/Auto/Load Linked Files");
-	p->OptionAdd(general, _("Undo Levels"), "Limits/Undo Levels", 2, 10000);
-
-	auto recent = p->PageSizer(_("Recently Used Lists"));
-	p->OptionAdd(recent, _("Files"), "Limits/MRU", 0, 16);
-	p->OptionAdd(recent, _("Find/Replace"), "Limits/Find Replace");
-
-	p->SetSizerAndFit(p->sizer);
+void General(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("General"));
+
+    auto general = p->PageSizer(_("General"));
+    p->OptionAdd(general, _("Check for updates on startup"), "App/Auto/Check For Updates");
+    p->OptionAdd(general, _("Show main toolbar"), "App/Show Toolbar");
+    p->OptionAdd(general, _("Save UI state in subtitles files"), "App/Save UI State");
+    p->CellSkip(general);
+
+    p->OptionAdd(general, _("Toolbar Icon Size"), "App/Toolbar Icon Size");
+    wxString autoload_modes[] = { _("Never"), _("Always"), _("Ask") };
+    wxArrayString autoload_modes_arr(3, autoload_modes);
+    p->OptionChoice(general, _("Automatically load linked files"), autoload_modes_arr, "App/Auto/Load Linked Files");
+    p->OptionAdd(general, _("Undo Levels"), "Limits/Undo Levels", 2, 10000);
+
+    auto recent = p->PageSizer(_("Recently Used Lists"));
+    p->OptionAdd(recent, _("Files"), "Limits/MRU", 0, 16);
+    p->OptionAdd(recent, _("Find/Replace"), "Limits/Find Replace");
+
+    p->SetSizerAndFit(p->sizer);
 }
 
-void General_DefaultStyles(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Default styles"), OptionPage::PAGE_SUB);
-
-	auto staticbox = new wxStaticBoxSizer(wxVERTICAL, p, _("Default style catalogs"));
-	p->sizer->Add(staticbox, 0, wxEXPAND, 5);
-	p->sizer->AddSpacer(8);
-
-	auto instructions = new wxStaticText(p, wxID_ANY, _("The chosen style catalogs will be loaded when you start a new file or import files in the various formats.\n\nYou can set up style catalogs in the Style Manager."));
-	p->sizer->Fit(p);
-	instructions->Wrap(400);
-	staticbox->Add(instructions, 0, wxALL, 5);
-	staticbox->AddSpacer(16);
-	
-	auto general = new wxFlexGridSizer(2, 5, 5);
-	general->AddGrowableCol(0, 1);
-	staticbox->Add(general, 1, wxEXPAND, 5);
-
-	// Build a list of available style catalogs, and wished-available ones
-	auto const& avail_catalogs = AssStyleStorage::GetCatalogs();
-	std::unordered_set<std::string> catalogs_set(begin(avail_catalogs), end(avail_catalogs));
-	// Always include one named "Default" even if it doesn't exist (ensure there is at least one on the list)
-	catalogs_set.insert("Default");
-	// Include all catalogs named in the existing configuration
-	static const char *formats[] = { "ASS", "MicroDVD", "SRT", "TTXT", "TXT" };
-	for (auto formatname : formats)
-		catalogs_set.insert(OPT_GET("Subtitle Format/" + std::string(formatname) + "/Default Style Catalog")->GetString());
-	// Sorted version
-	wxArrayString catalogs;
-	for (auto const& cn : catalogs_set)
-		catalogs.Add(to_wx(cn));
-	catalogs.Sort();
-
-	p->OptionChoice(general, _("New files"), catalogs, "Subtitle Format/ASS/Default Style Catalog");
-	p->OptionChoice(general, _("MicroDVD import"), catalogs, "Subtitle Format/MicroDVD/Default Style Catalog");
-	p->OptionChoice(general, _("SRT import"), catalogs, "Subtitle Format/SRT/Default Style Catalog");
-	p->OptionChoice(general, _("TTXT import"), catalogs, "Subtitle Format/TTXT/Default Style Catalog");
-	p->OptionChoice(general, _("Plain text import"), catalogs, "Subtitle Format/TXT/Default Style Catalog");
-
-	p->SetSizerAndFit(p->sizer);
+void General_DefaultStyles(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Default styles"), OptionPage::PAGE_SUB);
+
+    auto staticbox = new wxStaticBoxSizer(wxVERTICAL, p, _("Default style catalogs"));
+    p->sizer->Add(staticbox, 0, wxEXPAND, 5);
+    p->sizer->AddSpacer(8);
+
+    auto instructions = new wxStaticText(p, wxID_ANY, _("The chosen style catalogs will be loaded when you start a new file or import files in the various formats.\n\nYou can set up style catalogs in the Style Manager."));
+    p->sizer->Fit(p);
+    instructions->Wrap(400);
+    staticbox->Add(instructions, 0, wxALL, 5);
+    staticbox->AddSpacer(16);
+
+    auto general = new wxFlexGridSizer(2, 5, 5);
+    general->AddGrowableCol(0, 1);
+    staticbox->Add(general, 1, wxEXPAND, 5);
+
+    // Build a list of available style catalogs, and wished-available ones
+    auto const &avail_catalogs = AssStyleStorage::GetCatalogs();
+    std::unordered_set<std::string> catalogs_set(begin(avail_catalogs), end(avail_catalogs));
+    // Always include one named "Default" even if it doesn't exist (ensure there is at least one on the list)
+    catalogs_set.insert("Default");
+    // Include all catalogs named in the existing configuration
+    static const char *formats[] = { "ASS", "MicroDVD", "SRT", "TTXT", "TXT" };
+    for (auto formatname : formats)
+        catalogs_set.insert(OPT_GET("Subtitle Format/" + std::string(formatname) + "/Default Style Catalog")->GetString());
+    // Sorted version
+    wxArrayString catalogs;
+    for (auto const &cn : catalogs_set)
+        catalogs.Add(to_wx(cn));
+    catalogs.Sort();
+
+    p->OptionChoice(general, _("New files"), catalogs, "Subtitle Format/ASS/Default Style Catalog");
+    p->OptionChoice(general, _("MicroDVD import"), catalogs, "Subtitle Format/MicroDVD/Default Style Catalog");
+    p->OptionChoice(general, _("SRT import"), catalogs, "Subtitle Format/SRT/Default Style Catalog");
+    p->OptionChoice(general, _("TTXT import"), catalogs, "Subtitle Format/TTXT/Default Style Catalog");
+    p->OptionChoice(general, _("Plain text import"), catalogs, "Subtitle Format/TXT/Default Style Catalog");
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Audio preferences page
-void Audio(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Audio"));
-
-	auto general = p->PageSizer(_("Options"));
-	p->OptionAdd(general, _("Default mouse wheel to zoom"), "Audio/Wheel Default to Zoom");
-	p->OptionAdd(general, _("Lock scroll on cursor"), "Audio/Lock Scroll on Cursor");
-	p->OptionAdd(general, _("Snap markers by default"), "Audio/Snap/Enable");
-	p->OptionAdd(general, _("Auto-focus on mouse over"), "Audio/Auto/Focus");
-	p->OptionAdd(general, _("Play audio when stepping in video"), "Audio/Plays When Stepping Video");
-	p->OptionAdd(general, _("Left-click-drag moves end marker"), "Audio/Drag Timing");
-	p->OptionAdd(general, _("Default timing length (ms)"), "Timing/Default Duration", 0, 36000);
-	p->OptionAdd(general, _("Default lead-in length (ms)"), "Audio/Lead/IN", 0, 36000);
-	p->OptionAdd(general, _("Default lead-out length (ms)"), "Audio/Lead/OUT", 0, 36000);
-
-	p->OptionAdd(general, _("Marker drag-start sensitivity (px)"), "Audio/Start Drag Sensitivity", 1, 15);
-	p->OptionAdd(general, _("Line boundary thickness (px)"), "Audio/Line Boundaries Thickness", 1, 5);
-	p->OptionAdd(general, _("Maximum snap distance (px)"), "Audio/Snap/Distance", 0, 25);
-
-	const wxString dtl_arr[] = { _("Don't show"), _("Show previous"), _("Show previous and next"), _("Show all") };
-	wxArrayString choice_dtl(4, dtl_arr);
-	p->OptionChoice(general, _("Show inactive lines"), choice_dtl, "Audio/Inactive Lines Display Mode");
-	p->CellSkip(general);
-	p->OptionAdd(general, _("Include commented inactive lines"), "Audio/Display/Draw/Inactive Comments");
-
-	auto display = p->PageSizer(_("Display Visual Options"));
-	p->OptionAdd(display, _("Keyframes in dialogue mode"), "Audio/Display/Draw/Keyframes in Dialogue Mode");
-	p->OptionAdd(display, _("Keyframes in karaoke mode"), "Audio/Display/Draw/Keyframes in Karaoke Mode");
-	p->OptionAdd(display, _("Cursor time"), "Audio/Display/Draw/Cursor Time");
-	p->OptionAdd(display, _("Video position"), "Audio/Display/Draw/Video Position");
-	p->OptionAdd(display, _("Seconds boundaries"), "Audio/Display/Draw/Seconds");
-	p->CellSkip(display);
-	p->OptionChoice(display, _("Waveform Style"), AudioWaveformRenderer::GetWaveformStyles(), "Audio/Display/Waveform Style");
-
-	auto label = p->PageSizer(_("Audio labels"));
-	p->OptionFont(label, "Audio/Karaoke/");
-
-	p->SetSizerAndFit(p->sizer);
+void Audio(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Audio"));
+
+    auto general = p->PageSizer(_("Options"));
+    p->OptionAdd(general, _("Default mouse wheel to zoom"), "Audio/Wheel Default to Zoom");
+    p->OptionAdd(general, _("Lock scroll on cursor"), "Audio/Lock Scroll on Cursor");
+    p->OptionAdd(general, _("Snap markers by default"), "Audio/Snap/Enable");
+    p->OptionAdd(general, _("Auto-focus on mouse over"), "Audio/Auto/Focus");
+    p->OptionAdd(general, _("Play audio when stepping in video"), "Audio/Plays When Stepping Video");
+    p->OptionAdd(general, _("Left-click-drag moves end marker"), "Audio/Drag Timing");
+    p->OptionAdd(general, _("Default timing length (ms)"), "Timing/Default Duration", 0, 36000);
+    p->OptionAdd(general, _("Default lead-in length (ms)"), "Audio/Lead/IN", 0, 36000);
+    p->OptionAdd(general, _("Default lead-out length (ms)"), "Audio/Lead/OUT", 0, 36000);
+
+    p->OptionAdd(general, _("Marker drag-start sensitivity (px)"), "Audio/Start Drag Sensitivity", 1, 15);
+    p->OptionAdd(general, _("Line boundary thickness (px)"), "Audio/Line Boundaries Thickness", 1, 5);
+    p->OptionAdd(general, _("Maximum snap distance (px)"), "Audio/Snap/Distance", 0, 25);
+
+    const wxString dtl_arr[] = { _("Don't show"), _("Show previous"), _("Show previous and next"), _("Show all") };
+    wxArrayString choice_dtl(4, dtl_arr);
+    p->OptionChoice(general, _("Show inactive lines"), choice_dtl, "Audio/Inactive Lines Display Mode");
+    p->CellSkip(general);
+    p->OptionAdd(general, _("Include commented inactive lines"), "Audio/Display/Draw/Inactive Comments");
+
+    auto display = p->PageSizer(_("Display Visual Options"));
+    p->OptionAdd(display, _("Keyframes in dialogue mode"), "Audio/Display/Draw/Keyframes in Dialogue Mode");
+    p->OptionAdd(display, _("Keyframes in karaoke mode"), "Audio/Display/Draw/Keyframes in Karaoke Mode");
+    p->OptionAdd(display, _("Cursor time"), "Audio/Display/Draw/Cursor Time");
+    p->OptionAdd(display, _("Video position"), "Audio/Display/Draw/Video Position");
+    p->OptionAdd(display, _("Seconds boundaries"), "Audio/Display/Draw/Seconds");
+    p->CellSkip(display);
+    p->OptionChoice(display, _("Waveform Style"), AudioWaveformRenderer::GetWaveformStyles(), "Audio/Display/Waveform Style");
+
+    auto label = p->PageSizer(_("Audio labels"));
+    p->OptionFont(label, "Audio/Karaoke/");
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Video preferences page
-void Video(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Video"));
-
-	auto general = p->PageSizer(_("Options"));
-	p->OptionAdd(general, _("Show keyframes in slider"), "Video/Slider/Show Keyframes");
-	p->CellSkip(general);
-	p->OptionAdd(general, _("Only show visual tools when mouse is over video"), "Tool/Visual/Autohide");
-	p->CellSkip(general);
-	p->OptionAdd(general, _("Seek video to line start on selection change"), "Video/Subtitle Sync");
-	p->CellSkip(general);
-	p->OptionAdd(general, _("Automatically open audio when opening video"), "Video/Open Audio");
-	p->CellSkip(general);
-
-	const wxString czoom_arr[24] = { "12.5%", "25%", "37.5%", "50%", "62.5%", "75%", "87.5%", "100%", "112.5%", "125%", "137.5%", "150%", "162.5%", "175%", "187.5%", "200%", "212.5%", "225%", "237.5%", "250%", "262.5%", "275%", "287.5%", "300%" };
-	wxArrayString choice_zoom(24, czoom_arr);
-	p->OptionChoice(general, _("Default Zoom"), choice_zoom, "Video/Default Zoom");
-
-	p->OptionAdd(general, _("Fast jump step in frames"), "Video/Slider/Fast Jump Step");
-
-	const wxString cscr_arr[3] = { "?video", "?script", "." };
-	wxArrayString scr_res(3, cscr_arr);
-	p->OptionChoice(general, _("Screenshot save path"), scr_res, "Path/Screenshot");
-
-	auto resolution = p->PageSizer(_("Script Resolution"));
-	wxControl *autocb = p->OptionAdd(resolution, _("Use resolution of first video opened"), "Subtitle/Default Resolution/Auto");
-	p->CellSkip(resolution);
-	p->DisableIfChecked(autocb,
-		p->OptionAdd(resolution, _("Default width"), "Subtitle/Default Resolution/Width"));
-	p->DisableIfChecked(autocb,
-		p->OptionAdd(resolution, _("Default height"), "Subtitle/Default Resolution/Height"));
-
-	const wxString cres_arr[] = {_("Never"), _("Ask"), _("Always set"), _("Always resample")};
-	wxArrayString choice_res(4, cres_arr);
-	p->OptionChoice(resolution, _("Match video resolution on open"), choice_res, "Video/Script Resolution Mismatch");
-
-	p->SetSizerAndFit(p->sizer);
+void Video(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Video"));
+
+    auto general = p->PageSizer(_("Options"));
+    p->OptionAdd(general, _("Show keyframes in slider"), "Video/Slider/Show Keyframes");
+    p->CellSkip(general);
+    p->OptionAdd(general, _("Only show visual tools when mouse is over video"), "Tool/Visual/Autohide");
+    p->CellSkip(general);
+    p->OptionAdd(general, _("Seek video to line start on selection change"), "Video/Subtitle Sync");
+    p->CellSkip(general);
+    p->OptionAdd(general, _("Automatically open audio when opening video"), "Video/Open Audio");
+    p->CellSkip(general);
+
+    const wxString czoom_arr[24] = { "12.5%", "25%", "37.5%", "50%", "62.5%", "75%", "87.5%", "100%", "112.5%", "125%", "137.5%", "150%", "162.5%", "175%", "187.5%", "200%", "212.5%", "225%", "237.5%", "250%", "262.5%", "275%", "287.5%", "300%" };
+    wxArrayString choice_zoom(24, czoom_arr);
+    p->OptionChoice(general, _("Default Zoom"), choice_zoom, "Video/Default Zoom");
+
+    p->OptionAdd(general, _("Fast jump step in frames"), "Video/Slider/Fast Jump Step");
+
+    const wxString cscr_arr[3] = { "?video", "?script", "." };
+    wxArrayString scr_res(3, cscr_arr);
+    p->OptionChoice(general, _("Screenshot save path"), scr_res, "Path/Screenshot");
+
+    auto resolution = p->PageSizer(_("Script Resolution"));
+    wxControl *autocb = p->OptionAdd(resolution, _("Use resolution of first video opened"), "Subtitle/Default Resolution/Auto");
+    p->CellSkip(resolution);
+    p->DisableIfChecked(autocb,
+                        p->OptionAdd(resolution, _("Default width"), "Subtitle/Default Resolution/Width"));
+    p->DisableIfChecked(autocb,
+                        p->OptionAdd(resolution, _("Default height"), "Subtitle/Default Resolution/Height"));
+
+    const wxString cres_arr[] = {_("Never"), _("Ask"), _("Always set"), _("Always resample")};
+    wxArrayString choice_res(4, cres_arr);
+    p->OptionChoice(resolution, _("Match video resolution on open"), choice_res, "Video/Script Resolution Mismatch");
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Interface preferences page
-void Interface(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Interface"));
-
-	auto edit_box = p->PageSizer(_("Edit Box"));
-	p->OptionAdd(edit_box, _("Enable call tips"), "App/Call Tips");
-	p->OptionAdd(edit_box, _("Overwrite in time boxes"), "Subtitle/Time Edit/Insert Mode");
-	p->CellSkip(edit_box);
-	p->OptionAdd(edit_box, _("Enable syntax highlighting"), "Subtitle/Highlight/Syntax");
-	p->OptionBrowse(edit_box, _("Dictionaries path"), "Path/Dictionary");
-	p->OptionFont(edit_box, "Subtitle/Edit Box/");
-
-	auto character_count = p->PageSizer(_("Character Counter"));
-	p->OptionAdd(character_count, _("Maximum characters per line"), "Subtitle/Character Limit", 0, 1000);
-	p->OptionAdd(character_count, _("Characters Per Second Warning Threshold"), "Subtitle/Character Counter/CPS Warning Threshold", 0, 1000);
-	p->OptionAdd(character_count, _("Characters Per Second Error Threshold"), "Subtitle/Character Counter/CPS Error Threshold", 0, 1000);
-	p->OptionAdd(character_count, _("Ignore whitespace"), "Subtitle/Character Counter/Ignore Whitespace");
-	p->OptionAdd(character_count, _("Ignore punctuation"), "Subtitle/Character Counter/Ignore Punctuation");
-
-	auto grid = p->PageSizer(_("Grid"));
-	p->OptionAdd(grid, _("Focus grid on click"), "Subtitle/Grid/Focus Allow");
-	p->OptionAdd(grid, _("Highlight visible subtitles"), "Subtitle/Grid/Highlight Subtitles in Frame");
-	p->OptionAdd(grid, _("Hide overrides symbol"), "Subtitle/Grid/Hide Overrides Char");
-	p->OptionFont(grid, "Subtitle/Grid/");
-
-	auto tl_assistant = p->PageSizer(_("Translation Assistant"));
-	p->OptionAdd(tl_assistant, _("Skip over whitespace"), "Tool/Translation Assistant/Skip Whitespace");
-
-	p->SetSizerAndFit(p->sizer);
+void Interface(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Interface"));
+
+    auto edit_box = p->PageSizer(_("Edit Box"));
+    p->OptionAdd(edit_box, _("Enable call tips"), "App/Call Tips");
+    p->OptionAdd(edit_box, _("Overwrite in time boxes"), "Subtitle/Time Edit/Insert Mode");
+    p->CellSkip(edit_box);
+    p->OptionAdd(edit_box, _("Enable syntax highlighting"), "Subtitle/Highlight/Syntax");
+    p->OptionBrowse(edit_box, _("Dictionaries path"), "Path/Dictionary");
+    p->OptionFont(edit_box, "Subtitle/Edit Box/");
+
+    auto character_count = p->PageSizer(_("Character Counter"));
+    p->OptionAdd(character_count, _("Maximum characters per line"), "Subtitle/Character Limit", 0, 1000);
+    p->OptionAdd(character_count, _("Characters Per Second Warning Threshold"), "Subtitle/Character Counter/CPS Warning Threshold", 0, 1000);
+    p->OptionAdd(character_count, _("Characters Per Second Error Threshold"), "Subtitle/Character Counter/CPS Error Threshold", 0, 1000);
+    p->OptionAdd(character_count, _("Ignore whitespace"), "Subtitle/Character Counter/Ignore Whitespace");
+    p->OptionAdd(character_count, _("Ignore punctuation"), "Subtitle/Character Counter/Ignore Punctuation");
+
+    auto grid = p->PageSizer(_("Grid"));
+    p->OptionAdd(grid, _("Focus grid on click"), "Subtitle/Grid/Focus Allow");
+    p->OptionAdd(grid, _("Highlight visible subtitles"), "Subtitle/Grid/Highlight Subtitles in Frame");
+    p->OptionAdd(grid, _("Hide overrides symbol"), "Subtitle/Grid/Hide Overrides Char");
+    p->OptionFont(grid, "Subtitle/Grid/");
+
+    auto tl_assistant = p->PageSizer(_("Translation Assistant"));
+    p->OptionAdd(tl_assistant, _("Skip over whitespace"), "Tool/Translation Assistant/Skip Whitespace");
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Interface Colours preferences subpage
-void Interface_Colours(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Colors"), OptionPage::PAGE_SCROLL|OptionPage::PAGE_SUB);
-
-	delete p->sizer;
-	wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
-
-	p->sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(p->sizer, wxEXPAND);
-
-	auto audio = p->PageSizer(_("Audio Display"));
-	p->OptionAdd(audio, _("Play cursor"), "Colour/Audio Display/Play Cursor");
-	p->OptionAdd(audio, _("Line boundary start"), "Colour/Audio Display/Line boundary Start");
-	p->OptionAdd(audio, _("Line boundary end"), "Colour/Audio Display/Line boundary End");
-	p->OptionAdd(audio, _("Line boundary inactive line"), "Colour/Audio Display/Line Boundary Inactive Line");
-	p->OptionAdd(audio, _("Syllable boundaries"), "Colour/Audio Display/Syllable Boundaries");
-	p->OptionAdd(audio, _("Seconds boundaries"), "Colour/Audio Display/Seconds Line");
-
-	auto syntax = p->PageSizer(_("Syntax Highlighting"));
-	p->OptionAdd(syntax, _("Background"), "Colour/Subtitle/Background");
-	p->OptionAdd(syntax, _("Normal"), "Colour/Subtitle/Syntax/Normal");
-	p->OptionAdd(syntax, _("Comments"), "Colour/Subtitle/Syntax/Comment");
-	p->OptionAdd(syntax, _("Drawings"), "Colour/Subtitle/Syntax/Drawing");
-	p->OptionAdd(syntax, _("Brackets"), "Colour/Subtitle/Syntax/Brackets");
-	p->OptionAdd(syntax, _("Slashes and Parentheses"), "Colour/Subtitle/Syntax/Slashes");
-	p->OptionAdd(syntax, _("Tags"), "Colour/Subtitle/Syntax/Tags");
-	p->OptionAdd(syntax, _("Parameters"), "Colour/Subtitle/Syntax/Parameters");
-	p->OptionAdd(syntax, _("Error"), "Colour/Subtitle/Syntax/Error");
-	p->OptionAdd(syntax, _("Error Background"), "Colour/Subtitle/Syntax/Background/Error");
-	p->OptionAdd(syntax, _("Line Break"), "Colour/Subtitle/Syntax/Line Break");
-	p->OptionAdd(syntax, _("Karaoke templates"), "Colour/Subtitle/Syntax/Karaoke Template");
-	p->OptionAdd(syntax, _("Karaoke variables"), "Colour/Subtitle/Syntax/Karaoke Variable");
-
-	p->sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->AddSpacer(5);
-	main_sizer->Add(p->sizer, wxEXPAND);
-
-	auto color_schemes = p->PageSizer(_("Audio Color Schemes"));
-	wxArrayString schemes = to_wx(OPT_GET("Audio/Colour Schemes")->GetListString());
-	p->OptionChoice(color_schemes, _("Spectrum"), schemes, "Colour/Audio Display/Spectrum");
-	p->OptionChoice(color_schemes, _("Waveform"), schemes, "Colour/Audio Display/Waveform");
-
-	auto grid = p->PageSizer(_("Subtitle Grid"));
-	p->OptionAdd(grid, _("Standard foreground"), "Colour/Subtitle Grid/Standard");
-	p->OptionAdd(grid, _("Standard background"), "Colour/Subtitle Grid/Background/Background");
-	p->OptionAdd(grid, _("Selection foreground"), "Colour/Subtitle Grid/Selection");
-	p->OptionAdd(grid, _("Selection background"), "Colour/Subtitle Grid/Background/Selection");
-	p->OptionAdd(grid, _("Collision foreground"), "Colour/Subtitle Grid/Collision");
-	p->OptionAdd(grid, _("In frame background"), "Colour/Subtitle Grid/Background/Inframe");
-	p->OptionAdd(grid, _("Comment background"), "Colour/Subtitle Grid/Background/Comment");
-	p->OptionAdd(grid, _("Selected comment background"), "Colour/Subtitle Grid/Background/Selected Comment");
-	p->OptionAdd(grid, _("Header background"), "Colour/Subtitle Grid/Header");
-	p->OptionAdd(grid, _("Left Column"), "Colour/Subtitle Grid/Left Column");
-	p->OptionAdd(grid, _("Active Line Border"), "Colour/Subtitle Grid/Active Border");
-	p->OptionAdd(grid, _("Lines"), "Colour/Subtitle Grid/Lines");
-	p->OptionAdd(grid, _("CPS Error"), "Colour/Subtitle Grid/CPS Error");
-
-	auto visual_tools = p->PageSizer(_("Visual Typesetting Tools"));
-	p->OptionAdd(visual_tools, _("Primary Lines"), "Colour/Visual Tools/Lines Primary");
-	p->OptionAdd(visual_tools, _("Secondary Lines"), "Colour/Visual Tools/Lines Secondary");
-	p->OptionAdd(visual_tools, _("Primary Highlight"), "Colour/Visual Tools/Highlight Primary");
-	p->OptionAdd(visual_tools, _("Secondary Highlight"), "Colour/Visual Tools/Highlight Secondary");
-
-	// Separate sizer to prevent the colors in the visual tools section from getting resized
-	auto visual_tools_alpha = p->PageSizer(_("Visual Typesetting Tools Alpha"));
-	p->OptionAdd(visual_tools_alpha, _("Shaded Area"), "Colour/Visual Tools/Shaded Area Alpha", 0, 1, 0.1);
-
-	p->sizer = main_sizer;
-
-	p->SetSizerAndFit(p->sizer);
+void Interface_Colours(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Colors"), OptionPage::PAGE_SCROLL | OptionPage::PAGE_SUB);
+
+    delete p->sizer;
+    wxSizer *main_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+    p->sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(p->sizer, wxEXPAND);
+
+    auto audio = p->PageSizer(_("Audio Display"));
+    p->OptionAdd(audio, _("Play cursor"), "Colour/Audio Display/Play Cursor");
+    p->OptionAdd(audio, _("Line boundary start"), "Colour/Audio Display/Line boundary Start");
+    p->OptionAdd(audio, _("Line boundary end"), "Colour/Audio Display/Line boundary End");
+    p->OptionAdd(audio, _("Line boundary inactive line"), "Colour/Audio Display/Line Boundary Inactive Line");
+    p->OptionAdd(audio, _("Syllable boundaries"), "Colour/Audio Display/Syllable Boundaries");
+    p->OptionAdd(audio, _("Seconds boundaries"), "Colour/Audio Display/Seconds Line");
+
+    auto syntax = p->PageSizer(_("Syntax Highlighting"));
+    p->OptionAdd(syntax, _("Background"), "Colour/Subtitle/Background");
+    p->OptionAdd(syntax, _("Normal"), "Colour/Subtitle/Syntax/Normal");
+    p->OptionAdd(syntax, _("Comments"), "Colour/Subtitle/Syntax/Comment");
+    p->OptionAdd(syntax, _("Drawings"), "Colour/Subtitle/Syntax/Drawing");
+    p->OptionAdd(syntax, _("Brackets"), "Colour/Subtitle/Syntax/Brackets");
+    p->OptionAdd(syntax, _("Slashes and Parentheses"), "Colour/Subtitle/Syntax/Slashes");
+    p->OptionAdd(syntax, _("Tags"), "Colour/Subtitle/Syntax/Tags");
+    p->OptionAdd(syntax, _("Parameters"), "Colour/Subtitle/Syntax/Parameters");
+    p->OptionAdd(syntax, _("Error"), "Colour/Subtitle/Syntax/Error");
+    p->OptionAdd(syntax, _("Error Background"), "Colour/Subtitle/Syntax/Background/Error");
+    p->OptionAdd(syntax, _("Line Break"), "Colour/Subtitle/Syntax/Line Break");
+    p->OptionAdd(syntax, _("Karaoke templates"), "Colour/Subtitle/Syntax/Karaoke Template");
+    p->OptionAdd(syntax, _("Karaoke variables"), "Colour/Subtitle/Syntax/Karaoke Variable");
+
+    p->sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->AddSpacer(5);
+    main_sizer->Add(p->sizer, wxEXPAND);
+
+    auto color_schemes = p->PageSizer(_("Audio Color Schemes"));
+    wxArrayString schemes = to_wx(OPT_GET("Audio/Colour Schemes")->GetListString());
+    p->OptionChoice(color_schemes, _("Spectrum"), schemes, "Colour/Audio Display/Spectrum");
+    p->OptionChoice(color_schemes, _("Waveform"), schemes, "Colour/Audio Display/Waveform");
+
+    auto grid = p->PageSizer(_("Subtitle Grid"));
+    p->OptionAdd(grid, _("Standard foreground"), "Colour/Subtitle Grid/Standard");
+    p->OptionAdd(grid, _("Standard background"), "Colour/Subtitle Grid/Background/Background");
+    p->OptionAdd(grid, _("Selection foreground"), "Colour/Subtitle Grid/Selection");
+    p->OptionAdd(grid, _("Selection background"), "Colour/Subtitle Grid/Background/Selection");
+    p->OptionAdd(grid, _("Collision foreground"), "Colour/Subtitle Grid/Collision");
+    p->OptionAdd(grid, _("In frame background"), "Colour/Subtitle Grid/Background/Inframe");
+    p->OptionAdd(grid, _("Comment background"), "Colour/Subtitle Grid/Background/Comment");
+    p->OptionAdd(grid, _("Selected comment background"), "Colour/Subtitle Grid/Background/Selected Comment");
+    p->OptionAdd(grid, _("Header background"), "Colour/Subtitle Grid/Header");
+    p->OptionAdd(grid, _("Left Column"), "Colour/Subtitle Grid/Left Column");
+    p->OptionAdd(grid, _("Active Line Border"), "Colour/Subtitle Grid/Active Border");
+    p->OptionAdd(grid, _("Lines"), "Colour/Subtitle Grid/Lines");
+    p->OptionAdd(grid, _("CPS Error"), "Colour/Subtitle Grid/CPS Error");
+
+    auto visual_tools = p->PageSizer(_("Visual Typesetting Tools"));
+    p->OptionAdd(visual_tools, _("Primary Lines"), "Colour/Visual Tools/Lines Primary");
+    p->OptionAdd(visual_tools, _("Secondary Lines"), "Colour/Visual Tools/Lines Secondary");
+    p->OptionAdd(visual_tools, _("Primary Highlight"), "Colour/Visual Tools/Highlight Primary");
+    p->OptionAdd(visual_tools, _("Secondary Highlight"), "Colour/Visual Tools/Highlight Secondary");
+
+    // Separate sizer to prevent the colors in the visual tools section from getting resized
+    auto visual_tools_alpha = p->PageSizer(_("Visual Typesetting Tools Alpha"));
+    p->OptionAdd(visual_tools_alpha, _("Shaded Area"), "Colour/Visual Tools/Shaded Area Alpha", 0, 1, 0.1);
+
+    p->sizer = main_sizer;
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Backup preferences page
-void Backup(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Backup"));
-
-	auto save = p->PageSizer(_("Automatic Save"));
-	wxControl *cb = p->OptionAdd(save, _("Enable"), "App/Auto/Save");
-	p->CellSkip(save);
-	p->EnableIfChecked(cb,
-		p->OptionAdd(save, _("Interval in seconds"), "App/Auto/Save Every Seconds", 1));
-	p->OptionBrowse(save, _("Path"), "Path/Auto/Save", cb, true);
-	p->OptionAdd(save, _("Autosave after every change"), "App/Auto/Save on Every Change");
-
-	auto backup = p->PageSizer(_("Automatic Backup"));
-	cb = p->OptionAdd(backup, _("Enable"), "App/Auto/Backup");
-	p->CellSkip(backup);
-	p->OptionBrowse(backup, _("Path"), "Path/Auto/Backup", cb, true);
-
-	p->SetSizerAndFit(p->sizer);
+void Backup(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Backup"));
+
+    auto save = p->PageSizer(_("Automatic Save"));
+    wxControl *cb = p->OptionAdd(save, _("Enable"), "App/Auto/Save");
+    p->CellSkip(save);
+    p->EnableIfChecked(cb,
+                       p->OptionAdd(save, _("Interval in seconds"), "App/Auto/Save Every Seconds", 1));
+    p->OptionBrowse(save, _("Path"), "Path/Auto/Save", cb, true);
+    p->OptionAdd(save, _("Autosave after every change"), "App/Auto/Save on Every Change");
+
+    auto backup = p->PageSizer(_("Automatic Backup"));
+    cb = p->OptionAdd(backup, _("Enable"), "App/Auto/Backup");
+    p->CellSkip(backup);
+    p->OptionBrowse(backup, _("Path"), "Path/Auto/Backup", cb, true);
+
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Automation preferences page
-void Automation(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Automation"));
+void Automation(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Automation"));
 
-	auto general = p->PageSizer(_("General"));
+    auto general = p->PageSizer(_("General"));
 
-	p->OptionAdd(general, _("Base path"), "Path/Automation/Base");
-	p->OptionAdd(general, _("Include path"), "Path/Automation/Include");
-	p->OptionAdd(general, _("Auto-load path"), "Path/Automation/Autoload");
+    p->OptionAdd(general, _("Base path"), "Path/Automation/Base");
+    p->OptionAdd(general, _("Include path"), "Path/Automation/Include");
+    p->OptionAdd(general, _("Auto-load path"), "Path/Automation/Autoload");
 
-	const wxString tl_arr[6] = { _("0: Fatal"), _("1: Error"), _("2: Warning"), _("3: Hint"), _("4: Debug"), _("5: Trace") };
-	wxArrayString tl_choice(6, tl_arr);
-	p->OptionChoice(general, _("Trace level"), tl_choice, "Automation/Trace Level");
+    const wxString tl_arr[6] = { _("0: Fatal"), _("1: Error"), _("2: Warning"), _("3: Hint"), _("4: Debug"), _("5: Trace") };
+    wxArrayString tl_choice(6, tl_arr);
+    p->OptionChoice(general, _("Trace level"), tl_choice, "Automation/Trace Level");
 
-	const wxString ar_arr[4] = { _("No scripts"), _("Subtitle-local scripts"), _("Global autoload scripts"), _("All scripts") };
-	wxArrayString ar_choice(4, ar_arr);
-	p->OptionChoice(general, _("Autoreload on Export"), ar_choice, "Automation/Autoreload Mode");
+    const wxString ar_arr[4] = { _("No scripts"), _("Subtitle-local scripts"), _("Global autoload scripts"), _("All scripts") };
+    wxArrayString ar_choice(4, ar_arr);
+    p->OptionChoice(general, _("Autoreload on Export"), ar_choice, "Automation/Autoreload Mode");
 
-	p->SetSizerAndFit(p->sizer);
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Advanced preferences page
-void Advanced(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Advanced"));
+void Advanced(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Advanced"));
 
-	auto general = p->PageSizer(_("General"));
+    auto general = p->PageSizer(_("General"));
 
-	auto warning = new wxStaticText(p, wxID_ANY ,_("Changing these settings might result in bugs and/or crashes.  Do not touch these unless you know what you're doing."));
-	warning->SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
-	p->sizer->Fit(p);
-	warning->Wrap(400);
-	general->Add(warning, 0, wxALL, 5);
+    auto warning = new wxStaticText(p, wxID_ANY, _("Changing these settings might result in bugs and/or crashes.  Do not touch these unless you know what you're doing."));
+    warning->SetFont(wxFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
+    p->sizer->Fit(p);
+    warning->Wrap(400);
+    general->Add(warning, 0, wxALL, 5);
 
-	p->SetSizerAndFit(p->sizer);
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Advanced Audio preferences subpage
-void Advanced_Audio(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Audio"), OptionPage::PAGE_SUB);
+void Advanced_Audio(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Audio"), OptionPage::PAGE_SUB);
 
-	auto expert = p->PageSizer(_("Expert"));
+    auto expert = p->PageSizer(_("Expert"));
 
-	wxArrayString ap_choice = to_wx(GetAudioProviderNames());
-	p->OptionChoice(expert, _("Audio provider"), ap_choice, "Audio/Provider");
+    wxArrayString ap_choice = to_wx(GetAudioProviderNames());
+    p->OptionChoice(expert, _("Audio provider"), ap_choice, "Audio/Provider");
 
-	wxArrayString apl_choice = to_wx(AudioPlayerFactory::GetClasses());
-	p->OptionChoice(expert, _("Audio player"), apl_choice, "Audio/Player");
+    wxArrayString apl_choice = to_wx(AudioPlayerFactory::GetClasses());
+    p->OptionChoice(expert, _("Audio player"), apl_choice, "Audio/Player");
 
-	auto cache = p->PageSizer(_("Cache"));
-	const wxString ct_arr[3] = { _("None (NOT RECOMMENDED)"), _("RAM"), _("Hard Disk") };
-	wxArrayString ct_choice(3, ct_arr);
-	p->OptionChoice(cache, _("Cache type"), ct_choice, "Audio/Cache/Type");
-	p->OptionBrowse(cache, _("Path"), "Audio/Cache/HD/Location");
+    auto cache = p->PageSizer(_("Cache"));
+    const wxString ct_arr[3] = { _("None (NOT RECOMMENDED)"), _("RAM"), _("Hard Disk") };
+    wxArrayString ct_choice(3, ct_arr);
+    p->OptionChoice(cache, _("Cache type"), ct_choice, "Audio/Cache/Type");
+    p->OptionBrowse(cache, _("Path"), "Audio/Cache/HD/Location");
 
-	auto spectrum = p->PageSizer(_("Spectrum"));
+    auto spectrum = p->PageSizer(_("Spectrum"));
 
-	const wxString sq_arr[4] = { _("Regular quality"), _("Better quality"), _("High quality"), _("Insane quality") };
-	wxArrayString sq_choice(4, sq_arr);
-	p->OptionChoice(spectrum, _("Quality"), sq_choice, "Audio/Renderer/Spectrum/Quality");
+    const wxString sq_arr[4] = { _("Regular quality"), _("Better quality"), _("High quality"), _("Insane quality") };
+    wxArrayString sq_choice(4, sq_arr);
+    p->OptionChoice(spectrum, _("Quality"), sq_choice, "Audio/Renderer/Spectrum/Quality");
 
-	p->OptionAdd(spectrum, _("Cache memory max (MB)"), "Audio/Renderer/Spectrum/Memory Max", 2, 1024);
+    p->OptionAdd(spectrum, _("Cache memory max (MB)"), "Audio/Renderer/Spectrum/Memory Max", 2, 1024);
 
 #ifdef WITH_AVISYNTH
-	auto avisynth = p->PageSizer("Avisynth");
-	const wxString adm_arr[3] = { "ConvertToMono", "GetLeftChannel", "GetRightChannel" };
-	wxArrayString adm_choice(3, adm_arr);
-	p->OptionChoice(avisynth, _("Avisynth down-mixer"), adm_choice, "Audio/Downmixer");
-	p->OptionAdd(avisynth, _("Force sample rate"), "Provider/Audio/AVS/Sample Rate");
+    auto avisynth = p->PageSizer("Avisynth");
+    const wxString adm_arr[3] = { "ConvertToMono", "GetLeftChannel", "GetRightChannel" };
+    wxArrayString adm_choice(3, adm_arr);
+    p->OptionChoice(avisynth, _("Avisynth down-mixer"), adm_choice, "Audio/Downmixer");
+    p->OptionAdd(avisynth, _("Force sample rate"), "Provider/Audio/AVS/Sample Rate");
 #endif
 
 #ifdef WITH_FFMS2
-	auto ffms = p->PageSizer("FFmpegSource");
+    auto ffms = p->PageSizer("FFmpegSource");
 
-	const wxString error_modes[] = { _("Ignore"), _("Clear"), _("Stop"), _("Abort") };
-	wxArrayString error_modes_choice(4, error_modes);
-	p->OptionChoice(ffms, _("Audio indexing error handling mode"), error_modes_choice, "Provider/Audio/FFmpegSource/Decode Error Handling");
+    const wxString error_modes[] = { _("Ignore"), _("Clear"), _("Stop"), _("Abort") };
+    wxArrayString error_modes_choice(4, error_modes);
+    p->OptionChoice(ffms, _("Audio indexing error handling mode"), error_modes_choice, "Provider/Audio/FFmpegSource/Decode Error Handling");
 
-	p->OptionAdd(ffms, _("Always index all audio tracks"), "Provider/FFmpegSource/Index All Tracks");
+    p->OptionAdd(ffms, _("Always index all audio tracks"), "Provider/FFmpegSource/Index All Tracks");
 #endif
 
 #ifdef WITH_PORTAUDIO
-	auto portaudio = p->PageSizer("Portaudio");
-	p->OptionChoice(portaudio, _("Portaudio device"), PortAudioPlayer::GetOutputDevices(), "Player/Audio/PortAudio/Device Name");
+    auto portaudio = p->PageSizer("Portaudio");
+    p->OptionChoice(portaudio, _("Portaudio device"), PortAudioPlayer::GetOutputDevices(), "Player/Audio/PortAudio/Device Name");
 #endif
 
 #ifdef WITH_OSS
-	auto oss = p->PageSizer("OSS");
-	p->OptionBrowse(oss, _("OSS Device"), "Player/Audio/OSS/Device");
+    auto oss = p->PageSizer("OSS");
+    p->OptionBrowse(oss, _("OSS Device"), "Player/Audio/OSS/Device");
 #endif
 
 #ifdef WITH_DIRECTSOUND
-	auto dsound = p->PageSizer("DirectSound");
-	p->OptionAdd(dsound, _("Buffer latency"), "Player/Audio/DirectSound/Buffer Latency", 1, 1000);
-	p->OptionAdd(dsound, _("Buffer length"), "Player/Audio/DirectSound/Buffer Length", 1, 100);
+    auto dsound = p->PageSizer("DirectSound");
+    p->OptionAdd(dsound, _("Buffer latency"), "Player/Audio/DirectSound/Buffer Latency", 1, 1000);
+    p->OptionAdd(dsound, _("Buffer length"), "Player/Audio/DirectSound/Buffer Length", 1, 100);
 #endif
 
-	p->SetSizerAndFit(p->sizer);
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// Advanced Video preferences subpage
-void Advanced_Video(wxTreebook *book, Preferences *parent) {
-	auto p = new OptionPage(book, parent, _("Video"), OptionPage::PAGE_SUB);
+void Advanced_Video(wxTreebook *book, Preferences *parent)
+{
+    auto p = new OptionPage(book, parent, _("Video"), OptionPage::PAGE_SUB);
 
-	auto expert = p->PageSizer(_("Expert"));
+    auto expert = p->PageSizer(_("Expert"));
 
-	wxArrayString vp_choice = to_wx(VideoProviderFactory::GetClasses());
-	p->OptionChoice(expert, _("Video provider"), vp_choice, "Video/Provider");
+    wxArrayString vp_choice = to_wx(VideoProviderFactory::GetClasses());
+    p->OptionChoice(expert, _("Video provider"), vp_choice, "Video/Provider");
 
-	wxArrayString sp_choice = to_wx(SubtitlesProviderFactory::GetClasses());
-	p->OptionChoice(expert, _("Subtitles provider"), sp_choice, "Subtitle/Provider");
+    wxArrayString sp_choice = to_wx(SubtitlesProviderFactory::GetClasses());
+    p->OptionChoice(expert, _("Subtitles provider"), sp_choice, "Subtitle/Provider");
 
 #ifdef WITH_AVISYNTH
-	auto avisynth = p->PageSizer("Avisynth");
-	p->OptionAdd(avisynth, _("Allow pre-2.56a Avisynth"), "Provider/Avisynth/Allow Ancient");
-	p->CellSkip(avisynth);
-	p->OptionAdd(avisynth, _("Avisynth memory limit"), "Provider/Avisynth/Memory Max");
+    auto avisynth = p->PageSizer("Avisynth");
+    p->OptionAdd(avisynth, _("Allow pre-2.56a Avisynth"), "Provider/Avisynth/Allow Ancient");
+    p->CellSkip(avisynth);
+    p->OptionAdd(avisynth, _("Avisynth memory limit"), "Provider/Avisynth/Memory Max");
 #endif
 
 #ifdef WITH_FFMS2
-	auto ffms = p->PageSizer("FFmpegSource");
+    auto ffms = p->PageSizer("FFmpegSource");
 
-	const wxString log_levels[] = { "Quiet", "Panic", "Fatal", "Error", "Warning", "Info", "Verbose", "Debug" };
-	wxArrayString log_levels_choice(8, log_levels);
-	p->OptionChoice(ffms, _("Debug log verbosity"), log_levels_choice, "Provider/FFmpegSource/Log Level");
+    const wxString log_levels[] = { "Quiet", "Panic", "Fatal", "Error", "Warning", "Info", "Verbose", "Debug" };
+    wxArrayString log_levels_choice(8, log_levels);
+    p->OptionChoice(ffms, _("Debug log verbosity"), log_levels_choice, "Provider/FFmpegSource/Log Level");
 
-	p->OptionAdd(ffms, _("Decoding threads"), "Provider/Video/FFmpegSource/Decoding Threads", -1);
-	p->OptionAdd(ffms, _("Enable unsafe seeking"), "Provider/Video/FFmpegSource/Unsafe Seeking");
+    p->OptionAdd(ffms, _("Decoding threads"), "Provider/Video/FFmpegSource/Decoding Threads", -1);
+    p->OptionAdd(ffms, _("Enable unsafe seeking"), "Provider/Video/FFmpegSource/Unsafe Seeking");
 #endif
 
-	p->SetSizerAndFit(p->sizer);
+    p->SetSizerAndFit(p->sizer);
 }
 
 /// wxDataViewIconTextRenderer with command name autocompletion
 class CommandRenderer final : public wxDataViewCustomRenderer {
-	wxArrayString autocomplete;
-	wxDataViewIconText value;
-	static const int icon_width = 20;
+    wxArrayString autocomplete;
+    wxDataViewIconText value;
+    static const int icon_width = 20;
 
 public:
-	CommandRenderer()
-	: wxDataViewCustomRenderer("wxDataViewIconText", wxDATAVIEW_CELL_EDITABLE)
-	, autocomplete(to_wx(cmd::get_registered_commands()))
-	{
-	}
-
-	wxWindow *CreateEditorCtrl(wxWindow *parent, wxRect label_rect, wxVariant const& value) override {
-		wxDataViewIconText iconText;
-		iconText << value;
-
-		wxString text = iconText.GetText();
-
-		// adjust the label rect to take the width of the icon into account
-		label_rect.x += icon_width;
-		label_rect.width -= icon_width;
-
-		wxTextCtrl* ctrl = new wxTextCtrl(parent, -1, text, label_rect.GetPosition(), label_rect.GetSize(), wxTE_PROCESS_ENTER);
-		ctrl->SetInsertionPointEnd();
-		ctrl->SelectAll();
-		ctrl->AutoComplete(autocomplete);
-		return ctrl;
-	}
-
-	bool SetValue(wxVariant const& var) override {
-		value << var;
-		return true;
-	}
-
-	bool Render(wxRect rect, wxDC *dc, int state) override {
-		wxIcon const& icon = value.GetIcon();
-		if (icon.IsOk())
-			dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2);
-
-		RenderText(value.GetText(), icon_width, rect, dc, state);
-
-		return true;
-	}
-
-	wxSize GetSize() const override {
-		if (!value.GetText().empty()) {
-			wxSize size = GetTextExtent(value.GetText());
-			size.x += icon_width;
-			return size;
-		}
-		return wxSize(80,20);
-	}
-
-	bool GetValueFromEditorCtrl(wxWindow* editor, wxVariant &var) override {
-		wxTextCtrl *text = static_cast<wxTextCtrl*>(editor);
-		wxDataViewIconText iconText(text->GetValue(), value.GetIcon());
-		var << iconText;
-		return true;
-	}
-
-	bool GetValue(wxVariant &) const override { return false; }
-	bool HasEditorCtrl() const override { return true; }
+    CommandRenderer()
+        : wxDataViewCustomRenderer("wxDataViewIconText", wxDATAVIEW_CELL_EDITABLE)
+        , autocomplete(to_wx(cmd::get_registered_commands())) {
+    }
+
+    wxWindow *CreateEditorCtrl(wxWindow *parent, wxRect label_rect, wxVariant const &value) override {
+        wxDataViewIconText iconText;
+        iconText << value;
+
+        wxString text = iconText.GetText();
+
+        // adjust the label rect to take the width of the icon into account
+        label_rect.x += icon_width;
+        label_rect.width -= icon_width;
+
+        wxTextCtrl *ctrl = new wxTextCtrl(parent, -1, text, label_rect.GetPosition(), label_rect.GetSize(), wxTE_PROCESS_ENTER);
+        ctrl->SetInsertionPointEnd();
+        ctrl->SelectAll();
+        ctrl->AutoComplete(autocomplete);
+        return ctrl;
+    }
+
+    bool SetValue(wxVariant const &var) override {
+        value << var;
+        return true;
+    }
+
+    bool Render(wxRect rect, wxDC *dc, int state) override {
+        wxIcon const &icon = value.GetIcon();
+        if (icon.IsOk())
+            dc->DrawIcon(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2);
+
+        RenderText(value.GetText(), icon_width, rect, dc, state);
+
+        return true;
+    }
+
+    wxSize GetSize() const override {
+        if (!value.GetText().empty()) {
+            wxSize size = GetTextExtent(value.GetText());
+            size.x += icon_width;
+            return size;
+        }
+        return wxSize(80, 20);
+    }
+
+    bool GetValueFromEditorCtrl(wxWindow *editor, wxVariant &var) override {
+        wxTextCtrl *text = static_cast<wxTextCtrl *>(editor);
+        wxDataViewIconText iconText(text->GetValue(), value.GetIcon());
+        var << iconText;
+        return true;
+    }
+
+    bool GetValue(wxVariant &) const override { return false; }
+    bool HasEditorCtrl() const override { return true; }
 };
 
 class HotkeyRenderer final : public wxDataViewCustomRenderer {
-	wxString value;
-	wxTextCtrl *ctrl = nullptr;
+    wxString value;
+    wxTextCtrl *ctrl = nullptr;
 
 public:
-	HotkeyRenderer()
-	: wxDataViewCustomRenderer("string", wxDATAVIEW_CELL_EDITABLE)
-	{ }
-
-	wxWindow *CreateEditorCtrl(wxWindow *parent, wxRect label_rect, wxVariant const& var) override {
-		ctrl = new wxTextCtrl(parent, -1, var.GetString(), label_rect.GetPosition(), label_rect.GetSize(), wxTE_PROCESS_ENTER);
-		ctrl->SetInsertionPointEnd();
-		ctrl->SelectAll();
-		ctrl->Bind(wxEVT_CHAR_HOOK, &HotkeyRenderer::OnKeyDown, this);
-		return ctrl;
-	}
-
-	void OnKeyDown(wxKeyEvent &evt) {
-		ctrl->ChangeValue(to_wx(hotkey::keypress_to_str(evt.GetKeyCode(), evt.GetModifiers())));
-	}
-
-	bool SetValue(wxVariant const& var) override {
-		value = var.GetString();
-		return true;
-	}
-
-	bool Render(wxRect rect, wxDC *dc, int state) override {
-		RenderText(value, 0, rect, dc, state);
-		return true;
-	}
-
-	bool GetValueFromEditorCtrl(wxWindow*, wxVariant &var) override {
-		var = ctrl->GetValue();
-		return true;
-	}
-
-	bool GetValue(wxVariant &) const override { return false; }
-	wxSize GetSize() const override { return !value ? wxSize(80, 20) : GetTextExtent(value); }
-	bool HasEditorCtrl() const override { return true; }
+    HotkeyRenderer()
+        : wxDataViewCustomRenderer("string", wxDATAVIEW_CELL_EDITABLE)
+    { }
+
+    wxWindow *CreateEditorCtrl(wxWindow *parent, wxRect label_rect, wxVariant const &var) override {
+        ctrl = new wxTextCtrl(parent, -1, var.GetString(), label_rect.GetPosition(), label_rect.GetSize(), wxTE_PROCESS_ENTER);
+        ctrl->SetInsertionPointEnd();
+        ctrl->SelectAll();
+        ctrl->Bind(wxEVT_CHAR_HOOK, &HotkeyRenderer::OnKeyDown, this);
+        return ctrl;
+    }
+
+    void OnKeyDown(wxKeyEvent &evt) {
+        ctrl->ChangeValue(to_wx(hotkey::keypress_to_str(evt.GetKeyCode(), evt.GetModifiers())));
+    }
+
+    bool SetValue(wxVariant const &var) override {
+        value = var.GetString();
+        return true;
+    }
+
+    bool Render(wxRect rect, wxDC *dc, int state) override {
+        RenderText(value, 0, rect, dc, state);
+        return true;
+    }
+
+    bool GetValueFromEditorCtrl(wxWindow *, wxVariant &var) override {
+        var = ctrl->GetValue();
+        return true;
+    }
+
+    bool GetValue(wxVariant &) const override { return false; }
+    wxSize GetSize() const override { return !value ? wxSize(80, 20) : GetTextExtent(value); }
+    bool HasEditorCtrl() const override { return true; }
 };
 
-static void edit_item(wxDataViewCtrl *dvc, wxDataViewItem item) {
-	dvc->EditItem(item, dvc->GetColumn(0));
+static void edit_item(wxDataViewCtrl *dvc, wxDataViewItem item)
+{
+    dvc->EditItem(item, dvc->GetColumn(0));
 }
 
 class Interface_Hotkeys final : public OptionPage {
-	wxDataViewCtrl *dvc;
-	wxObjectDataPtr<HotkeyDataViewModel> model;
-	wxSearchCtrl *quick_search;
+    wxDataViewCtrl *dvc;
+    wxObjectDataPtr<HotkeyDataViewModel> model;
+    wxSearchCtrl *quick_search;
 
-	void OnNewButton(wxCommandEvent&);
-	void OnUpdateFilter(wxCommandEvent&);
+    void OnNewButton(wxCommandEvent &);
+    void OnUpdateFilter(wxCommandEvent &);
 public:
-	Interface_Hotkeys(wxTreebook *book, Preferences *parent);
+    Interface_Hotkeys(wxTreebook *book, Preferences *parent);
 };
 
 /// Interface Hotkeys preferences subpage
 Interface_Hotkeys::Interface_Hotkeys(wxTreebook *book, Preferences *parent)
-: OptionPage(book, parent, _("Hotkeys"), OptionPage::PAGE_SUB)
-, model(new HotkeyDataViewModel(parent))
+    : OptionPage(book, parent, _("Hotkeys"), OptionPage::PAGE_SUB)
+    , model(new HotkeyDataViewModel(parent))
 {
-	quick_search = new wxSearchCtrl(this, -1);
-	auto new_button = new wxButton(this, -1, _("&New"));
-	auto edit_button = new wxButton(this, -1, _("&Edit"));
-	auto delete_button = new wxButton(this, -1, _("&Delete"));
+    quick_search = new wxSearchCtrl(this, -1);
+    auto new_button = new wxButton(this, -1, _("&New"));
+    auto edit_button = new wxButton(this, -1, _("&Edit"));
+    auto delete_button = new wxButton(this, -1, _("&Delete"));
 
-	new_button->Bind(wxEVT_BUTTON, &Interface_Hotkeys::OnNewButton, this);
-	edit_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { edit_item(dvc, dvc->GetSelection()); });
-	delete_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { model->Delete(dvc->GetSelection()); });
+    new_button->Bind(wxEVT_BUTTON, &Interface_Hotkeys::OnNewButton, this);
+    edit_button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { edit_item(dvc, dvc->GetSelection()); });
+    delete_button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) { model->Delete(dvc->GetSelection()); });
 
-	quick_search->Bind(wxEVT_TEXT, &Interface_Hotkeys::OnUpdateFilter, this);
-	quick_search->Bind(wxEVT_SEARCHCTRL_CANCEL_BTN, [=](wxCommandEvent&) { quick_search->SetValue(""); });
+    quick_search->Bind(wxEVT_TEXT, &Interface_Hotkeys::OnUpdateFilter, this);
+    quick_search->Bind(wxEVT_SEARCHCTRL_CANCEL_BTN, [ = ](wxCommandEvent &) { quick_search->SetValue(""); });
 
-	dvc = new wxDataViewCtrl(this, -1);
-	dvc->AssociateModel(model.get());
+    dvc = new wxDataViewCtrl(this, -1);
+    dvc->AssociateModel(model.get());
 #ifndef __APPLE__
-	dvc->AppendColumn(new wxDataViewColumn("Hotkey", new HotkeyRenderer, 0, 125, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
-	dvc->AppendColumn(new wxDataViewColumn("Command", new CommandRenderer, 1, 250, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
+    dvc->AppendColumn(new wxDataViewColumn("Hotkey", new HotkeyRenderer, 0, 125, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
+    dvc->AppendColumn(new wxDataViewColumn("Command", new CommandRenderer, 1, 250, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
 #else
-	auto col = new wxDataViewColumn("Hotkey", new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_EDITABLE), 0, 150, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE);
-	col->SetMinWidth(150);
-	dvc->AppendColumn(col);
-	dvc->AppendColumn(new wxDataViewColumn("Command", new wxDataViewIconTextRenderer("wxDataViewIconText", wxDATAVIEW_CELL_EDITABLE), 1, 250, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
+    auto col = new wxDataViewColumn("Hotkey", new wxDataViewTextRenderer("string", wxDATAVIEW_CELL_EDITABLE), 0, 150, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE);
+    col->SetMinWidth(150);
+    dvc->AppendColumn(col);
+    dvc->AppendColumn(new wxDataViewColumn("Command", new wxDataViewIconTextRenderer("wxDataViewIconText", wxDATAVIEW_CELL_EDITABLE), 1, 250, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE));
 #endif
-	dvc->AppendTextColumn("Description", 2, wxDATAVIEW_CELL_INERT, 300, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE);
+    dvc->AppendTextColumn("Description", 2, wxDATAVIEW_CELL_INERT, 300, wxALIGN_LEFT, wxCOL_SORTABLE | wxCOL_RESIZABLE);
 
-	wxSizer *buttons = new wxBoxSizer(wxHORIZONTAL);
-	buttons->Add(quick_search, wxSizerFlags().Border());
-	buttons->AddStretchSpacer(1);
-	buttons->Add(new_button, wxSizerFlags().Border());
-	buttons->Add(edit_button, wxSizerFlags().Border());
-	buttons->Add(delete_button, wxSizerFlags().Border());
+    wxSizer *buttons = new wxBoxSizer(wxHORIZONTAL);
+    buttons->Add(quick_search, wxSizerFlags().Border());
+    buttons->AddStretchSpacer(1);
+    buttons->Add(new_button, wxSizerFlags().Border());
+    buttons->Add(edit_button, wxSizerFlags().Border());
+    buttons->Add(delete_button, wxSizerFlags().Border());
 
-	sizer->Add(buttons, wxSizerFlags().Expand());
-	sizer->Add(dvc, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT));
+    sizer->Add(buttons, wxSizerFlags().Expand());
+    sizer->Add(dvc, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT));
 
-	SetSizerAndFit(sizer);
+    SetSizerAndFit(sizer);
 }
 
-void Interface_Hotkeys::OnNewButton(wxCommandEvent&) {
-	wxDataViewItem sel = dvc->GetSelection();
-	dvc->ExpandAncestors(sel);
-	dvc->Expand(sel);
-
-	wxDataViewItem new_item = model->New(sel);
-	if (new_item.IsOk()) {
-		dvc->Select(new_item);
-		dvc->EnsureVisible(new_item);
-		edit_item(dvc, new_item);
-	}
+void Interface_Hotkeys::OnNewButton(wxCommandEvent &)
+{
+    wxDataViewItem sel = dvc->GetSelection();
+    dvc->ExpandAncestors(sel);
+    dvc->Expand(sel);
+
+    wxDataViewItem new_item = model->New(sel);
+    if (new_item.IsOk()) {
+        dvc->Select(new_item);
+        dvc->EnsureVisible(new_item);
+        edit_item(dvc, new_item);
+    }
 }
 
-void Interface_Hotkeys::OnUpdateFilter(wxCommandEvent&) {
-	model->SetFilter(quick_search->GetValue());
-
-	if (!quick_search->GetValue().empty()) {
-		wxDataViewItemArray contexts;
-		model->GetChildren(wxDataViewItem(nullptr), contexts);
-		for (auto const& context : contexts)
-			dvc->Expand(context);
-	}
+void Interface_Hotkeys::OnUpdateFilter(wxCommandEvent &)
+{
+    model->SetFilter(quick_search->GetValue());
+
+    if (!quick_search->GetValue().empty()) {
+        wxDataViewItemArray contexts;
+        model->GetChildren(wxDataViewItem(nullptr), contexts);
+        for (auto const &context : contexts)
+            dvc->Expand(context);
+    }
 }
 }
 
-void Preferences::SetOption(std::unique_ptr<agi::OptionValue> new_value) {
-	pending_changes[new_value->GetName()] = std::move(new_value);
-	applyButton->Enable(true);
+void Preferences::SetOption(std::unique_ptr<agi::OptionValue> new_value)
+{
+    pending_changes[new_value->GetName()] = std::move(new_value);
+    applyButton->Enable(true);
 }
 
-void Preferences::AddPendingChange(Thunk const& callback) {
-	pending_callbacks.push_back(callback);
-	applyButton->Enable(true);
+void Preferences::AddPendingChange(Thunk const &callback)
+{
+    pending_callbacks.push_back(callback);
+    applyButton->Enable(true);
 }
 
-void Preferences::AddChangeableOption(std::string const& name) {
-	option_names.push_back(name);
+void Preferences::AddChangeableOption(std::string const &name)
+{
+    option_names.push_back(name);
 }
 
-void Preferences::OnOK(wxCommandEvent &event) {
-	OnApply(event);
-	EndModal(0);
+void Preferences::OnOK(wxCommandEvent &event)
+{
+    OnApply(event);
+    EndModal(0);
 }
 
-void Preferences::OnApply(wxCommandEvent &) {
-	for (auto const& change : pending_changes)
-		OPT_SET(change.first)->Set(change.second.get());
-	pending_changes.clear();
+void Preferences::OnApply(wxCommandEvent &)
+{
+    for (auto const &change : pending_changes)
+        OPT_SET(change.first)->Set(change.second.get());
+    pending_changes.clear();
 
-	for (auto const& thunk : pending_callbacks)
-		thunk();
-	pending_callbacks.clear();
+    for (auto const &thunk : pending_callbacks)
+        thunk();
+    pending_callbacks.clear();
 
-	applyButton->Enable(false);
-	config::opt->Flush();
+    applyButton->Enable(false);
+    config::opt->Flush();
 }
 
-void Preferences::OnResetDefault(wxCommandEvent&) {
-	if (wxYES != wxMessageBox(_("Are you sure that you want to restore the defaults? All your settings will be overridden."), _("Restore defaults?"), wxYES_NO))
-		return;
-
-	for (auto const& opt_name : option_names) {
-		agi::OptionValue *opt = OPT_SET(opt_name);
-		if (!opt->IsDefault())
-			opt->Reset();
-	}
-	config::opt->Flush();
-
-	agi::hotkey::Hotkey def_hotkeys("", GET_DEFAULT_CONFIG(default_hotkey));
-	hotkey::inst->SetHotkeyMap(def_hotkeys.GetHotkeyMap());
-
-	// Close and reopen the dialog to update all the controls with the new values
-	OPT_SET("Tool/Preferences/Page")->SetInt(book->GetSelection());
-	EndModal(-1);
+void Preferences::OnResetDefault(wxCommandEvent &)
+{
+    if (wxYES != wxMessageBox(_("Are you sure that you want to restore the defaults? All your settings will be overridden."), _("Restore defaults?"), wxYES_NO))
+        return;
+
+    for (auto const &opt_name : option_names) {
+        agi::OptionValue *opt = OPT_SET(opt_name);
+        if (!opt->IsDefault())
+            opt->Reset();
+    }
+    config::opt->Flush();
+
+    agi::hotkey::Hotkey def_hotkeys("", GET_DEFAULT_CONFIG(default_hotkey));
+    hotkey::inst->SetHotkeyMap(def_hotkeys.GetHotkeyMap());
+
+    // Close and reopen the dialog to update all the controls with the new values
+    OPT_SET("Tool/Preferences/Page")->SetInt(book->GetSelection());
+    EndModal(-1);
 }
 
-Preferences::Preferences(wxWindow *parent): wxDialog(parent, -1, _("Preferences"), wxDefaultPosition, wxSize(-1, -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
-	SetIcon(GETICON(options_button_16));
-
-	book = new wxTreebook(this, -1, wxDefaultPosition, wxDefaultSize);
-	General(book, this);
-	General_DefaultStyles(book, this);
-	Audio(book, this);
-	Video(book, this);
-	Interface(book, this);
-	Interface_Colours(book, this);
-	new Interface_Hotkeys(book, this);
-	Backup(book, this);
-	Automation(book, this);
-	Advanced(book, this);
-	Advanced_Audio(book, this);
-	Advanced_Video(book, this);
-
-	book->Fit();
-
-	book->ChangeSelection(OPT_GET("Tool/Preferences/Page")->GetInt());
-	book->Bind(wxEVT_TREEBOOK_PAGE_CHANGED, [](wxBookCtrlEvent &evt) {
-		OPT_SET("Tool/Preferences/Page")->SetInt(evt.GetSelection());
-	});
-
-	// Bottom Buttons
-	auto stdButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxAPPLY | wxHELP);
-	applyButton = stdButtonSizer->GetApplyButton();
-	wxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
-	auto defaultButton = new wxButton(this, -1, _("&Restore Defaults"));
-	buttonSizer->Add(defaultButton, wxSizerFlags(0).Expand());
-	buttonSizer->AddStretchSpacer(1);
-	buttonSizer->Add(stdButtonSizer, wxSizerFlags(0).Expand());
-
-	// Main Sizer
-	wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
-	mainSizer->Add(book, wxSizerFlags(1).Expand().Border());
-	mainSizer->Add(buttonSizer, wxSizerFlags(0).Expand().Border(wxALL & ~wxTOP));
-
-	SetSizerAndFit(mainSizer);
-	CenterOnParent();
-
-	applyButton->Enable(false);
-
-	Bind(wxEVT_BUTTON, &Preferences::OnOK, this, wxID_OK);
-	Bind(wxEVT_BUTTON, &Preferences::OnApply, this, wxID_APPLY);
-	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Options"), wxID_HELP);
-	defaultButton->Bind(wxEVT_BUTTON, &Preferences::OnResetDefault, this);
+Preferences::Preferences(wxWindow *parent): wxDialog(parent, -1, _("Preferences"), wxDefaultPosition, wxSize(-1, -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+{
+    SetIcon(GETICON(options_button_16));
+
+    book = new wxTreebook(this, -1, wxDefaultPosition, wxDefaultSize);
+    General(book, this);
+    General_DefaultStyles(book, this);
+    Audio(book, this);
+    Video(book, this);
+    Interface(book, this);
+    Interface_Colours(book, this);
+    new Interface_Hotkeys(book, this);
+    Backup(book, this);
+    Automation(book, this);
+    Advanced(book, this);
+    Advanced_Audio(book, this);
+    Advanced_Video(book, this);
+
+    book->Fit();
+
+    book->ChangeSelection(OPT_GET("Tool/Preferences/Page")->GetInt());
+    book->Bind(wxEVT_TREEBOOK_PAGE_CHANGED, [](wxBookCtrlEvent & evt) {
+        OPT_SET("Tool/Preferences/Page")->SetInt(evt.GetSelection());
+    });
+
+    // Bottom Buttons
+    auto stdButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxAPPLY | wxHELP);
+    applyButton = stdButtonSizer->GetApplyButton();
+    wxSizer *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
+    auto defaultButton = new wxButton(this, -1, _("&Restore Defaults"));
+    buttonSizer->Add(defaultButton, wxSizerFlags(0).Expand());
+    buttonSizer->AddStretchSpacer(1);
+    buttonSizer->Add(stdButtonSizer, wxSizerFlags(0).Expand());
+
+    // Main Sizer
+    wxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
+    mainSizer->Add(book, wxSizerFlags(1).Expand().Border());
+    mainSizer->Add(buttonSizer, wxSizerFlags(0).Expand().Border(wxALL & ~wxTOP));
+
+    SetSizerAndFit(mainSizer);
+    CenterOnParent();
+
+    applyButton->Enable(false);
+
+    Bind(wxEVT_BUTTON, &Preferences::OnOK, this, wxID_OK);
+    Bind(wxEVT_BUTTON, &Preferences::OnApply, this, wxID_APPLY);
+    Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Options"), wxID_HELP);
+    defaultButton->Bind(wxEVT_BUTTON, &Preferences::OnResetDefault, this);
 }
 
-void ShowPreferences(wxWindow *parent) {
-	while (Preferences(parent).ShowModal() < 0);
+void ShowPreferences(wxWindow *parent)
+{
+    while (Preferences(parent).ShowModal() < 0);
 }
diff --git a/src/preferences.h b/src/preferences.h
index e21998c9b350d1616ecb6aa95dacf66e42a6aa5e..9cb14c6565fd92e7a9a2b39a3ed94ba122efb954 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -30,36 +30,36 @@ namespace agi { class OptionValue; }
 
 class Preferences final : public wxDialog {
 public:
-	typedef std::function<void ()> Thunk;
+    typedef std::function<void ()> Thunk;
 private:
-	wxTreebook *book;
-	wxButton *applyButton;
+    wxTreebook *book;
+    wxButton *applyButton;
 
-	std::map<std::string, std::unique_ptr<agi::OptionValue>> pending_changes;
-	std::vector<Thunk> pending_callbacks;
-	std::vector<std::string> option_names;
+    std::map<std::string, std::unique_ptr<agi::OptionValue>> pending_changes;
+    std::vector<Thunk> pending_callbacks;
+    std::vector<std::string> option_names;
 
-	void OnOK(wxCommandEvent &);
-	void OnCancel(wxCommandEvent &);
-	void OnApply(wxCommandEvent &);
-	void OnResetDefault(wxCommandEvent&);
+    void OnOK(wxCommandEvent &);
+    void OnCancel(wxCommandEvent &);
+    void OnApply(wxCommandEvent &);
+    void OnResetDefault(wxCommandEvent &);
 
 public:
-	Preferences(wxWindow *parent);
+    Preferences(wxWindow *parent);
 
-	/// Add an option to be set when the OK or Apply button is clicked
-	/// @param new_value Clone of the option with the new value to copy over
-	void SetOption(std::unique_ptr<agi::OptionValue> new_value);
+    /// Add an option to be set when the OK or Apply button is clicked
+    /// @param new_value Clone of the option with the new value to copy over
+    void SetOption(std::unique_ptr<agi::OptionValue> new_value);
 
-	/// All a function to call when the OK or Apply button is clicked
-	/// @param callback Function to call
-	void AddPendingChange(Thunk const& callback);
+    /// All a function to call when the OK or Apply button is clicked
+    /// @param callback Function to call
+    void AddPendingChange(Thunk const &callback);
 
-	/// Add an option which can be changed via the dialog
-	/// @param name Name of the option
-	///
-	/// This is used for resetting options to the defaults. We don't want to
-	/// simply revert to the default config file as a bunch of things other than
-	/// user options are stored in it. Perhaps that should change in the future.
-	void AddChangeableOption(std::string const& name);
+    /// Add an option which can be changed via the dialog
+    /// @param name Name of the option
+    ///
+    /// This is used for resetting options to the defaults. We don't want to
+    /// simply revert to the default config file as a bunch of things other than
+    /// user options are stored in it. Perhaps that should change in the future.
+    void AddChangeableOption(std::string const &name);
 };
diff --git a/src/preferences_base.cpp b/src/preferences_base.cpp
index cb2c832d79fa9c8e80ee3e70101e3a78f6a873bb..67daf1d21c6af1c60ace9142647da2e8009e46ce 100644
--- a/src/preferences_base.cpp
+++ b/src/preferences_base.cpp
@@ -59,211 +59,222 @@ OPTION_UPDATER(DoubleUpdater, wxSpinDoubleEvent, OptionValueDouble, evt.GetValue
 OPTION_UPDATER(BoolUpdater, wxCommandEvent, OptionValueBool, !!evt.GetInt());
 OPTION_UPDATER(ColourUpdater, ValueEvent<agi::Color>, OptionValueColor, evt.Get());
 
-static void browse_button(wxTextCtrl *ctrl) {
-	wxDirDialog dlg(nullptr, _("Please choose the folder:"), config::path->Decode(from_wx(ctrl->GetValue())).wstring());
-	if (dlg.ShowModal() == wxID_OK) {
-		wxString dir = dlg.GetPath();
-		if (!dir.empty())
-			ctrl->SetValue(dir);
-	}
+static void browse_button(wxTextCtrl *ctrl)
+{
+    wxDirDialog dlg(nullptr, _("Please choose the folder:"), config::path->Decode(from_wx(ctrl->GetValue())).wstring());
+    if (dlg.ShowModal() == wxID_OK) {
+        wxString dir = dlg.GetPath();
+        if (!dir.empty())
+            ctrl->SetValue(dir);
+    }
 }
 
-static void font_button(Preferences *parent, wxTextCtrl *name, wxSpinCtrl *size) {
-	wxFont font;
-	font.SetFaceName(name->GetValue());
-	font.SetPointSize(size->GetValue());
-	font = wxGetFontFromUser(parent, font);
-	if (font.IsOk()) {
-		name->SetValue(font.GetFaceName());
-		size->SetValue(font.GetPointSize());
-		// wxGTK doesn't generate wxEVT_SPINCTRL from SetValue
-		wxSpinEvent evt(wxEVT_SPINCTRL);
-		evt.SetInt(font.GetPointSize());
-		size->ProcessWindowEvent(evt);
-	}
+static void font_button(Preferences *parent, wxTextCtrl *name, wxSpinCtrl *size)
+{
+    wxFont font;
+    font.SetFaceName(name->GetValue());
+    font.SetPointSize(size->GetValue());
+    font = wxGetFontFromUser(parent, font);
+    if (font.IsOk()) {
+        name->SetValue(font.GetFaceName());
+        size->SetValue(font.GetPointSize());
+        // wxGTK doesn't generate wxEVT_SPINCTRL from SetValue
+        wxSpinEvent evt(wxEVT_SPINCTRL);
+        evt.SetInt(font.GetPointSize());
+        size->ProcessWindowEvent(evt);
+    }
 }
 
 OptionPage::OptionPage(wxTreebook *book, Preferences *parent, wxString name, int style)
-: wxScrolled<wxPanel>(book, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL)
-, sizer(new wxBoxSizer(wxVERTICAL))
-, parent(parent)
+    : wxScrolled<wxPanel>(book, -1, wxDefaultPosition, wxDefaultSize, wxVSCROLL)
+    , sizer(new wxBoxSizer(wxVERTICAL))
+    , parent(parent)
 {
-	if (style & PAGE_SUB)
-		book->AddSubPage(this, name, true);
-	else
-		book->AddPage(this, name, true);
-
-	if (style & PAGE_SCROLL)
-		SetScrollbars(0, 20, 0, 50);
-	else
-		SetScrollbars(0, 0, 0, 0);
-	DisableKeyboardScrolling();
+    if (style & PAGE_SUB)
+        book->AddSubPage(this, name, true);
+    else
+        book->AddPage(this, name, true);
+
+    if (style & PAGE_SCROLL)
+        SetScrollbars(0, 20, 0, 50);
+    else
+        SetScrollbars(0, 0, 0, 0);
+    DisableKeyboardScrolling();
 }
 
 template<class T>
-void OptionPage::Add(wxSizer *sizer, wxString const& label, T *control) {
-	sizer->Add(new wxStaticText(this, -1, label), 1, wxALIGN_CENTRE_VERTICAL);
-	sizer->Add(control, wxSizerFlags().Expand());
+void OptionPage::Add(wxSizer *sizer, wxString const &label, T *control)
+{
+    sizer->Add(new wxStaticText(this, -1, label), 1, wxALIGN_CENTRE_VERTICAL);
+    sizer->Add(control, wxSizerFlags().Expand());
 }
 
-void OptionPage::CellSkip(wxFlexGridSizer *flex) {
-	flex->AddStretchSpacer();
+void OptionPage::CellSkip(wxFlexGridSizer *flex)
+{
+    flex->AddStretchSpacer();
 }
 
-wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min, double max, double inc) {
-	parent->AddChangeableOption(opt_name);
-	const auto opt = OPT_GET(opt_name);
-
-	switch (opt->GetType()) {
-		case agi::OptionType::Bool: {
-			auto cb = new wxCheckBox(this, -1, name);
-			flex->Add(cb, 1, wxEXPAND, 0);
-			cb->SetValue(opt->GetBool());
-			cb->Bind(wxEVT_CHECKBOX, BoolUpdater(opt_name, parent));
-			return cb;
-		}
-
-		case agi::OptionType::Int: {
-			auto sc = new wxSpinCtrl(this, -1, std::to_wstring((int)opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetInt());
-			sc->Bind(wxEVT_SPINCTRL, IntUpdater(opt_name, parent));
-			Add(flex, name, sc);
-			return sc;
-		}
-
-		case agi::OptionType::Double: {
-			auto scd = new wxSpinCtrlDouble(this, -1, std::to_wstring(opt->GetDouble()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetDouble(), inc);
-			scd->Bind(wxEVT_SPINCTRLDOUBLE, DoubleUpdater(opt_name, parent));
-			Add(flex, name, scd);
-			return scd;
-		}
-
-		case agi::OptionType::String: {
-			auto text = new wxTextCtrl(this, -1 , to_wx(opt->GetString()));
-			text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
-			Add(flex, name, text);
-			return text;
-		}
-
-		case agi::OptionType::Color: {
-			auto cb = new ColourButton(this, wxSize(40,10), false, opt->GetColor());
-			cb->Bind(EVT_COLOR, ColourUpdater(opt_name, parent));
-			Add(flex, name, cb);
-			return cb;
-		}
-
-		default:
-			throw agi::InternalError("Unsupported type");
-	}
+wxControl *OptionPage::OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min, double max, double inc)
+{
+    parent->AddChangeableOption(opt_name);
+    const auto opt = OPT_GET(opt_name);
+
+    switch (opt->GetType()) {
+    case agi::OptionType::Bool: {
+        auto cb = new wxCheckBox(this, -1, name);
+        flex->Add(cb, 1, wxEXPAND, 0);
+        cb->SetValue(opt->GetBool());
+        cb->Bind(wxEVT_CHECKBOX, BoolUpdater(opt_name, parent));
+        return cb;
+    }
+
+    case agi::OptionType::Int: {
+        auto sc = new wxSpinCtrl(this, -1, std::to_wstring((int)opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetInt());
+        sc->Bind(wxEVT_SPINCTRL, IntUpdater(opt_name, parent));
+        Add(flex, name, sc);
+        return sc;
+    }
+
+    case agi::OptionType::Double: {
+        auto scd = new wxSpinCtrlDouble(this, -1, std::to_wstring(opt->GetDouble()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, opt->GetDouble(), inc);
+        scd->Bind(wxEVT_SPINCTRLDOUBLE, DoubleUpdater(opt_name, parent));
+        Add(flex, name, scd);
+        return scd;
+    }
+
+    case agi::OptionType::String: {
+        auto text = new wxTextCtrl(this, -1, to_wx(opt->GetString()));
+        text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
+        Add(flex, name, text);
+        return text;
+    }
+
+    case agi::OptionType::Color: {
+        auto cb = new ColourButton(this, wxSize(40, 10), false, opt->GetColor());
+        cb->Bind(EVT_COLOR, ColourUpdater(opt_name, parent));
+        Add(flex, name, cb);
+        return cb;
+    }
+
+    default:
+        throw agi::InternalError("Unsupported type");
+    }
 }
 
-void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name) {
-	parent->AddChangeableOption(opt_name);
-	const auto opt = OPT_GET(opt_name);
-
-	auto cb = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY | wxCB_DROPDOWN);
-	Add(flex, name, cb);
-
-	switch (opt->GetType()) {
-		case agi::OptionType::Int: {
-			int val = opt->GetInt();
-			cb->Select(val < (int)choices.size() ? val : 0);
-			cb->Bind(wxEVT_COMBOBOX, IntCBUpdater(opt_name, parent));
-			break;
-		}
-		case agi::OptionType::String: {
-			wxString val(to_wx(opt->GetString()));
-			if (cb->FindString(val) != wxNOT_FOUND)
-				cb->SetStringSelection(val);
-			else if (!choices.empty())
-				cb->SetSelection(0);
-			cb->Bind(wxEVT_COMBOBOX, StringUpdater(opt_name, parent));
-			break;
-		}
-
-		default:
-			throw agi::InternalError("Unsupported type");
-	}
+void OptionPage::OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name)
+{
+    parent->AddChangeableOption(opt_name);
+    const auto opt = OPT_GET(opt_name);
+
+    auto cb = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, choices, wxCB_READONLY | wxCB_DROPDOWN);
+    Add(flex, name, cb);
+
+    switch (opt->GetType()) {
+    case agi::OptionType::Int: {
+        int val = opt->GetInt();
+        cb->Select(val < (int)choices.size() ? val : 0);
+        cb->Bind(wxEVT_COMBOBOX, IntCBUpdater(opt_name, parent));
+        break;
+    }
+    case agi::OptionType::String: {
+        wxString val(to_wx(opt->GetString()));
+        if (cb->FindString(val) != wxNOT_FOUND)
+            cb->SetStringSelection(val);
+        else if (!choices.empty())
+            cb->SetSelection(0);
+        cb->Bind(wxEVT_COMBOBOX, StringUpdater(opt_name, parent));
+        break;
+    }
+
+    default:
+        throw agi::InternalError("Unsupported type");
+    }
 }
 
-wxFlexGridSizer* OptionPage::PageSizer(wxString name) {
-	auto tmp_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, name);
-	sizer->Add(tmp_sizer, 0,wxEXPAND, 5);
-	auto flex = new wxFlexGridSizer(2,5,5);
-	flex->AddGrowableCol(0,1);
-	tmp_sizer->Add(flex, 1, wxEXPAND, 5);
-	sizer->AddSpacer(8);
-	return flex;
+wxFlexGridSizer *OptionPage::PageSizer(wxString name)
+{
+    auto tmp_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, name);
+    sizer->Add(tmp_sizer, 0, wxEXPAND, 5);
+    auto flex = new wxFlexGridSizer(2, 5, 5);
+    flex->AddGrowableCol(0, 1);
+    tmp_sizer->Add(flex, 1, wxEXPAND, 5);
+    sizer->AddSpacer(8);
+    return flex;
 }
 
-void OptionPage::OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler, bool do_enable) {
-	parent->AddChangeableOption(opt_name);
-	const auto opt = OPT_GET(opt_name);
-
-	if (opt->GetType() != agi::OptionType::String)
-		throw agi::InternalError("Option must be agi::OptionType::String for BrowseButton.");
-
-	auto text = new wxTextCtrl(this, -1 , to_wx(opt->GetString()));
-	text->SetMinSize(wxSize(160, -1));
-	text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
-
-	auto browse = new wxButton(this, -1, _("Browse..."));
-	browse->Bind(wxEVT_BUTTON, std::bind(browse_button, text));
-
-	auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
-	button_sizer->Add(text, wxSizerFlags(1).Expand());
-	button_sizer->Add(browse, wxSizerFlags().Expand());
-
-	Add(flex, name, button_sizer);
-
-	if (enabler) {
-		if (do_enable) {
-			EnableIfChecked(enabler, text);
-			EnableIfChecked(enabler, browse);
-		}
-		else {
-			DisableIfChecked(enabler, text);
-			DisableIfChecked(enabler, browse);
-		}
-	}
+void OptionPage::OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler, bool do_enable)
+{
+    parent->AddChangeableOption(opt_name);
+    const auto opt = OPT_GET(opt_name);
+
+    if (opt->GetType() != agi::OptionType::String)
+        throw agi::InternalError("Option must be agi::OptionType::String for BrowseButton.");
+
+    auto text = new wxTextCtrl(this, -1, to_wx(opt->GetString()));
+    text->SetMinSize(wxSize(160, -1));
+    text->Bind(wxEVT_TEXT, StringUpdater(opt_name, parent));
+
+    auto browse = new wxButton(this, -1, _("Browse..."));
+    browse->Bind(wxEVT_BUTTON, std::bind(browse_button, text));
+
+    auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
+    button_sizer->Add(text, wxSizerFlags(1).Expand());
+    button_sizer->Add(browse, wxSizerFlags().Expand());
+
+    Add(flex, name, button_sizer);
+
+    if (enabler) {
+        if (do_enable) {
+            EnableIfChecked(enabler, text);
+            EnableIfChecked(enabler, browse);
+        }
+        else {
+            DisableIfChecked(enabler, text);
+            DisableIfChecked(enabler, browse);
+        }
+    }
 }
 
-void OptionPage::OptionFont(wxSizer *sizer, std::string opt_prefix) {
-	const auto face_opt = OPT_GET(opt_prefix + "Font Face");
-	const auto size_opt = OPT_GET(opt_prefix + "Font Size");
+void OptionPage::OptionFont(wxSizer *sizer, std::string opt_prefix)
+{
+    const auto face_opt = OPT_GET(opt_prefix + "Font Face");
+    const auto size_opt = OPT_GET(opt_prefix + "Font Size");
 
-	parent->AddChangeableOption(face_opt->GetName());
-	parent->AddChangeableOption(size_opt->GetName());
+    parent->AddChangeableOption(face_opt->GetName());
+    parent->AddChangeableOption(size_opt->GetName());
 
-	auto font_name = new wxTextCtrl(this, -1, to_wx(face_opt->GetString()));
-	font_name->SetMinSize(wxSize(160, -1));
-	font_name->Bind(wxEVT_TEXT, StringUpdater(face_opt->GetName().c_str(), parent));
+    auto font_name = new wxTextCtrl(this, -1, to_wx(face_opt->GetString()));
+    font_name->SetMinSize(wxSize(160, -1));
+    font_name->Bind(wxEVT_TEXT, StringUpdater(face_opt->GetName().c_str(), parent));
 
-	auto font_size = new wxSpinCtrl(this, -1, std::to_wstring((int)size_opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 3, 42, size_opt->GetInt());
-	font_size->Bind(wxEVT_SPINCTRL, IntUpdater(size_opt->GetName().c_str(), parent));
+    auto font_size = new wxSpinCtrl(this, -1, std::to_wstring((int)size_opt->GetInt()), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 3, 42, size_opt->GetInt());
+    font_size->Bind(wxEVT_SPINCTRL, IntUpdater(size_opt->GetName().c_str(), parent));
 
-	auto pick_btn = new wxButton(this, -1, _("Choose..."));
-	pick_btn->Bind(wxEVT_BUTTON, std::bind(font_button, parent, font_name, font_size));
+    auto pick_btn = new wxButton(this, -1, _("Choose..."));
+    pick_btn->Bind(wxEVT_BUTTON, std::bind(font_button, parent, font_name, font_size));
 
-	auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
-	button_sizer->Add(font_name, wxSizerFlags(1).Expand());
-	button_sizer->Add(pick_btn, wxSizerFlags().Expand());
+    auto button_sizer = new wxBoxSizer(wxHORIZONTAL);
+    button_sizer->Add(font_name, wxSizerFlags(1).Expand());
+    button_sizer->Add(pick_btn, wxSizerFlags().Expand());
 
-	Add(sizer, _("Font Face"), button_sizer);
-	Add(sizer, _("Font Size"), font_size);
+    Add(sizer, _("Font Face"), button_sizer);
+    Add(sizer, _("Font Size"), font_size);
 }
 
-void OptionPage::EnableIfChecked(wxControl *cbx, wxControl *ctrl) {
-	auto cb = dynamic_cast<wxCheckBox*>(cbx);
-	if (!cb) return;
+void OptionPage::EnableIfChecked(wxControl *cbx, wxControl *ctrl)
+{
+    auto cb = dynamic_cast<wxCheckBox *>(cbx);
+    if (!cb) return;
 
-	ctrl->Enable(cb->IsChecked());
-	cb->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent& evt) { ctrl->Enable(!!evt.GetInt()); evt.Skip(); });
+    ctrl->Enable(cb->IsChecked());
+    cb->Bind(wxEVT_CHECKBOX, [ = ](wxCommandEvent & evt) { ctrl->Enable(!!evt.GetInt()); evt.Skip(); });
 }
 
-void OptionPage::DisableIfChecked(wxControl *cbx, wxControl *ctrl) {
-	auto cb = dynamic_cast<wxCheckBox*>(cbx);
-	if (!cb) return;
+void OptionPage::DisableIfChecked(wxControl *cbx, wxControl *ctrl)
+{
+    auto cb = dynamic_cast<wxCheckBox *>(cbx);
+    if (!cb) return;
 
-	ctrl->Enable(!cb->IsChecked());
-	cb->Bind(wxEVT_CHECKBOX, [=](wxCommandEvent& evt) { ctrl->Enable(!evt.GetInt()); evt.Skip(); });
+    ctrl->Enable(!cb->IsChecked());
+    cb->Bind(wxEVT_CHECKBOX, [ = ](wxCommandEvent & evt) { ctrl->Enable(!evt.GetInt()); evt.Skip(); });
 }
diff --git a/src/preferences_base.h b/src/preferences_base.h
index d88507f9f1d05fe770efcd2dccf8a7dc4d1da731..e9a424535bb753e6ec2161530b81fcc146621d1f 100644
--- a/src/preferences_base.h
+++ b/src/preferences_base.h
@@ -28,29 +28,29 @@ class wxString;
 class wxTreebook;
 
 class OptionPage : public wxScrolled<wxPanel> {
-	template<class T>
-	void Add(wxSizer *sizer, wxString const& label, T *control);
+    template<class T>
+    void Add(wxSizer *sizer, wxString const &label, T *control);
 public:
-	enum Style {
-		PAGE_DEFAULT    =   0x00000000,
-		PAGE_SCROLL     =   0x00000001,
-		PAGE_SUB        =   0x00000002
-	};
-
-	wxSizer *sizer;
-	Preferences *parent;
-	wxFlexGridSizer *PageSizer(wxString name);
-
-	void CellSkip(wxFlexGridSizer *flex);
-	wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min=0, double max=INT_MAX, double inc=1);
-	void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name);
-	void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false);
-	void OptionFont(wxSizer *sizer, std::string opt_prefix);
-
-	/// Enable ctrl only when cbx is checked
-	void EnableIfChecked(wxControl *cbx, wxControl *ctrl);
-	/// Enable ctrl only when cbx is not checked
-	void DisableIfChecked(wxControl *cbx, wxControl *ctrl);
-
-	OptionPage(wxTreebook *book, Preferences *parent, wxString name, int style = PAGE_DEFAULT);
+    enum Style {
+        PAGE_DEFAULT    =   0x00000000,
+        PAGE_SCROLL     =   0x00000001,
+        PAGE_SUB        =   0x00000002
+    };
+
+    wxSizer *sizer;
+    Preferences *parent;
+    wxFlexGridSizer *PageSizer(wxString name);
+
+    void CellSkip(wxFlexGridSizer *flex);
+    wxControl *OptionAdd(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, double min = 0, double max = INT_MAX, double inc = 1);
+    void OptionChoice(wxFlexGridSizer *flex, const wxString &name, const wxArrayString &choices, const char *opt_name);
+    void OptionBrowse(wxFlexGridSizer *flex, const wxString &name, const char *opt_name, wxControl *enabler = nullptr, bool do_enable = false);
+    void OptionFont(wxSizer *sizer, std::string opt_prefix);
+
+    /// Enable ctrl only when cbx is checked
+    void EnableIfChecked(wxControl *cbx, wxControl *ctrl);
+    /// Enable ctrl only when cbx is not checked
+    void DisableIfChecked(wxControl *cbx, wxControl *ctrl);
+
+    OptionPage(wxTreebook *book, Preferences *parent, wxString name, int style = PAGE_DEFAULT);
 };
diff --git a/src/project.cpp b/src/project.cpp
index e01dd3749fdd923d00552f42b09040db8f4e2f27..0e4149db69df0bf6d92299ca87290a687783c832 100644
--- a/src/project.cpp
+++ b/src/project.cpp
@@ -49,492 +49,520 @@
 #include <boost/filesystem/operations.hpp>
 #include <wx/msgdlg.h>
 
-Project::Project(agi::Context *c) : context(c) {
-	OPT_SUB("Audio/Cache/Type", &Project::ReloadAudio, this);
-	OPT_SUB("Audio/Provider", &Project::ReloadAudio, this);
-	OPT_SUB("Provider/Audio/FFmpegSource/Decode Error Handling", &Project::ReloadAudio, this);
-	OPT_SUB("Provider/Avisynth/Allow Ancient", &Project::ReloadVideo, this);
-	OPT_SUB("Provider/Avisynth/Memory Max", &Project::ReloadVideo, this);
-	OPT_SUB("Provider/Video/FFmpegSource/Decoding Threads", &Project::ReloadVideo, this);
-	OPT_SUB("Provider/Video/FFmpegSource/Unsafe Seeking", &Project::ReloadVideo, this);
-	OPT_SUB("Subtitle/Provider", &Project::ReloadVideo, this);
-	OPT_SUB("Video/Provider", &Project::ReloadVideo, this);
+Project::Project(agi::Context *c) : context(c)
+{
+    OPT_SUB("Audio/Cache/Type", &Project::ReloadAudio, this);
+    OPT_SUB("Audio/Provider", &Project::ReloadAudio, this);
+    OPT_SUB("Provider/Audio/FFmpegSource/Decode Error Handling", &Project::ReloadAudio, this);
+    OPT_SUB("Provider/Avisynth/Allow Ancient", &Project::ReloadVideo, this);
+    OPT_SUB("Provider/Avisynth/Memory Max", &Project::ReloadVideo, this);
+    OPT_SUB("Provider/Video/FFmpegSource/Decoding Threads", &Project::ReloadVideo, this);
+    OPT_SUB("Provider/Video/FFmpegSource/Unsafe Seeking", &Project::ReloadVideo, this);
+    OPT_SUB("Subtitle/Provider", &Project::ReloadVideo, this);
+    OPT_SUB("Video/Provider", &Project::ReloadVideo, this);
 }
 
 Project::~Project() { }
 
-void Project::UpdateRelativePaths() {
-	context->ass->Properties.audio_file     = context->path->MakeRelative(audio_file, "?script").generic_string();
-	context->ass->Properties.video_file     = context->path->MakeRelative(video_file, "?script").generic_string();
-	context->ass->Properties.timecodes_file = context->path->MakeRelative(timecodes_file, "?script").generic_string();
-	context->ass->Properties.keyframes_file = context->path->MakeRelative(keyframes_file, "?script").generic_string();
+void Project::UpdateRelativePaths()
+{
+    context->ass->Properties.audio_file     = context->path->MakeRelative(audio_file, "?script").generic_string();
+    context->ass->Properties.video_file     = context->path->MakeRelative(video_file, "?script").generic_string();
+    context->ass->Properties.timecodes_file = context->path->MakeRelative(timecodes_file, "?script").generic_string();
+    context->ass->Properties.keyframes_file = context->path->MakeRelative(keyframes_file, "?script").generic_string();
 }
 
-void Project::ReloadAudio() {
-	if (audio_provider)
-		LoadAudio(audio_file);
+void Project::ReloadAudio()
+{
+    if (audio_provider)
+        LoadAudio(audio_file);
 }
 
-void Project::ReloadVideo() {
-	if (video_provider) {
-		DoLoadVideo(video_file);
-		context->videoController->JumpToFrame(context->videoController->GetFrameN());
-	}
+void Project::ReloadVideo()
+{
+    if (video_provider) {
+        DoLoadVideo(video_file);
+        context->videoController->JumpToFrame(context->videoController->GetFrameN());
+    }
 }
 
-void Project::ShowError(wxString const& message) {
-	wxMessageBox(message, "Error loading file", wxOK | wxICON_ERROR | wxCENTER, context->parent);
+void Project::ShowError(wxString const &message)
+{
+    wxMessageBox(message, "Error loading file", wxOK | wxICON_ERROR | wxCENTER, context->parent);
 }
 
-void Project::ShowError(std::string const& message) {
-	ShowError(to_wx(message));
+void Project::ShowError(std::string const &message)
+{
+    ShowError(to_wx(message));
 }
 
-void Project::SetPath(agi::fs::path& var, const char *token, const char *mru, agi::fs::path const& value) {
-	var = value;
-	if (*token)
-		context->path->SetToken(token, value);
-	if (*mru)
-		config::mru->Add(mru, value);
-	UpdateRelativePaths();
+void Project::SetPath(agi::fs::path &var, const char *token, const char *mru, agi::fs::path const &value)
+{
+    var = value;
+    if (*token)
+        context->path->SetToken(token, value);
+    if (*mru)
+        config::mru->Add(mru, value);
+    UpdateRelativePaths();
 }
 
-bool Project::DoLoadSubtitles(agi::fs::path const& path, std::string encoding, ProjectProperties &properties) {
-	try {
-		if (encoding.empty())
-			encoding = CharSetDetect::GetEncoding(path);
-	}
-	catch (agi::UserCancelException const&) {
-		return false;
-	}
-	catch (agi::fs::FileNotFound const&) {
-		config::mru->Remove("Subtitle", path);
-		ShowError(path.string() + " not found.");
-		return false;
-	}
-
-	if (encoding != "binary") {
-		// Try loading as timecodes and keyframes first since we can't
-		// distinguish them based on filename alone, and just ignore failures
-		// rather than trying to differentiate between malformed timecodes
-		// files and things that aren't timecodes files at all
-		try { DoLoadTimecodes(path); return false; } catch (...) { }
-		try { DoLoadKeyframes(path); return false; } catch (...) { }
-	}
-
-	try {
-		properties = context->subsController->Load(path, encoding);
-	}
-	catch (agi::UserCancelException const&) { return false; }
-	catch (agi::fs::FileNotFound const&) {
-		config::mru->Remove("Subtitle", path);
-		ShowError(path.string() + " not found.");
-		return false;
-	}
-	catch (agi::Exception const& e) {
-		ShowError(e.GetMessage());
-		return false;
-	}
-	catch (std::exception const& e) {
-		ShowError(std::string(e.what()));
-		return false;
-	}
-	catch (...) {
-		ShowError(wxString("Unknown error"));
-		return false;
-	}
-
-	Selection sel;
-	AssDialogue *active_line = nullptr;
-	if (!context->ass->Events.empty()) {
-		int row = mid<int>(0, properties.active_row, context->ass->Events.size() - 1);
-		active_line = &*std::next(context->ass->Events.begin(), row);
-		sel.insert(active_line);
-	}
-	context->selectionController->SetSelectionAndActive(std::move(sel), active_line);
-	context->subsGrid->ScrollTo(properties.scroll_position);
-
-	return true;
+bool Project::DoLoadSubtitles(agi::fs::path const &path, std::string encoding, ProjectProperties &properties)
+{
+    try {
+        if (encoding.empty())
+            encoding = CharSetDetect::GetEncoding(path);
+    }
+    catch (agi::UserCancelException const &) {
+        return false;
+    }
+    catch (agi::fs::FileNotFound const &) {
+        config::mru->Remove("Subtitle", path);
+        ShowError(path.string() + " not found.");
+        return false;
+    }
+
+    if (encoding != "binary") {
+        // Try loading as timecodes and keyframes first since we can't
+        // distinguish them based on filename alone, and just ignore failures
+        // rather than trying to differentiate between malformed timecodes
+        // files and things that aren't timecodes files at all
+        try { DoLoadTimecodes(path); return false; }
+        catch (...) { }
+        try { DoLoadKeyframes(path); return false; }
+        catch (...) { }
+    }
+
+    try {
+        properties = context->subsController->Load(path, encoding);
+    }
+    catch (agi::UserCancelException const &) { return false; }
+    catch (agi::fs::FileNotFound const &) {
+        config::mru->Remove("Subtitle", path);
+        ShowError(path.string() + " not found.");
+        return false;
+    }
+    catch (agi::Exception const &e) {
+        ShowError(e.GetMessage());
+        return false;
+    }
+    catch (std::exception const &e) {
+        ShowError(std::string(e.what()));
+        return false;
+    }
+    catch (...) {
+        ShowError(wxString("Unknown error"));
+        return false;
+    }
+
+    Selection sel;
+    AssDialogue *active_line = nullptr;
+    if (!context->ass->Events.empty()) {
+        int row = mid<int>(0, properties.active_row, context->ass->Events.size() - 1);
+        active_line = &*std::next(context->ass->Events.begin(), row);
+        sel.insert(active_line);
+    }
+    context->selectionController->SetSelectionAndActive(std::move(sel), active_line);
+    context->subsGrid->ScrollTo(properties.scroll_position);
+
+    return true;
 }
 
-void Project::LoadSubtitles(agi::fs::path path, std::string encoding, bool load_linked) {
-	ProjectProperties properties;
-	if (DoLoadSubtitles(path, encoding, properties) && load_linked)
-		LoadUnloadFiles(properties);
+void Project::LoadSubtitles(agi::fs::path path, std::string encoding, bool load_linked)
+{
+    ProjectProperties properties;
+    if (DoLoadSubtitles(path, encoding, properties) && load_linked)
+        LoadUnloadFiles(properties);
 }
 
-void Project::CloseSubtitles() {
-	context->subsController->Close();
-	context->path->SetToken("?script", "");
-	LoadUnloadFiles(context->ass->Properties);
-	auto line = &*context->ass->Events.begin();
-	context->selectionController->SetSelectionAndActive({line}, line);
+void Project::CloseSubtitles()
+{
+    context->subsController->Close();
+    context->path->SetToken("?script", "");
+    LoadUnloadFiles(context->ass->Properties);
+    auto line = &*context->ass->Events.begin();
+    context->selectionController->SetSelectionAndActive({line}, line);
 }
 
-void Project::LoadUnloadFiles(ProjectProperties properties) {
-	auto load_linked = OPT_GET("App/Auto/Load Linked Files")->GetInt();
-	if (!load_linked) return;
-
-	auto audio     = context->path->MakeAbsolute(properties.audio_file, "?script");
-	auto video     = context->path->MakeAbsolute(properties.video_file, "?script");
-	auto timecodes = context->path->MakeAbsolute(properties.timecodes_file, "?script");
-	auto keyframes = context->path->MakeAbsolute(properties.keyframes_file, "?script");
-
-	if (video == video_file && audio == audio_file && keyframes == keyframes_file && timecodes == timecodes_file)
-		return;
-
-	if (load_linked == 2) {
-		wxString str = _("Do you want to load/unload the associated files?");
-		str += "\n";
-
-		auto append_file = [&](agi::fs::path const& p, wxString const& unload, wxString const& load) {
-			if (p.empty())
-				str += "\n" + unload;
-			else
-				str += "\n" + agi::wxformat(load, p);
-		};
-
-		if (audio != audio_file)
-			append_file(audio, _("Unload audio"), _("Load audio file: %s"));
-		if (video != video_file)
-			append_file(video, _("Unload video"), _("Load video file: %s"));
-		if (timecodes != timecodes_file)
-			append_file(timecodes, _("Unload timecodes"), _("Load timecodes file: %s"));
-		if (keyframes != keyframes_file)
-			append_file(keyframes, _("Unload keyframes"), _("Load keyframes file: %s"));
-
-		if (wxMessageBox(str, _("(Un)Load files?"), wxYES_NO | wxCENTRE, context->parent) != wxYES)
-			return;
-	}
-
-	bool loaded_video = false;
-	if (video != video_file) {
-		if (video.empty())
-			CloseVideo();
-		else if ((loaded_video = DoLoadVideo(video))) {
-			auto vc = context->videoController.get();
-			vc->JumpToFrame(properties.video_position);
-
-			auto ar_mode = static_cast<AspectRatio>(properties.ar_mode);
-			if (ar_mode == AspectRatio::Custom)
-				vc->SetAspectRatio(properties.ar_value);
-			else
-				vc->SetAspectRatio(ar_mode);
-			context->videoDisplay->SetZoom(properties.video_zoom);
-		}
-	}
-
-	if (!timecodes.empty()) LoadTimecodes(timecodes);
-	if (!keyframes.empty()) LoadKeyframes(keyframes);
-
-	if (audio != audio_file) {
-		if (audio.empty())
-			CloseAudio();
-		else
-			DoLoadAudio(audio, false);
-	}
-	else if (loaded_video && OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio())
-		DoLoadAudio(video, true);
+void Project::LoadUnloadFiles(ProjectProperties properties)
+{
+    auto load_linked = OPT_GET("App/Auto/Load Linked Files")->GetInt();
+    if (!load_linked) return;
+
+    auto audio     = context->path->MakeAbsolute(properties.audio_file, "?script");
+    auto video     = context->path->MakeAbsolute(properties.video_file, "?script");
+    auto timecodes = context->path->MakeAbsolute(properties.timecodes_file, "?script");
+    auto keyframes = context->path->MakeAbsolute(properties.keyframes_file, "?script");
+
+    if (video == video_file && audio == audio_file && keyframes == keyframes_file && timecodes == timecodes_file)
+        return;
+
+    if (load_linked == 2) {
+        wxString str = _("Do you want to load/unload the associated files?");
+        str += "\n";
+
+        auto append_file = [&](agi::fs::path const & p, wxString const & unload, wxString const & load) {
+            if (p.empty())
+                str += "\n" + unload;
+            else
+                str += "\n" + agi::wxformat(load, p);
+        };
+
+        if (audio != audio_file)
+            append_file(audio, _("Unload audio"), _("Load audio file: %s"));
+        if (video != video_file)
+            append_file(video, _("Unload video"), _("Load video file: %s"));
+        if (timecodes != timecodes_file)
+            append_file(timecodes, _("Unload timecodes"), _("Load timecodes file: %s"));
+        if (keyframes != keyframes_file)
+            append_file(keyframes, _("Unload keyframes"), _("Load keyframes file: %s"));
+
+        if (wxMessageBox(str, _("(Un)Load files?"), wxYES_NO | wxCENTRE, context->parent) != wxYES)
+            return;
+    }
+
+    bool loaded_video = false;
+    if (video != video_file) {
+        if (video.empty())
+            CloseVideo();
+        else if ((loaded_video = DoLoadVideo(video))) {
+            auto vc = context->videoController.get();
+            vc->JumpToFrame(properties.video_position);
+
+            auto ar_mode = static_cast<AspectRatio>(properties.ar_mode);
+            if (ar_mode == AspectRatio::Custom)
+                vc->SetAspectRatio(properties.ar_value);
+            else
+                vc->SetAspectRatio(ar_mode);
+            context->videoDisplay->SetZoom(properties.video_zoom);
+        }
+    }
+
+    if (!timecodes.empty()) LoadTimecodes(timecodes);
+    if (!keyframes.empty()) LoadKeyframes(keyframes);
+
+    if (audio != audio_file) {
+        if (audio.empty())
+            CloseAudio();
+        else
+            DoLoadAudio(audio, false);
+    }
+    else if (loaded_video && OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio())
+        DoLoadAudio(video, true);
 }
 
-void Project::DoLoadAudio(agi::fs::path const& path, bool quiet) {
-	if (!progress)
-		progress = new DialogProgress(context->parent);
-
-	try {
-		try {
-			audio_provider = GetAudioProvider(path, *context->path, progress);
-		}
-		catch (agi::UserCancelException const&) { return; }
-		catch (...) {
-			config::mru->Remove("Audio", path);
-			throw;
-		}
-	}
-	catch (agi::fs::FileNotFound const& e) {
-		return ShowError(_("The audio file was not found: ") + to_wx(e.GetMessage()));
-	}
-	catch (agi::AudioDataNotFound const& e) {
-		if (quiet) {
-			LOG_D("video/open/audio") << "File " << video_file << " has no audio data: " << e.GetMessage();
-			return;
-		}
-		else
-			return ShowError(_("None of the available audio providers recognised the selected file as containing audio data.\n\nThe following providers were tried:\n") + to_wx(e.GetMessage()));
-	}
-	catch (agi::AudioProviderError const& e) {
-		return ShowError(_("None of the available audio providers have a codec available to handle the selected file.\n\nThe following providers were tried:\n") + to_wx(e.GetMessage()));
-	}
-	catch (agi::Exception const& e) {
-		return ShowError(e.GetMessage());
-	}
-
-	SetPath(audio_file, "?audio", "Audio", path);
-	AnnounceAudioProviderModified(audio_provider.get());
+void Project::DoLoadAudio(agi::fs::path const &path, bool quiet)
+{
+    if (!progress)
+        progress = new DialogProgress(context->parent);
+
+    try {
+        try {
+            audio_provider = GetAudioProvider(path, *context->path, progress);
+        }
+        catch (agi::UserCancelException const &) { return; }
+        catch (...) {
+            config::mru->Remove("Audio", path);
+            throw;
+        }
+    }
+    catch (agi::fs::FileNotFound const &e) {
+        return ShowError(_("The audio file was not found: ") + to_wx(e.GetMessage()));
+    }
+    catch (agi::AudioDataNotFound const &e) {
+        if (quiet) {
+            LOG_D("video/open/audio") << "File " << video_file << " has no audio data: " << e.GetMessage();
+            return;
+        }
+        else
+            return ShowError(_("None of the available audio providers recognised the selected file as containing audio data.\n\nThe following providers were tried:\n") + to_wx(e.GetMessage()));
+    }
+    catch (agi::AudioProviderError const &e) {
+        return ShowError(_("None of the available audio providers have a codec available to handle the selected file.\n\nThe following providers were tried:\n") + to_wx(e.GetMessage()));
+    }
+    catch (agi::Exception const &e) {
+        return ShowError(e.GetMessage());
+    }
+
+    SetPath(audio_file, "?audio", "Audio", path);
+    AnnounceAudioProviderModified(audio_provider.get());
 }
 
-void Project::LoadAudio(agi::fs::path path) {
-	DoLoadAudio(path, false);
+void Project::LoadAudio(agi::fs::path path)
+{
+    DoLoadAudio(path, false);
 }
 
-void Project::CloseAudio() {
-	AnnounceAudioProviderModified(nullptr);
-	audio_provider.reset();
-	SetPath(audio_file, "?audio", "", "");
+void Project::CloseAudio()
+{
+    AnnounceAudioProviderModified(nullptr);
+    audio_provider.reset();
+    SetPath(audio_file, "?audio", "", "");
 }
 
-bool Project::DoLoadVideo(agi::fs::path const& path) {
-	if (!progress)
-		progress = new DialogProgress(context->parent);
-
-	try {
-		auto old_matrix = context->ass->GetScriptInfo("YCbCr Matrix");
-		video_provider = agi::make_unique<AsyncVideoProvider>(path, old_matrix, context->videoController.get(), progress);
-	}
-	catch (agi::UserCancelException const&) { return false; }
-	catch (agi::fs::FileSystemError const& err) {
-		config::mru->Remove("Video", path);
-		ShowError(to_wx(err.GetMessage()));
-		return false;
-	}
-	catch (VideoProviderError const& err) {
-		ShowError(to_wx(err.GetMessage()));
-		return false;
-	}
-
-	AnnounceVideoProviderModified(video_provider.get());
-
-	UpdateVideoProperties(context->ass.get(), video_provider.get(), context->parent);
-	video_provider->LoadSubtitles(context->ass.get());
-
-	timecodes = video_provider->GetFPS();
-	keyframes = video_provider->GetKeyFrames();
-
-	timecodes_file.clear();
-	keyframes_file.clear();
-	SetPath(video_file, "?video", "Video", path);
-
-	std::string warning = video_provider->GetWarning();
-	if (!warning.empty())
-		wxMessageBox(to_wx(warning), "Warning", wxICON_WARNING | wxOK);
-
-	video_has_subtitles = false;
-	if (agi::fs::HasExtension(path, "mkv"))
-		video_has_subtitles = MatroskaWrapper::HasSubtitles(path);
-
-	AnnounceKeyframesModified(keyframes);
-	AnnounceTimecodesModified(timecodes);
-	return true;
+bool Project::DoLoadVideo(agi::fs::path const &path)
+{
+    if (!progress)
+        progress = new DialogProgress(context->parent);
+
+    try {
+        auto old_matrix = context->ass->GetScriptInfo("YCbCr Matrix");
+        video_provider = agi::make_unique<AsyncVideoProvider>(path, old_matrix, context->videoController.get(), progress);
+    }
+    catch (agi::UserCancelException const &) { return false; }
+    catch (agi::fs::FileSystemError const &err) {
+        config::mru->Remove("Video", path);
+        ShowError(to_wx(err.GetMessage()));
+        return false;
+    }
+    catch (VideoProviderError const &err) {
+        ShowError(to_wx(err.GetMessage()));
+        return false;
+    }
+
+    AnnounceVideoProviderModified(video_provider.get());
+
+    UpdateVideoProperties(context->ass.get(), video_provider.get(), context->parent);
+    video_provider->LoadSubtitles(context->ass.get());
+
+    timecodes = video_provider->GetFPS();
+    keyframes = video_provider->GetKeyFrames();
+
+    timecodes_file.clear();
+    keyframes_file.clear();
+    SetPath(video_file, "?video", "Video", path);
+
+    std::string warning = video_provider->GetWarning();
+    if (!warning.empty())
+        wxMessageBox(to_wx(warning), "Warning", wxICON_WARNING | wxOK);
+
+    video_has_subtitles = false;
+    if (agi::fs::HasExtension(path, "mkv"))
+        video_has_subtitles = MatroskaWrapper::HasSubtitles(path);
+
+    AnnounceKeyframesModified(keyframes);
+    AnnounceTimecodesModified(timecodes);
+    return true;
 }
 
-void Project::LoadVideo(agi::fs::path path) {
-	if (path.empty()) return;
-	if (!DoLoadVideo(path)) return;
-	if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio())
-		DoLoadAudio(video_file, true);
-
-	double dar = video_provider->GetDAR();
-	if (dar > 0)
-		context->videoController->SetAspectRatio(dar);
-	else
-		context->videoController->SetAspectRatio(AspectRatio::Default);
-	context->videoController->JumpToFrame(0);
+void Project::LoadVideo(agi::fs::path path)
+{
+    if (path.empty()) return;
+    if (!DoLoadVideo(path)) return;
+    if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file && video_provider->HasAudio())
+        DoLoadAudio(video_file, true);
+
+    double dar = video_provider->GetDAR();
+    if (dar > 0)
+        context->videoController->SetAspectRatio(dar);
+    else
+        context->videoController->SetAspectRatio(AspectRatio::Default);
+    context->videoController->JumpToFrame(0);
 }
 
-void Project::CloseVideo() {
-	AnnounceVideoProviderModified(nullptr);
-	video_provider.reset();
-	SetPath(video_file, "?video", "", "");
-	video_has_subtitles = false;
-	context->ass->Properties.ar_mode = 0;
-	context->ass->Properties.ar_value = 0.0;
-	context->ass->Properties.video_position = 0;
+void Project::CloseVideo()
+{
+    AnnounceVideoProviderModified(nullptr);
+    video_provider.reset();
+    SetPath(video_file, "?video", "", "");
+    video_has_subtitles = false;
+    context->ass->Properties.ar_mode = 0;
+    context->ass->Properties.ar_value = 0.0;
+    context->ass->Properties.video_position = 0;
 }
 
-void Project::DoLoadTimecodes(agi::fs::path const& path) {
-	timecodes = agi::vfr::Framerate(path);
-	SetPath(timecodes_file, "", "Timecodes", path);
-	AnnounceTimecodesModified(timecodes);
+void Project::DoLoadTimecodes(agi::fs::path const &path)
+{
+    timecodes = agi::vfr::Framerate(path);
+    SetPath(timecodes_file, "", "Timecodes", path);
+    AnnounceTimecodesModified(timecodes);
 }
 
-void Project::LoadTimecodes(agi::fs::path path) {
-	try {
-		DoLoadTimecodes(path);
-	}
-	catch (agi::fs::FileSystemError const& e) {
-		ShowError(e.GetMessage());
-		config::mru->Remove("Timecodes", path);
-	}
-	catch (agi::vfr::Error const& e) {
-		ShowError("Failed to parse timecodes file: " + e.GetMessage());
-		config::mru->Remove("Timecodes", path);
-	}
+void Project::LoadTimecodes(agi::fs::path path)
+{
+    try {
+        DoLoadTimecodes(path);
+    }
+    catch (agi::fs::FileSystemError const &e) {
+        ShowError(e.GetMessage());
+        config::mru->Remove("Timecodes", path);
+    }
+    catch (agi::vfr::Error const &e) {
+        ShowError("Failed to parse timecodes file: " + e.GetMessage());
+        config::mru->Remove("Timecodes", path);
+    }
 }
 
-void Project::CloseTimecodes() {
-	timecodes = video_provider ? video_provider->GetFPS() : agi::vfr::Framerate{};
-	SetPath(timecodes_file, "", "", "");
-	AnnounceTimecodesModified(timecodes);
+void Project::CloseTimecodes()
+{
+    timecodes = video_provider ? video_provider->GetFPS() : agi::vfr::Framerate{};
+    SetPath(timecodes_file, "", "", "");
+    AnnounceTimecodesModified(timecodes);
 }
 
-void Project::DoLoadKeyframes(agi::fs::path const& path) {
-	keyframes = agi::keyframe::Load(path);
-	SetPath(keyframes_file, "", "Keyframes", path);
-	AnnounceKeyframesModified(keyframes);
+void Project::DoLoadKeyframes(agi::fs::path const &path)
+{
+    keyframes = agi::keyframe::Load(path);
+    SetPath(keyframes_file, "", "Keyframes", path);
+    AnnounceKeyframesModified(keyframes);
 }
 
-void Project::LoadKeyframes(agi::fs::path path) {
-	try {
-		DoLoadKeyframes(path);
-	}
-	catch (agi::fs::FileSystemError const& e) {
-		ShowError(e.GetMessage());
-		config::mru->Remove("Keyframes", path);
-	}
-	catch (agi::keyframe::KeyframeFormatParseError const& e) {
-		ShowError("Failed to parse keyframes file: " + e.GetMessage());
-		config::mru->Remove("Keyframes", path);
-	}
-	catch (agi::keyframe::UnknownKeyframeFormatError const& e) {
-		ShowError("Keyframes file in unknown format: " + e.GetMessage());
-		config::mru->Remove("Keyframes", path);
-	}
+void Project::LoadKeyframes(agi::fs::path path)
+{
+    try {
+        DoLoadKeyframes(path);
+    }
+    catch (agi::fs::FileSystemError const &e) {
+        ShowError(e.GetMessage());
+        config::mru->Remove("Keyframes", path);
+    }
+    catch (agi::keyframe::KeyframeFormatParseError const &e) {
+        ShowError("Failed to parse keyframes file: " + e.GetMessage());
+        config::mru->Remove("Keyframes", path);
+    }
+    catch (agi::keyframe::UnknownKeyframeFormatError const &e) {
+        ShowError("Keyframes file in unknown format: " + e.GetMessage());
+        config::mru->Remove("Keyframes", path);
+    }
 }
 
-void Project::CloseKeyframes() {
-	keyframes = video_provider ? video_provider->GetKeyFrames() : std::vector<int>{};
-	SetPath(keyframes_file, "", "", "");
-	AnnounceKeyframesModified(keyframes);
+void Project::CloseKeyframes()
+{
+    keyframes = video_provider ? video_provider->GetKeyFrames() : std::vector<int> {};
+    SetPath(keyframes_file, "", "", "");
+    AnnounceKeyframesModified(keyframes);
 }
 
-void Project::LoadList(std::vector<agi::fs::path> const& files) {
-	// Keep these lists sorted
-
-	// Video formats
-	const char *videoList[] = {
-		".asf",
-		".avi",
-		".avs",
-		".d2v",
-		".h264",
-		".hevc",
-		".m2ts",
-		".m4v",
-		".mkv",
-		".mov",
-		".mp4",
-		".mpeg",
-		".mpg",
-		".ogm",
-		".rm",
-		".rmvb",
-		".ts",
-		".webm"
-		".wmv",
-		".y4m",
-		".yuv"
-	};
-
-	// Subtitle formats
-	const char *subsList[] = {
-		".ass",
-		".srt",
-		".ssa",
-		".sub",
-		".ttxt"
-	};
-
-	// Audio formats
-	const char *audioList[] = {
-		".aac",
-		".ac3",
-		".ape",
-		".dts",
-		".eac3",
-		".flac",
-		".m4a",
-		".mka",
-		".mp3",
-		".ogg",
-		".opus",
-		".w64",
-		".wav",
-		".wma"
-	};
-
-	auto search = [](const char **begin, const char **end, std::string const& str) {
-		return std::binary_search(begin, end, str.c_str(), [](const char *a, const char *b) {
-			return strcmp(a, b) < 0;
-		});
-	};
-
-	agi::fs::path audio, video, subs, timecodes, keyframes;
-	for (auto file : files) {
-		if (file.is_relative()) file = absolute(file);
-		if (!agi::fs::FileExists(file)) continue;
-
-		auto ext = file.extension().string();
-		boost::to_lower(ext);
-
-		// Could be subtitles, keyframes or timecodes, so try loading as each
-		if (ext == ".txt" || ext == ".log") {
-			if (timecodes.empty()) {
-				try {
-					DoLoadTimecodes(file);
-					timecodes = file;
-					continue;
-				} catch (...) { }
-			}
-
-			if (keyframes.empty()) {
-				try {
-					DoLoadKeyframes(file);
-					keyframes = file;
-					continue;
-				} catch (...) { }
-			}
-
-			if (subs.empty() && ext != ".log")
-				subs = file;
-			continue;
-		}
-
-		if (subs.empty() && search(std::begin(subsList), std::end(subsList), ext))
-			subs = file;
-		if (video.empty() && search(std::begin(videoList), std::end(videoList), ext))
-			video = file;
-		if (audio.empty() && search(std::begin(audioList), std::end(audioList), ext))
-			audio = file;
-	}
-
-	ProjectProperties properties;
-	if (!subs.empty()) {
-		if (!DoLoadSubtitles(subs, "", properties))
-			subs.clear();
-	}
-
-	if (!video.empty() && DoLoadVideo(video)) {
-		double dar = video_provider->GetDAR();
-		if (dar > 0)
-			context->videoController->SetAspectRatio(dar);
-		else
-			context->videoController->SetAspectRatio(AspectRatio::Default);
-		context->videoController->JumpToFrame(0);
-
-		// We loaded these earlier, but loading video unloaded them
-		// Non-Do version of Load in case they've vanished or changed between
-		// then and now
-		if (!timecodes.empty())
-			LoadTimecodes(timecodes);
-		if (!keyframes.empty())
-			LoadKeyframes(keyframes);
-	}
-
-	if (!audio.empty())
-		DoLoadAudio(audio, false);
-	else if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file)
-		DoLoadAudio(video_file, true);
-
-	if (!subs.empty())
-		LoadUnloadFiles(properties);
+void Project::LoadList(std::vector<agi::fs::path> const &files)
+{
+    // Keep these lists sorted
+
+    // Video formats
+    const char *videoList[] = {
+        ".asf",
+        ".avi",
+        ".avs",
+        ".d2v",
+        ".h264",
+        ".hevc",
+        ".m2ts",
+        ".m4v",
+        ".mkv",
+        ".mov",
+        ".mp4",
+        ".mpeg",
+        ".mpg",
+        ".ogm",
+        ".rm",
+        ".rmvb",
+        ".ts",
+        ".webm"
+        ".wmv",
+        ".y4m",
+        ".yuv"
+    };
+
+    // Subtitle formats
+    const char *subsList[] = {
+        ".ass",
+        ".srt",
+        ".ssa",
+        ".sub",
+        ".ttxt"
+    };
+
+    // Audio formats
+    const char *audioList[] = {
+        ".aac",
+        ".ac3",
+        ".ape",
+        ".dts",
+        ".eac3",
+        ".flac",
+        ".m4a",
+        ".mka",
+        ".mp3",
+        ".ogg",
+        ".opus",
+        ".w64",
+        ".wav",
+        ".wma"
+    };
+
+    auto search = [](const char **begin, const char **end, std::string const & str) {
+        return std::binary_search(begin, end, str.c_str(), [](const char *a, const char *b) {
+            return strcmp(a, b) < 0;
+        });
+    };
+
+    agi::fs::path audio, video, subs, timecodes, keyframes;
+    for (auto file : files) {
+        if (file.is_relative()) file = absolute(file);
+        if (!agi::fs::FileExists(file)) continue;
+
+        auto ext = file.extension().string();
+        boost::to_lower(ext);
+
+        // Could be subtitles, keyframes or timecodes, so try loading as each
+        if (ext == ".txt" || ext == ".log") {
+            if (timecodes.empty()) {
+                try {
+                    DoLoadTimecodes(file);
+                    timecodes = file;
+                    continue;
+                }
+                catch (...) { }
+            }
+
+            if (keyframes.empty()) {
+                try {
+                    DoLoadKeyframes(file);
+                    keyframes = file;
+                    continue;
+                }
+                catch (...) { }
+            }
+
+            if (subs.empty() && ext != ".log")
+                subs = file;
+            continue;
+        }
+
+        if (subs.empty() && search(std::begin(subsList), std::end(subsList), ext))
+            subs = file;
+        if (video.empty() && search(std::begin(videoList), std::end(videoList), ext))
+            video = file;
+        if (audio.empty() && search(std::begin(audioList), std::end(audioList), ext))
+            audio = file;
+    }
+
+    ProjectProperties properties;
+    if (!subs.empty()) {
+        if (!DoLoadSubtitles(subs, "", properties))
+            subs.clear();
+    }
+
+    if (!video.empty() && DoLoadVideo(video)) {
+        double dar = video_provider->GetDAR();
+        if (dar > 0)
+            context->videoController->SetAspectRatio(dar);
+        else
+            context->videoController->SetAspectRatio(AspectRatio::Default);
+        context->videoController->JumpToFrame(0);
+
+        // We loaded these earlier, but loading video unloaded them
+        // Non-Do version of Load in case they've vanished or changed between
+        // then and now
+        if (!timecodes.empty())
+            LoadTimecodes(timecodes);
+        if (!keyframes.empty())
+            LoadKeyframes(keyframes);
+    }
+
+    if (!audio.empty())
+        DoLoadAudio(audio, false);
+    else if (OPT_GET("Video/Open Audio")->GetBool() && audio_file != video_file)
+        DoLoadAudio(video_file, true);
+
+    if (!subs.empty())
+        LoadUnloadFiles(properties);
 }
diff --git a/src/project.h b/src/project.h
index 385a41b4b3ca9b36bcda5ccbba2f81adc4ee2734..44d37d8c641ece518027449c3246dbd27f51f75c 100644
--- a/src/project.h
+++ b/src/project.h
@@ -30,73 +30,73 @@ namespace agi { struct Context; }
 struct ProjectProperties;
 
 class Project {
-	std::unique_ptr<agi::AudioProvider> audio_provider;
-	std::unique_ptr<AsyncVideoProvider> video_provider;
-	agi::vfr::Framerate timecodes;
-	std::vector<int> keyframes;
+    std::unique_ptr<agi::AudioProvider> audio_provider;
+    std::unique_ptr<AsyncVideoProvider> video_provider;
+    agi::vfr::Framerate timecodes;
+    std::vector<int> keyframes;
 
-	agi::fs::path audio_file;
-	agi::fs::path video_file;
-	agi::fs::path timecodes_file;
-	agi::fs::path keyframes_file;
+    agi::fs::path audio_file;
+    agi::fs::path video_file;
+    agi::fs::path timecodes_file;
+    agi::fs::path keyframes_file;
 
-	agi::signal::Signal<agi::AudioProvider *> AnnounceAudioProviderModified;
-	agi::signal::Signal<AsyncVideoProvider *> AnnounceVideoProviderModified;
-	agi::signal::Signal<agi::vfr::Framerate const&> AnnounceTimecodesModified;
-	agi::signal::Signal<std::vector<int> const&> AnnounceKeyframesModified;
+    agi::signal::Signal<agi::AudioProvider *> AnnounceAudioProviderModified;
+    agi::signal::Signal<AsyncVideoProvider *> AnnounceVideoProviderModified;
+    agi::signal::Signal<agi::vfr::Framerate const &> AnnounceTimecodesModified;
+    agi::signal::Signal<std::vector<int> const &> AnnounceKeyframesModified;
 
-	bool video_has_subtitles = false;
-	DialogProgress *progress = nullptr;
-	agi::Context *context = nullptr;
+    bool video_has_subtitles = false;
+    DialogProgress *progress = nullptr;
+    agi::Context *context = nullptr;
 
-	void ShowError(wxString const& message);
-	void ShowError(std::string const& message);
+    void ShowError(wxString const &message);
+    void ShowError(std::string const &message);
 
-	bool DoLoadSubtitles(agi::fs::path const& path, std::string encoding, ProjectProperties &properties);
-	void DoLoadAudio(agi::fs::path const& path, bool quiet);
-	bool DoLoadVideo(agi::fs::path const& path);
-	void DoLoadTimecodes(agi::fs::path const& path);
-	void DoLoadKeyframes(agi::fs::path const& path);
+    bool DoLoadSubtitles(agi::fs::path const &path, std::string encoding, ProjectProperties &properties);
+    void DoLoadAudio(agi::fs::path const &path, bool quiet);
+    bool DoLoadVideo(agi::fs::path const &path);
+    void DoLoadTimecodes(agi::fs::path const &path);
+    void DoLoadKeyframes(agi::fs::path const &path);
 
-	void LoadUnloadFiles(ProjectProperties properties);
-	void UpdateRelativePaths();
-	void ReloadAudio();
-	void ReloadVideo();
+    void LoadUnloadFiles(ProjectProperties properties);
+    void UpdateRelativePaths();
+    void ReloadAudio();
+    void ReloadVideo();
 
-	void SetPath(agi::fs::path& var, const char *token, const char *mru, agi::fs::path const& value);
+    void SetPath(agi::fs::path &var, const char *token, const char *mru, agi::fs::path const &value);
 
 public:
-	Project(agi::Context *context);
-	~Project();
-
-	void LoadSubtitles(agi::fs::path path, std::string encoding="", bool load_linked=true);
-	void CloseSubtitles();
-	bool CanLoadSubtitlesFromVideo() const { return video_has_subtitles; }
-
-	void LoadAudio(agi::fs::path path);
-	void CloseAudio();
-	agi::AudioProvider *AudioProvider() const { return audio_provider.get(); }
-	agi::fs::path const& AudioName() const { return audio_file; }
-
-	void LoadVideo(agi::fs::path path);
-	void CloseVideo();
-	AsyncVideoProvider *VideoProvider() const { return video_provider.get(); }
-	agi::fs::path const& VideoName() const { return video_file; }
-
-	void LoadTimecodes(agi::fs::path path);
-	void CloseTimecodes();
-	bool CanCloseTimecodes() const { return !timecodes_file.empty(); }
-	agi::vfr::Framerate const& Timecodes() const { return timecodes; }
-
-	void LoadKeyframes(agi::fs::path path);
-	void CloseKeyframes();
-	bool CanCloseKeyframes() const { return !keyframes_file.empty(); }
-	std::vector<int> const& Keyframes() const { return keyframes; }
-
-	void LoadList(std::vector<agi::fs::path> const& files);
-
-	DEFINE_SIGNAL_ADDERS(AnnounceAudioProviderModified, AddAudioProviderListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceVideoProviderModified, AddVideoProviderListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceTimecodesModified, AddTimecodesListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceKeyframesModified, AddKeyframesListener)
+    Project(agi::Context *context);
+    ~Project();
+
+    void LoadSubtitles(agi::fs::path path, std::string encoding = "", bool load_linked = true);
+    void CloseSubtitles();
+    bool CanLoadSubtitlesFromVideo() const { return video_has_subtitles; }
+
+    void LoadAudio(agi::fs::path path);
+    void CloseAudio();
+    agi::AudioProvider *AudioProvider() const { return audio_provider.get(); }
+    agi::fs::path const &AudioName() const { return audio_file; }
+
+    void LoadVideo(agi::fs::path path);
+    void CloseVideo();
+    AsyncVideoProvider *VideoProvider() const { return video_provider.get(); }
+    agi::fs::path const &VideoName() const { return video_file; }
+
+    void LoadTimecodes(agi::fs::path path);
+    void CloseTimecodes();
+    bool CanCloseTimecodes() const { return !timecodes_file.empty(); }
+    agi::vfr::Framerate const &Timecodes() const { return timecodes; }
+
+    void LoadKeyframes(agi::fs::path path);
+    void CloseKeyframes();
+    bool CanCloseKeyframes() const { return !keyframes_file.empty(); }
+    std::vector<int> const &Keyframes() const { return keyframes; }
+
+    void LoadList(std::vector<agi::fs::path> const &files);
+
+    DEFINE_SIGNAL_ADDERS(AnnounceAudioProviderModified, AddAudioProviderListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceVideoProviderModified, AddVideoProviderListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceTimecodesModified, AddTimecodesListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceKeyframesModified, AddKeyframesListener)
 };
diff --git a/src/resolution_resampler.cpp b/src/resolution_resampler.cpp
index e5c0b56f9a463ae11348c4678c41bde4571d9f14..8d1c56f13278cdc1bcc493da48cf28b6412443e9 100644
--- a/src/resolution_resampler.cpp
+++ b/src/resolution_resampler.cpp
@@ -33,258 +33,268 @@
 #include <wx/intl.h>
 
 enum {
-	LEFT = 0,
-	RIGHT = 1,
-	TOP = 2,
-	BOTTOM = 3
+    LEFT = 0,
+    RIGHT = 1,
+    TOP = 2,
+    BOTTOM = 3
 };
 
 static const std::string names[] = {
-	"None",
-	"TV.601", "PC.601",
-	"TV.709", "PC.709",
-	"TV.FCC", "PC.FCC",
-	"TV.240M", "PC.240M"
+    "None",
+    "TV.601", "PC.601",
+    "TV.709", "PC.709",
+    "TV.FCC", "PC.FCC",
+    "TV.240M", "PC.240M"
 };
 
-YCbCrMatrix MatrixFromString(std::string const& str) {
-	if (str.empty()) return YCbCrMatrix::tv_709;
-	auto pos = std::find(std::begin(names), std::end(names), str);
-	if (pos == std::end(names))
-		return YCbCrMatrix::rgb;
-	return static_cast<YCbCrMatrix>(std::distance(std::begin(names), pos));
+YCbCrMatrix MatrixFromString(std::string const &str)
+{
+    if (str.empty()) return YCbCrMatrix::tv_709;
+    auto pos = std::find(std::begin(names), std::end(names), str);
+    if (pos == std::end(names))
+        return YCbCrMatrix::rgb;
+    return static_cast<YCbCrMatrix>(std::distance(std::begin(names), pos));
 }
 
-std::string MatrixToString(YCbCrMatrix mat) {
-	return names[static_cast<int>(mat)];
+std::string MatrixToString(YCbCrMatrix mat)
+{
+    return names[static_cast<int>(mat)];
 }
 
-std::vector<std::string> MatrixNames() {
-	return {std::begin(names), std::end(names)};
+std::vector<std::string> MatrixNames()
+{
+    return {std::begin(names), std::end(names)};
 }
 
 namespace {
-	std::string transform_drawing(std::string const& drawing, int shift_x, int shift_y, double scale_x, double scale_y) {
-		bool is_x = true;
-		std::string final;
-		final.reserve(drawing.size());
-
-		for (auto const& cur : agi::Split(drawing, ' ')) {
-			double val;
-			if (agi::util::try_parse(agi::str(cur), &val)) {
-				if (is_x)
-					val = (val + shift_x) * scale_x;
-				else
-					val = (val + shift_y) * scale_y;
-				val = round(val * 8) / 8.0; // round to eighth-pixels
-				final += float_to_string(val);
-				final += ' ';
-				is_x = !is_x;
-			}
-			else if (cur.size() == 1) {
-				char c = tolower(cur[0]);
-				if (c == 'm' || c == 'n' || c == 'l' || c == 'b' || c == 's' || c == 'p' || c == 'c') {
-					is_x = true;
-					final += c;
-					final += ' ';
-				}
-			}
-		}
-
-		if (final.size())
-			final.pop_back();
-		return final;
-	}
-
-	struct resample_state {
-		const int *margin;
-		double rx;
-		double ry;
-		double ar;
-		agi::ycbcr_converter conv;
-		bool convert_colors;
-	};
-
-	void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) {
-		resample_state *state = static_cast<resample_state *>(ud);
-
-		double resizer = 1.0;
-		int shift = 0;
-
-		switch (cur->classification) {
-			case AssParameterClass::ABSOLUTE_SIZE:
-				resizer = state->ry;
-				break;
-
-			case AssParameterClass::ABSOLUTE_POS_X:
-				resizer = state->rx;
-				shift = state->margin[LEFT];
-				break;
-
-			case AssParameterClass::ABSOLUTE_POS_Y:
-				resizer = state->ry;
-				shift = state->margin[TOP];
-				break;
-
-			case AssParameterClass::RELATIVE_SIZE_X:
-				resizer = state->ar;
-				break;
-
-			case AssParameterClass::RELATIVE_SIZE_Y:
-				break;
-
-			case AssParameterClass::DRAWING: {
-				cur->Set(transform_drawing(
-					cur->Get<std::string>(),
-					state->margin[LEFT], state->margin[TOP], state->rx, state->ry));
-				return;
-			}
-
-			case AssParameterClass::COLOR:
-				if (state->convert_colors)
-					cur->Set<std::string>(state->conv.rgb_to_rgb(agi::Color{cur->Get<std::string>()}).GetAssOverrideFormatted());
-				return;
-
-			default:
-				return;
-		}
-
-		VariableDataType curType = cur->GetType();
-		if (curType == VariableDataType::FLOAT)
-			cur->Set((cur->Get<double>() + shift) * resizer);
-		else if (curType == VariableDataType::INT)
-			cur->Set<int>((cur->Get<int>() + shift) * resizer + 0.5);
-	}
-
-	void resample_line(resample_state *state, AssDialogue &diag) {
-		if (diag.Comment && (boost::starts_with(diag.Effect.get(), "template") || boost::starts_with(diag.Effect.get(), "code")))
-			return;
-
-		auto blocks = diag.ParseTags();
-
-		for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
-			block->ProcessParameters(resample_tags, state);
-
-		for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
-			drawing->text = transform_drawing(drawing->text, 0, 0, state->rx / state->ar, state->ry);
-
-		for (size_t i = 0; i < 3; ++i) {
-			if (diag.Margin[i])
-				diag.Margin[i] = int((diag.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
-		}
-
-		diag.UpdateText(blocks);
-	}
-
-	void resample_style(resample_state *state, AssStyle &style) {
-		style.fontsize = int(style.fontsize * state->ry + 0.5);
-		style.outline_w *= state->ry;
-		style.shadow_w *= state->ry;
-		style.spacing *= state->rx;
-		style.scalex *= state->ar;
-		for (int i = 0; i < 3; i++)
-			style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
-		if (state->convert_colors) {
-			style.primary = state->conv.rgb_to_rgb(style.primary);
-			style.secondary = state->conv.rgb_to_rgb(style.secondary);
-			style.outline = state->conv.rgb_to_rgb(style.outline);
-			style.shadow = state->conv.rgb_to_rgb(style.shadow);
-		}
-		style.UpdateData();
-	}
-
-	agi::ycbcr_matrix matrix(YCbCrMatrix mat) {
-		switch (mat) {
-			case YCbCrMatrix::rgb: return agi::ycbcr_matrix::bt601;
-			case YCbCrMatrix::tv_601:  case YCbCrMatrix::pc_601:  return agi::ycbcr_matrix::bt601;
-			case YCbCrMatrix::tv_709:  case YCbCrMatrix::pc_709:  return agi::ycbcr_matrix::bt709;
-			case YCbCrMatrix::tv_fcc:  case YCbCrMatrix::pc_fcc:  return agi::ycbcr_matrix::fcc;
-			case YCbCrMatrix::tv_240m: case YCbCrMatrix::pc_240m: return agi::ycbcr_matrix::smpte_240m;
-		}
-		throw agi::InternalError("Invalid matrix");
-	}
-
-	agi::ycbcr_range range(YCbCrMatrix mat) {
-		switch (mat) {
-			case YCbCrMatrix::rgb:
-			case YCbCrMatrix::tv_601:
-			case YCbCrMatrix::tv_709:
-			case YCbCrMatrix::tv_fcc:
-			case YCbCrMatrix::tv_240m:
-				return agi::ycbcr_range::tv;
-			case YCbCrMatrix::pc_601:
-			case YCbCrMatrix::pc_709:
-			case YCbCrMatrix::pc_fcc:
-			case YCbCrMatrix::pc_240m:
-				return agi::ycbcr_range::pc;
-		}
-		throw agi::InternalError("Invalid matrix");
-	}
+std::string transform_drawing(std::string const &drawing, int shift_x, int shift_y, double scale_x, double scale_y)
+{
+    bool is_x = true;
+    std::string final;
+    final.reserve(drawing.size());
+
+    for (auto const &cur : agi::Split(drawing, ' ')) {
+        double val;
+        if (agi::util::try_parse(agi::str(cur), &val)) {
+            if (is_x)
+                val = (val + shift_x) * scale_x;
+            else
+                val = (val + shift_y) * scale_y;
+            val = round(val * 8) / 8.0; // round to eighth-pixels
+            final += float_to_string(val);
+            final += ' ';
+            is_x = !is_x;
+        }
+        else if (cur.size() == 1) {
+            char c = tolower(cur[0]);
+            if (c == 'm' || c == 'n' || c == 'l' || c == 'b' || c == 's' || c == 'p' || c == 'c') {
+                is_x = true;
+                final += c;
+                final += ' ';
+            }
+        }
+    }
+
+    if (final.size())
+        final.pop_back();
+    return final;
 }
 
-void ResampleResolution(AssFile *ass, ResampleSettings settings) {
-	auto horizontal_stretch = 1.0;
-	auto old_ar = double(settings.source_x) / settings.source_y;
-	auto new_ar = double(settings.dest_x) / settings.dest_y;
-	bool border_horizontally = new_ar > old_ar;
-	// Don't convert aspect ratio if it's very close to correct
-	// (for reference, 848x480 <-> 1280x720 is .006)
-	if (std::abs(old_ar - new_ar) / new_ar > .01) {
-		switch (settings.ar_mode) {
-		case ResampleARMode::RemoveBorder:
-			border_horizontally = !border_horizontally;
-		case ResampleARMode::AddBorder:
-			if (border_horizontally) // Wider/Shorter
-				settings.margin[LEFT] = settings.margin[RIGHT] = (settings.source_y * new_ar - settings.source_x) / 2;
-			else // Taller/Narrower
-				settings.margin[TOP] = settings.margin[BOTTOM] = (settings.source_x / new_ar - settings.source_y) / 2;
-			break;
-		case ResampleARMode::Stretch:
-			horizontal_stretch = new_ar / old_ar;
-			break;
-		case ResampleARMode::Manual:
-			old_ar =
-				double(settings.source_x + settings.margin[LEFT] + settings.margin[RIGHT]) /
-				double(settings.source_y + settings.margin[TOP] + settings.margin[BOTTOM]);
-
-			if (std::abs(old_ar - new_ar) / new_ar > .01)
-				horizontal_stretch = new_ar / old_ar;
-			break;
-		}
-	}
-
-	// Add margins to original resolution
-	settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT];
-	settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM];
-
-	bool resample_colors =
-		settings.source_matrix != settings.dest_matrix &&
-		settings.source_matrix != YCbCrMatrix::rgb &&
-		settings.dest_matrix != YCbCrMatrix::rgb;
-
-	resample_state state = {
-		settings.margin,
-		double(settings.dest_x) / double(settings.source_x),
-		double(settings.dest_y) / double(settings.source_y),
-		horizontal_stretch,
-		agi::ycbcr_converter{
-			matrix(settings.source_matrix),
-			range(settings.source_matrix),
-			matrix(settings.dest_matrix),
-			range(settings.dest_matrix),
-		},
-		resample_colors
-	};
-
-	for (auto& line : ass->Styles)
-		resample_style(&state, line);
-	for (auto& line : ass->Events)
-		resample_line(&state, line);
-
-	ass->SetScriptInfo("PlayResX", std::to_string(settings.dest_x));
-	ass->SetScriptInfo("PlayResY", std::to_string(settings.dest_y));
-	if (resample_colors)
-		ass->SetScriptInfo("YCbCr Matrix", MatrixToString(settings.dest_matrix));
-
-	ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
+struct resample_state {
+    const int *margin;
+    double rx;
+    double ry;
+    double ar;
+    agi::ycbcr_converter conv;
+    bool convert_colors;
+};
+
+void resample_tags(std::string const &name, AssOverrideParameter *cur, void *ud)
+{
+    resample_state *state = static_cast<resample_state *>(ud);
+
+    double resizer = 1.0;
+    int shift = 0;
+
+    switch (cur->classification) {
+    case AssParameterClass::ABSOLUTE_SIZE:
+        resizer = state->ry;
+        break;
+
+    case AssParameterClass::ABSOLUTE_POS_X:
+        resizer = state->rx;
+        shift = state->margin[LEFT];
+        break;
+
+    case AssParameterClass::ABSOLUTE_POS_Y:
+        resizer = state->ry;
+        shift = state->margin[TOP];
+        break;
+
+    case AssParameterClass::RELATIVE_SIZE_X:
+        resizer = state->ar;
+        break;
+
+    case AssParameterClass::RELATIVE_SIZE_Y:
+        break;
+
+    case AssParameterClass::DRAWING: {
+        cur->Set(transform_drawing(
+                     cur->Get<std::string>(),
+                     state->margin[LEFT], state->margin[TOP], state->rx, state->ry));
+        return;
+    }
+
+    case AssParameterClass::COLOR:
+        if (state->convert_colors)
+            cur->Set<std::string>(state->conv.rgb_to_rgb(agi::Color{cur->Get<std::string>()}).GetAssOverrideFormatted());
+        return;
+
+    default:
+        return;
+    }
+
+    VariableDataType curType = cur->GetType();
+    if (curType == VariableDataType::FLOAT)
+        cur->Set((cur->Get<double>() + shift) * resizer);
+    else if (curType == VariableDataType::INT)
+        cur->Set<int>((cur->Get<int>() + shift) * resizer + 0.5);
+}
+
+void resample_line(resample_state *state, AssDialogue &diag)
+{
+    if (diag.Comment && (boost::starts_with(diag.Effect.get(), "template") || boost::starts_with(diag.Effect.get(), "code")))
+        return;
+
+    auto blocks = diag.ParseTags();
+
+    for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
+        block->ProcessParameters(resample_tags, state);
+
+    for (auto drawing : blocks | agi::of_type<AssDialogueBlockDrawing>())
+        drawing->text = transform_drawing(drawing->text, 0, 0, state->rx / state->ar, state->ry);
+
+    for (size_t i = 0; i < 3; ++i) {
+        if (diag.Margin[i])
+            diag.Margin[i] = int((diag.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
+    }
+
+    diag.UpdateText(blocks);
+}
+
+void resample_style(resample_state *state, AssStyle &style)
+{
+    style.fontsize = int(style.fontsize * state->ry + 0.5);
+    style.outline_w *= state->ry;
+    style.shadow_w *= state->ry;
+    style.spacing *= state->rx;
+    style.scalex *= state->ar;
+    for (int i = 0; i < 3; i++)
+        style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5);
+    if (state->convert_colors) {
+        style.primary = state->conv.rgb_to_rgb(style.primary);
+        style.secondary = state->conv.rgb_to_rgb(style.secondary);
+        style.outline = state->conv.rgb_to_rgb(style.outline);
+        style.shadow = state->conv.rgb_to_rgb(style.shadow);
+    }
+    style.UpdateData();
+}
+
+agi::ycbcr_matrix matrix(YCbCrMatrix mat)
+{
+    switch (mat) {
+    case YCbCrMatrix::rgb: return agi::ycbcr_matrix::bt601;
+    case YCbCrMatrix::tv_601:  case YCbCrMatrix::pc_601:  return agi::ycbcr_matrix::bt601;
+    case YCbCrMatrix::tv_709:  case YCbCrMatrix::pc_709:  return agi::ycbcr_matrix::bt709;
+    case YCbCrMatrix::tv_fcc:  case YCbCrMatrix::pc_fcc:  return agi::ycbcr_matrix::fcc;
+    case YCbCrMatrix::tv_240m: case YCbCrMatrix::pc_240m: return agi::ycbcr_matrix::smpte_240m;
+    }
+    throw agi::InternalError("Invalid matrix");
+}
+
+agi::ycbcr_range range(YCbCrMatrix mat)
+{
+    switch (mat) {
+    case YCbCrMatrix::rgb:
+    case YCbCrMatrix::tv_601:
+    case YCbCrMatrix::tv_709:
+    case YCbCrMatrix::tv_fcc:
+    case YCbCrMatrix::tv_240m:
+        return agi::ycbcr_range::tv;
+    case YCbCrMatrix::pc_601:
+    case YCbCrMatrix::pc_709:
+    case YCbCrMatrix::pc_fcc:
+    case YCbCrMatrix::pc_240m:
+        return agi::ycbcr_range::pc;
+    }
+    throw agi::InternalError("Invalid matrix");
+}
+}
+
+void ResampleResolution(AssFile *ass, ResampleSettings settings)
+{
+    auto horizontal_stretch = 1.0;
+    auto old_ar = double(settings.source_x) / settings.source_y;
+    auto new_ar = double(settings.dest_x) / settings.dest_y;
+    bool border_horizontally = new_ar > old_ar;
+    // Don't convert aspect ratio if it's very close to correct
+    // (for reference, 848x480 <-> 1280x720 is .006)
+    if (std::abs(old_ar - new_ar) / new_ar > .01) {
+        switch (settings.ar_mode) {
+        case ResampleARMode::RemoveBorder:
+            border_horizontally = !border_horizontally;
+        case ResampleARMode::AddBorder:
+            if (border_horizontally) // Wider/Shorter
+                settings.margin[LEFT] = settings.margin[RIGHT] = (settings.source_y * new_ar - settings.source_x) / 2;
+            else // Taller/Narrower
+                settings.margin[TOP] = settings.margin[BOTTOM] = (settings.source_x / new_ar - settings.source_y) / 2;
+            break;
+        case ResampleARMode::Stretch:
+            horizontal_stretch = new_ar / old_ar;
+            break;
+        case ResampleARMode::Manual:
+            old_ar =
+                double(settings.source_x + settings.margin[LEFT] + settings.margin[RIGHT]) /
+                double(settings.source_y + settings.margin[TOP] + settings.margin[BOTTOM]);
+
+            if (std::abs(old_ar - new_ar) / new_ar > .01)
+                horizontal_stretch = new_ar / old_ar;
+            break;
+        }
+    }
+
+    // Add margins to original resolution
+    settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT];
+    settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM];
+
+    bool resample_colors =
+        settings.source_matrix != settings.dest_matrix &&
+        settings.source_matrix != YCbCrMatrix::rgb &&
+        settings.dest_matrix != YCbCrMatrix::rgb;
+
+    resample_state state = {
+        settings.margin,
+        double(settings.dest_x) / double(settings.source_x),
+        double(settings.dest_y) / double(settings.source_y),
+        horizontal_stretch,
+        agi::ycbcr_converter{
+            matrix(settings.source_matrix),
+            range(settings.source_matrix),
+            matrix(settings.dest_matrix),
+            range(settings.dest_matrix),
+        },
+        resample_colors
+    };
+
+    for (auto &line : ass->Styles)
+        resample_style(&state, line);
+    for (auto &line : ass->Events)
+        resample_line(&state, line);
+
+    ass->SetScriptInfo("PlayResX", std::to_string(settings.dest_x));
+    ass->SetScriptInfo("PlayResY", std::to_string(settings.dest_y));
+    if (resample_colors)
+        ass->SetScriptInfo("YCbCr Matrix", MatrixToString(settings.dest_matrix));
+
+    ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL);
 }
diff --git a/src/resolution_resampler.h b/src/resolution_resampler.h
index f2975e87a41842f7aa1e648f7b862e2ccef68e25..caf7e5ad0c577b2ba96507ade0ed0ce402c0343f 100644
--- a/src/resolution_resampler.h
+++ b/src/resolution_resampler.h
@@ -20,38 +20,38 @@
 class AssFile;
 
 enum class ResampleARMode {
-	Stretch,
-	AddBorder,
-	RemoveBorder,
-	Manual
+    Stretch,
+    AddBorder,
+    RemoveBorder,
+    Manual
 };
 
 enum class YCbCrMatrix : int {
-	rgb,
-	tv_601,
-	pc_601,
-	tv_709,
-	pc_709,
-	tv_fcc,
-	pc_fcc,
-	tv_240m,
-	pc_240m
+    rgb,
+    tv_601,
+    pc_601,
+    tv_709,
+    pc_709,
+    tv_fcc,
+    pc_fcc,
+    tv_240m,
+    pc_240m
 };
 
-YCbCrMatrix MatrixFromString(std::string const& str);
+YCbCrMatrix MatrixFromString(std::string const &str);
 std::string MatrixToString(YCbCrMatrix mat);
 std::vector<std::string> MatrixNames();
 
 /// Configuration parameters for a resample
 struct ResampleSettings {
-	int margin[4];  ///< Amount to add to each margin
-	int source_x;   ///< Original  X resolution
-	int source_y;   ///< Original Y resolution
-	int dest_x;     ///< New X resolution
-	int dest_y;     ///< New Y resolution
-	ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match
-	YCbCrMatrix source_matrix;
-	YCbCrMatrix dest_matrix;
+    int margin[4];  ///< Amount to add to each margin
+    int source_x;   ///< Original  X resolution
+    int source_y;   ///< Original Y resolution
+    int dest_x;     ///< New X resolution
+    int dest_y;     ///< New Y resolution
+    ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match
+    YCbCrMatrix source_matrix;
+    YCbCrMatrix dest_matrix;
 };
 
 /// Resample the subtitles in the project
diff --git a/src/retina_helper.h b/src/retina_helper.h
index 389036d0f260c17e13277b8bc6e127b4b74f4475..612b093ac4a69406426530633f79c7e194c7343b 100644
--- a/src/retina_helper.h
+++ b/src/retina_helper.h
@@ -19,12 +19,12 @@
 class wxWindow;
 
 class RetinaHelper {
-	agi::signal::Signal<int> ScaleFactorChanged;
+    agi::signal::Signal<int> ScaleFactorChanged;
 public:
-	RetinaHelper(wxWindow *window);
-	~RetinaHelper();
+    RetinaHelper(wxWindow *window);
+    ~RetinaHelper();
 
-	int GetScaleFactor() const;
-	DEFINE_SIGNAL_ADDERS(ScaleFactorChanged, AddScaleFactorListener)
+    int GetScaleFactor() const;
+    DEFINE_SIGNAL_ADDERS(ScaleFactorChanged, AddScaleFactorListener)
 };
 
diff --git a/src/search_replace_engine.cpp b/src/search_replace_engine.cpp
index 14c71680da164ba266f0a01fefba82c235f28284..bc9142a5011aee906d08e3abcee429b020889aa8 100644
--- a/src/search_replace_engine.cpp
+++ b/src/search_replace_engine.cpp
@@ -34,253 +34,263 @@ namespace {
 static const size_t bad_pos = -1;
 static const MatchState bad_match{nullptr, 0, bad_pos};
 
-auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogueBase::Text) {
-	switch (field) {
-		case SearchReplaceSettings::Field::TEXT: return &AssDialogueBase::Text;
-		case SearchReplaceSettings::Field::STYLE: return &AssDialogueBase::Style;
-		case SearchReplaceSettings::Field::ACTOR: return &AssDialogueBase::Actor;
-		case SearchReplaceSettings::Field::EFFECT: return &AssDialogueBase::Effect;
-	}
-	throw agi::InternalError("Bad field for search");
+auto get_dialogue_field(SearchReplaceSettings::Field field) -> decltype(&AssDialogueBase::Text)
+{
+    switch (field) {
+    case SearchReplaceSettings::Field::TEXT: return &AssDialogueBase::Text;
+    case SearchReplaceSettings::Field::STYLE: return &AssDialogueBase::Style;
+    case SearchReplaceSettings::Field::ACTOR: return &AssDialogueBase::Actor;
+    case SearchReplaceSettings::Field::EFFECT: return &AssDialogueBase::Effect;
+    }
+    throw agi::InternalError("Bad field for search");
 }
 
-std::string const& get_normalized(const AssDialogue *diag, decltype(&AssDialogueBase::Text) field) {
-	auto& value = const_cast<AssDialogue*>(diag)->*field;
-	auto normalized = boost::locale::normalize(value.get());
-	if (normalized != value)
-		value = normalized;
-	return value.get();
+std::string const &get_normalized(const AssDialogue *diag, decltype(&AssDialogueBase::Text) field)
+{
+    auto &value = const_cast<AssDialogue *>(diag)->*field;
+    auto normalized = boost::locale::normalize(value.get());
+    if (normalized != value)
+        value = normalized;
+    return value.get();
 }
 
-typedef std::function<MatchState (const AssDialogue*, size_t)> matcher;
+typedef std::function<MatchState (const AssDialogue *, size_t)> matcher;
 
 class noop_accessor {
-	boost::flyweight<std::string> AssDialogueBase::*field;
-	size_t start = 0;
+    boost::flyweight<std::string> AssDialogueBase::*field;
+    size_t start = 0;
 
 public:
-	noop_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
+    noop_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
 
-	std::string get(const AssDialogue *d, size_t s) {
-		start = s;
-		return get_normalized(d, field).substr(s);
-	}
+    std::string get(const AssDialogue *d, size_t s) {
+        start = s;
+        return get_normalized(d, field).substr(s);
+    }
 
-	MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
-		return {r, s + start, e + start};
-	}
+    MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
+        return {r, s + start, e + start};
+    }
 };
 
 class skip_tags_accessor {
-	boost::flyweight<std::string> AssDialogueBase::*field;
-	agi::util::tagless_find_helper helper;
+    boost::flyweight<std::string> AssDialogueBase::*field;
+    agi::util::tagless_find_helper helper;
 
 public:
-	skip_tags_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
+    skip_tags_accessor(SearchReplaceSettings::Field f) : field(get_dialogue_field(f)) { }
 
-	std::string get(const AssDialogue *d, size_t s) {
-		return helper.strip_tags(get_normalized(d, field), s);
-	}
+    std::string get(const AssDialogue *d, size_t s) {
+        return helper.strip_tags(get_normalized(d, field), s);
+    }
 
-	MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
-		helper.map_range(s, e);
-		return {r, s, e};
-	}
+    MatchState make_match_state(size_t s, size_t e, boost::u32regex *r = nullptr) {
+        helper.map_range(s, e);
+        return {r, s, e};
+    }
 };
 
 template<typename Accessor>
-matcher get_matcher(SearchReplaceSettings const& settings, Accessor&& a) {
-	if (settings.use_regex) {
-		int flags = boost::u32regex::perl;
-		if (!settings.match_case)
-			flags |= boost::u32regex::icase;
-
-		auto regex = boost::make_u32regex(settings.find, flags);
-
-		return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
-			boost::smatch result;
-			auto const& str = a.get(diag, start);
-			if (!u32regex_search(str, result, regex, start > 0 ? boost::match_not_bol : boost::match_default))
-				return bad_match;
-			return a.make_match_state(result.position(), result.position() + result.length(), &regex);
-		};
-	}
-
-	bool full_match_only = settings.exact_match;
-	bool match_case = settings.match_case;
-	std::string look_for = settings.find;
-
-	if (!settings.match_case)
-		look_for = boost::locale::fold_case(look_for);
-
-	return [=](const AssDialogue *diag, size_t start) mutable -> MatchState {
-		const auto str = a.get(diag, start);
-		if (full_match_only && str.size() != look_for.size())
-			return bad_match;
-
-		if (match_case) {
-			const auto pos = str.find(look_for);
-			return pos == std::string::npos ? bad_match : a.make_match_state(pos, pos + look_for.size());
-		}
-
-		const auto pos = agi::util::ifind(str, look_for);
-		return pos.first == bad_pos ? bad_match : a.make_match_state(pos.first, pos.second);
-	};
+matcher get_matcher(SearchReplaceSettings const &settings, Accessor &&a)
+{
+    if (settings.use_regex) {
+        int flags = boost::u32regex::perl;
+        if (!settings.match_case)
+            flags |= boost::u32regex::icase;
+
+        auto regex = boost::make_u32regex(settings.find, flags);
+
+        return [ = ](const AssDialogue * diag, size_t start) mutable -> MatchState {
+            boost::smatch result;
+            auto const &str = a.get(diag, start);
+            if (!u32regex_search(str, result, regex, start > 0 ? boost::match_not_bol : boost::match_default))
+                return bad_match;
+            return a.make_match_state(result.position(), result.position() + result.length(), &regex);
+        };
+    }
+
+    bool full_match_only = settings.exact_match;
+    bool match_case = settings.match_case;
+    std::string look_for = settings.find;
+
+    if (!settings.match_case)
+        look_for = boost::locale::fold_case(look_for);
+
+    return [ = ](const AssDialogue * diag, size_t start) mutable -> MatchState {
+        const auto str = a.get(diag, start);
+        if (full_match_only && str.size() != look_for.size())
+            return bad_match;
+
+        if (match_case)
+        {
+            const auto pos = str.find(look_for);
+            return pos == std::string::npos ? bad_match : a.make_match_state(pos, pos + look_for.size());
+        }
+
+        const auto pos = agi::util::ifind(str, look_for);
+        return pos.first == bad_pos ? bad_match : a.make_match_state(pos.first, pos.second);
+    };
 }
 
 template<typename Iterator, typename Container>
-Iterator circular_next(Iterator it, Container& c) {
-	++it;
-	if (it == c.end())
-		it = c.begin();
-	return it;
+Iterator circular_next(Iterator it, Container &c)
+{
+    ++it;
+    if (it == c.end())
+        it = c.begin();
+    return it;
 }
 
 }
 
-std::function<MatchState (const AssDialogue*, size_t)> SearchReplaceEngine::GetMatcher(SearchReplaceSettings const& settings) {
-	if (settings.skip_tags)
-		return get_matcher(settings, skip_tags_accessor(settings.field));
-	return get_matcher(settings, noop_accessor(settings.field));
+std::function<MatchState (const AssDialogue *, size_t)> SearchReplaceEngine::GetMatcher(SearchReplaceSettings const &settings)
+{
+    if (settings.skip_tags)
+        return get_matcher(settings, skip_tags_accessor(settings.field));
+    return get_matcher(settings, noop_accessor(settings.field));
 }
 
 SearchReplaceEngine::SearchReplaceEngine(agi::Context *c)
-: context(c)
+    : context(c)
 {
 }
 
-void SearchReplaceEngine::Replace(AssDialogue *diag, MatchState &ms) {
-	auto& diag_field = diag->*get_dialogue_field(settings.field);
-	auto text = diag_field.get();
+void SearchReplaceEngine::Replace(AssDialogue *diag, MatchState &ms)
+{
+    auto &diag_field = diag->*get_dialogue_field(settings.field);
+    auto text = diag_field.get();
 
-	std::string replacement = settings.replace_with;
-	if (ms.re) {
-		auto to_replace = text.substr(ms.start, ms.end - ms.start);
-		replacement = u32regex_replace(to_replace, *ms.re, replacement, boost::format_first_only);
-	}
+    std::string replacement = settings.replace_with;
+    if (ms.re) {
+        auto to_replace = text.substr(ms.start, ms.end - ms.start);
+        replacement = u32regex_replace(to_replace, *ms.re, replacement, boost::format_first_only);
+    }
 
-	diag_field = text.substr(0, ms.start) + replacement + text.substr(ms.end);
-	ms.end = ms.start + replacement.size();
+    diag_field = text.substr(0, ms.start) + replacement + text.substr(ms.end);
+    ms.end = ms.start + replacement.size();
 }
 
-bool SearchReplaceEngine::FindReplace(bool replace) {
-	if (!initialized)
-		return false;
-
-	auto matches = GetMatcher(settings);
-
-	AssDialogue *line = context->selectionController->GetActiveLine();
-	auto it = context->ass->iterator_to(*line);
-	size_t pos = 0;
-
-	auto replace_ms = bad_match;
-	if (replace) {
-		if (settings.field == SearchReplaceSettings::Field::TEXT)
-			pos = context->textSelectionController->GetSelectionStart();
-
-		if ((replace_ms = matches(line, pos))) {
-			size_t end = bad_pos;
-			if (settings.field == SearchReplaceSettings::Field::TEXT)
-				end = context->textSelectionController->GetSelectionEnd();
-
-			if (end == bad_pos || (pos == replace_ms.start && end == replace_ms.end)) {
-				Replace(line, replace_ms);
-				pos = replace_ms.end;
-				context->ass->Commit(_("replace"), AssFile::COMMIT_DIAG_TEXT);
-			}
-			else {
-				// The current line matches, but it wasn't already selected,
-				// so the match hasn't been "found" and displayed to the user
-				// yet, so do that rather than replacing
-				context->textSelectionController->SetSelection(replace_ms.start, replace_ms.end);
-				return true;
-			}
-		}
-	}
-	// Search from the end of the selection to avoid endless matching the same thing
-	else if (settings.field == SearchReplaceSettings::Field::TEXT)
-		pos = context->textSelectionController->GetSelectionEnd();
-	// For non-text fields we just look for matching lines rather than each
-	// match within the line, so move to the next line
-	else if (settings.field != SearchReplaceSettings::Field::TEXT)
-		it = circular_next(it, context->ass->Events);
-
-	auto const& sel = context->selectionController->GetSelectedSet();
-	bool selection_only = sel.size() > 1 && settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
-
-	do {
-		if (selection_only && !sel.count(&*it)) continue;
-		if (settings.ignore_comments && it->Comment) continue;
-
-		if (MatchState ms = matches(&*it, pos)) {
-			if (selection_only)
-				// We're cycling through the selection, so don't muck with it
-				context->selectionController->SetActiveLine(&*it);
-			else
-				context->selectionController->SetSelectionAndActive({ &*it }, &*it);
-
-			if (settings.field == SearchReplaceSettings::Field::TEXT)
-				context->textSelectionController->SetSelection(ms.start, ms.end);
-
-			return true;
-		}
-	} while (pos = 0, &*(it = circular_next(it, context->ass->Events)) != line);
-
-	// Replaced something and didn't find another match, so select the newly
-	// inserted text
-	if (replace_ms && settings.field == SearchReplaceSettings::Field::TEXT)
-		context->textSelectionController->SetSelection(replace_ms.start, replace_ms.end);
-
-	return true;
+bool SearchReplaceEngine::FindReplace(bool replace)
+{
+    if (!initialized)
+        return false;
+
+    auto matches = GetMatcher(settings);
+
+    AssDialogue *line = context->selectionController->GetActiveLine();
+    auto it = context->ass->iterator_to(*line);
+    size_t pos = 0;
+
+    auto replace_ms = bad_match;
+    if (replace) {
+        if (settings.field == SearchReplaceSettings::Field::TEXT)
+            pos = context->textSelectionController->GetSelectionStart();
+
+        if ((replace_ms = matches(line, pos))) {
+            size_t end = bad_pos;
+            if (settings.field == SearchReplaceSettings::Field::TEXT)
+                end = context->textSelectionController->GetSelectionEnd();
+
+            if (end == bad_pos || (pos == replace_ms.start && end == replace_ms.end)) {
+                Replace(line, replace_ms);
+                pos = replace_ms.end;
+                context->ass->Commit(_("replace"), AssFile::COMMIT_DIAG_TEXT);
+            }
+            else {
+                // The current line matches, but it wasn't already selected,
+                // so the match hasn't been "found" and displayed to the user
+                // yet, so do that rather than replacing
+                context->textSelectionController->SetSelection(replace_ms.start, replace_ms.end);
+                return true;
+            }
+        }
+    }
+    // Search from the end of the selection to avoid endless matching the same thing
+    else if (settings.field == SearchReplaceSettings::Field::TEXT)
+        pos = context->textSelectionController->GetSelectionEnd();
+    // For non-text fields we just look for matching lines rather than each
+    // match within the line, so move to the next line
+    else if (settings.field != SearchReplaceSettings::Field::TEXT)
+        it = circular_next(it, context->ass->Events);
+
+    auto const &sel = context->selectionController->GetSelectedSet();
+    bool selection_only = sel.size() > 1 && settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
+
+    do {
+        if (selection_only && !sel.count(&*it)) continue;
+        if (settings.ignore_comments && it->Comment) continue;
+
+        if (MatchState ms = matches(&*it, pos)) {
+            if (selection_only)
+                // We're cycling through the selection, so don't muck with it
+                context->selectionController->SetActiveLine(&*it);
+            else
+                context->selectionController->SetSelectionAndActive({ &*it }, &*it);
+
+            if (settings.field == SearchReplaceSettings::Field::TEXT)
+                context->textSelectionController->SetSelection(ms.start, ms.end);
+
+            return true;
+        }
+    } while (pos = 0, &*(it = circular_next(it, context->ass->Events)) != line);
+
+    // Replaced something and didn't find another match, so select the newly
+    // inserted text
+    if (replace_ms && settings.field == SearchReplaceSettings::Field::TEXT)
+        context->textSelectionController->SetSelection(replace_ms.start, replace_ms.end);
+
+    return true;
 }
 
-bool SearchReplaceEngine::ReplaceAll() {
-	if (!initialized)
-		return false;
-
-	size_t count = 0;
-
-	auto matches = GetMatcher(settings);
-
-	auto const& sel = context->selectionController->GetSelectedSet();
-	bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
-
-	for (auto& diag : context->ass->Events) {
-		if (selection_only && !sel.count(&diag)) continue;
-		if (settings.ignore_comments && diag.Comment) continue;
-
-		if (settings.use_regex) {
-			if (MatchState ms = matches(&diag, 0)) {
-				auto& diag_field = diag.*get_dialogue_field(settings.field);
-				std::string const& text = diag_field.get();
-				count += std::distance(
-					boost::u32regex_iterator<std::string::const_iterator>(begin(text), end(text), *ms.re),
-					boost::u32regex_iterator<std::string::const_iterator>());
-				diag_field = u32regex_replace(text, *ms.re, settings.replace_with);
-			}
-			continue;
-		}
-
-		size_t pos = 0;
-		while (MatchState ms = matches(&diag, pos)) {
-			++count;
-			Replace(&diag, ms);
-			pos = ms.end;
-		}
-	}
-
-	if (count > 0) {
-		context->ass->Commit(_("replace"), AssFile::COMMIT_DIAG_TEXT);
-		wxMessageBox(fmt_plural(count, "One match was replaced.", "%d matches were replaced.", (int)count));
-	}
-	else {
-		wxMessageBox(_("No matches found."));
-	}
-
-	return true;
+bool SearchReplaceEngine::ReplaceAll()
+{
+    if (!initialized)
+        return false;
+
+    size_t count = 0;
+
+    auto matches = GetMatcher(settings);
+
+    auto const &sel = context->selectionController->GetSelectedSet();
+    bool selection_only = settings.limit_to == SearchReplaceSettings::Limit::SELECTED;
+
+    for (auto &diag : context->ass->Events) {
+        if (selection_only && !sel.count(&diag)) continue;
+        if (settings.ignore_comments && diag.Comment) continue;
+
+        if (settings.use_regex) {
+            if (MatchState ms = matches(&diag, 0)) {
+                auto &diag_field = diag.*get_dialogue_field(settings.field);
+                std::string const &text = diag_field.get();
+                count += std::distance(
+                             boost::u32regex_iterator<std::string::const_iterator>(begin(text), end(text), *ms.re),
+                             boost::u32regex_iterator<std::string::const_iterator>());
+                diag_field = u32regex_replace(text, *ms.re, settings.replace_with);
+            }
+            continue;
+        }
+
+        size_t pos = 0;
+        while (MatchState ms = matches(&diag, pos)) {
+            ++count;
+            Replace(&diag, ms);
+            pos = ms.end;
+        }
+    }
+
+    if (count > 0) {
+        context->ass->Commit(_("replace"), AssFile::COMMIT_DIAG_TEXT);
+        wxMessageBox(fmt_plural(count, "One match was replaced.", "%d matches were replaced.", (int)count));
+    }
+    else {
+        wxMessageBox(_("No matches found."));
+    }
+
+    return true;
 }
 
-void SearchReplaceEngine::Configure(SearchReplaceSettings const& new_settings) {
-	settings = new_settings;
-	initialized = true;
+void SearchReplaceEngine::Configure(SearchReplaceSettings const &new_settings)
+{
+    settings = new_settings;
+    initialized = true;
 }
diff --git a/src/search_replace_engine.h b/src/search_replace_engine.h
index 4f7bfd21a39b9590eb9babe8a6fa447d6907233f..7e01d32b557d08b1e60ab4ed73cf5f7dce784624 100644
--- a/src/search_replace_engine.h
+++ b/src/search_replace_engine.h
@@ -22,54 +22,54 @@ namespace agi { struct Context; }
 class AssDialogue;
 
 struct MatchState {
-	boost::u32regex *re;
-	size_t start, end;
+    boost::u32regex *re;
+    size_t start, end;
 
-	operator bool() const { return end != (size_t)-1; }
+    operator bool() const { return end != (size_t) -1; }
 };
 
 struct SearchReplaceSettings {
-	enum class Field {
-		TEXT = 0,
-		STYLE,
-		ACTOR,
-		EFFECT
-	};
+    enum class Field {
+        TEXT = 0,
+        STYLE,
+        ACTOR,
+        EFFECT
+    };
 
-	enum class Limit {
-		ALL = 0,
-		SELECTED
-	};
+    enum class Limit {
+        ALL = 0,
+        SELECTED
+    };
 
-	std::string find;
-	std::string replace_with;
+    std::string find;
+    std::string replace_with;
 
-	Field field;
-	Limit limit_to;
+    Field field;
+    Limit limit_to;
 
-	bool match_case;
-	bool use_regex;
-	bool ignore_comments;
-	bool skip_tags;
-	bool exact_match;
+    bool match_case;
+    bool use_regex;
+    bool ignore_comments;
+    bool skip_tags;
+    bool exact_match;
 };
 
 class SearchReplaceEngine {
-	agi::Context *context;
-	bool initialized = false;
-	SearchReplaceSettings settings;
+    agi::Context *context;
+    bool initialized = false;
+    SearchReplaceSettings settings;
 
-	bool FindReplace(bool replace);
-	void Replace(AssDialogue *line, MatchState &ms);
+    bool FindReplace(bool replace);
+    void Replace(AssDialogue *line, MatchState &ms);
 
 public:
-	bool FindNext() { return FindReplace(false); }
-	bool ReplaceNext() { return FindReplace(true); }
-	bool ReplaceAll();
+    bool FindNext() { return FindReplace(false); }
+    bool ReplaceNext() { return FindReplace(true); }
+    bool ReplaceAll();
 
-	void Configure(SearchReplaceSettings const& new_settings);
+    void Configure(SearchReplaceSettings const &new_settings);
 
-	static std::function<MatchState (const AssDialogue*, size_t)> GetMatcher(SearchReplaceSettings const& settings);
+    static std::function<MatchState (const AssDialogue *, size_t)> GetMatcher(SearchReplaceSettings const &settings);
 
-	SearchReplaceEngine(agi::Context *c);
+    SearchReplaceEngine(agi::Context *c);
 };
diff --git a/src/selection_controller.cpp b/src/selection_controller.cpp
index 576f9e4998938b417a5dce04c504bef6ecbcefb5..1320fdc7d8efbd4b4905e7959b1816c95c4aaf9a 100644
--- a/src/selection_controller.cpp
+++ b/src/selection_controller.cpp
@@ -25,50 +25,56 @@
 
 SelectionController::SelectionController(agi::Context *c) : context(c) { }
 
-void SelectionController::SetSelectedSet(Selection new_selection) {
-	selection = std::move(new_selection);
-	AnnounceSelectedSetChanged();
+void SelectionController::SetSelectedSet(Selection new_selection)
+{
+    selection = std::move(new_selection);
+    AnnounceSelectedSetChanged();
 }
 
-void SelectionController::SetActiveLine(AssDialogue *new_line) {
-	if (new_line != active_line) {
-		active_line = new_line;
-		if (active_line)
-			context->ass->Properties.active_row = active_line->Row;
-		AnnounceActiveLineChanged(new_line);
-	}
+void SelectionController::SetActiveLine(AssDialogue *new_line)
+{
+    if (new_line != active_line) {
+        active_line = new_line;
+        if (active_line)
+            context->ass->Properties.active_row = active_line->Row;
+        AnnounceActiveLineChanged(new_line);
+    }
 }
 
-void SelectionController::SetSelectionAndActive(Selection new_selection, AssDialogue *new_line) {
-	bool active_line_changed = new_line != active_line;
-	selection = std::move(new_selection);
-	active_line = new_line;
-	if (active_line)
-		context->ass->Properties.active_row = active_line->Row;
+void SelectionController::SetSelectionAndActive(Selection new_selection, AssDialogue *new_line)
+{
+    bool active_line_changed = new_line != active_line;
+    selection = std::move(new_selection);
+    active_line = new_line;
+    if (active_line)
+        context->ass->Properties.active_row = active_line->Row;
 
-	AnnounceSelectedSetChanged();
-	if (active_line_changed)
-		AnnounceActiveLineChanged(new_line);
+    AnnounceSelectedSetChanged();
+    if (active_line_changed)
+        AnnounceActiveLineChanged(new_line);
 }
 
-std::vector<AssDialogue *> SelectionController::GetSortedSelection() const {
-	std::vector<AssDialogue *> ret(selection.begin(), selection.end());
-	sort(begin(ret), end(ret), [](AssDialogue *a, AssDialogue *b) { return a->Row < b->Row; });
-	return ret;
+std::vector<AssDialogue *> SelectionController::GetSortedSelection() const
+{
+    std::vector<AssDialogue *> ret(selection.begin(), selection.end());
+    sort(begin(ret), end(ret), [](AssDialogue * a, AssDialogue * b) { return a->Row < b->Row; });
+    return ret;
 }
 
-void SelectionController::PrevLine() {
-	if (!active_line) return;
-	auto it = context->ass->iterator_to(*active_line);
-	if (it != context->ass->Events.begin()) {
-		--it;
-		SetSelectionAndActive({&*it}, &*it);
-	}
+void SelectionController::PrevLine()
+{
+    if (!active_line) return;
+    auto it = context->ass->iterator_to(*active_line);
+    if (it != context->ass->Events.begin()) {
+        --it;
+        SetSelectionAndActive({&*it}, &*it);
+    }
 }
 
-void SelectionController::NextLine() {
-	if (!active_line) return;
-	auto it = context->ass->iterator_to(*active_line);
-	if (++it != context->ass->Events.end())
-		SetSelectionAndActive({&*it}, &*it);
+void SelectionController::NextLine()
+{
+    if (!active_line) return;
+    auto it = context->ass->iterator_to(*active_line);
+    if (++it != context->ass->Events.end())
+        SetSelectionAndActive({&*it}, &*it);
 }
diff --git a/src/selection_controller.h b/src/selection_controller.h
index d0acc8d2c8286fbbd37fdf1be494d9a878ebe621..19793dd78760174e93e602232119d8e3791b0d2e 100644
--- a/src/selection_controller.h
+++ b/src/selection_controller.h
@@ -38,75 +38,75 @@ typedef std::set<AssDialogue *> Selection;
 namespace agi { struct Context; }
 
 class SelectionController {
-	agi::signal::Signal<AssDialogue *> AnnounceActiveLineChanged;
-	agi::signal::Signal<> AnnounceSelectedSetChanged;
+    agi::signal::Signal<AssDialogue *> AnnounceActiveLineChanged;
+    agi::signal::Signal<> AnnounceSelectedSetChanged;
 
-	agi::Context *context;
+    agi::Context *context;
 
-	Selection selection; ///< Currently selected lines
-	AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none
+    Selection selection; ///< Currently selected lines
+    AssDialogue *active_line = nullptr; ///< The currently active line or 0 if none
 
 public:
-	SelectionController(agi::Context *context);
-
-	/// @brief Change the active line
-	/// @param new_line Subtitle line to become the new active line
-	///
-	/// The active line may be changed to nullptr, in which case there is no longer an
-	/// active line.
-	///
-	/// Calling this method should only cause a change notification to be sent if
-	/// the active line was actually changed.
-	///
-	/// This method must not affect the selected set.
-	void SetActiveLine(AssDialogue *new_line);
-
-	/// @brief Obtain the active line
-	/// @return The active line or nullptr if there is none
-	AssDialogue *GetActiveLine() const { return active_line;  }
-
-	/// @brief Change the selected set
-	/// @param new_selection The set of subtitle lines to become the new selected set
-	///
-	/// Implementations must either completely change the selected set to the new
-	/// set provided, or not change the selected set at all. Partial changes are
-	/// not allowed.
-	///
-	/// If no change happens to the selected set, whether because it was refused or
-	/// because the new set was identical to the old set, no change notification may
-	/// be sent.
-	void SetSelectedSet(Selection new_selection);
-
-	/// @brief Obtain the selected set
-	/// @return The selected set
-	Selection const& GetSelectedSet() const { return selection; }
-
-	/// Get the selection sorted by row number
-	std::vector<AssDialogue *> GetSortedSelection() const;
-
-	/// @brief Set both the selected set and active line
-	/// @param new_line Subtitle line to become the new active line
-	/// @param new_selection The set of subtitle lines to become the new selected set
-	///
-	/// This sets both the active line and selected set before announcing the
-	/// change to either of them, and is guaranteed to announce the active line
-	/// change before the selection change.
-	void SetSelectionAndActive(Selection new_selection, AssDialogue *new_line);
-
-	/// @brief Change the active line to the next in sequence
-	///
-	/// If there is no logical next line in sequence, no change happens. This should
-	/// also reset the selected set to consist of exactly the active line, if the
-	/// active line was changed.
-	void NextLine();
-
-	/// @brief Change the active line to the previous in sequence
-	///
-	/// If there is no logical previous line in sequence, no change happens. This
-	/// should also reset the selected set to consist of exactly the active line, if
-	/// the active line was changed.
-	void PrevLine();
-
-	DEFINE_SIGNAL_ADDERS(AnnounceSelectedSetChanged, AddSelectionListener)
-	DEFINE_SIGNAL_ADDERS(AnnounceActiveLineChanged, AddActiveLineListener)
+    SelectionController(agi::Context *context);
+
+    /// @brief Change the active line
+    /// @param new_line Subtitle line to become the new active line
+    ///
+    /// The active line may be changed to nullptr, in which case there is no longer an
+    /// active line.
+    ///
+    /// Calling this method should only cause a change notification to be sent if
+    /// the active line was actually changed.
+    ///
+    /// This method must not affect the selected set.
+    void SetActiveLine(AssDialogue *new_line);
+
+    /// @brief Obtain the active line
+    /// @return The active line or nullptr if there is none
+    AssDialogue *GetActiveLine() const { return active_line;  }
+
+    /// @brief Change the selected set
+    /// @param new_selection The set of subtitle lines to become the new selected set
+    ///
+    /// Implementations must either completely change the selected set to the new
+    /// set provided, or not change the selected set at all. Partial changes are
+    /// not allowed.
+    ///
+    /// If no change happens to the selected set, whether because it was refused or
+    /// because the new set was identical to the old set, no change notification may
+    /// be sent.
+    void SetSelectedSet(Selection new_selection);
+
+    /// @brief Obtain the selected set
+    /// @return The selected set
+    Selection const &GetSelectedSet() const { return selection; }
+
+    /// Get the selection sorted by row number
+    std::vector<AssDialogue *> GetSortedSelection() const;
+
+    /// @brief Set both the selected set and active line
+    /// @param new_line Subtitle line to become the new active line
+    /// @param new_selection The set of subtitle lines to become the new selected set
+    ///
+    /// This sets both the active line and selected set before announcing the
+    /// change to either of them, and is guaranteed to announce the active line
+    /// change before the selection change.
+    void SetSelectionAndActive(Selection new_selection, AssDialogue *new_line);
+
+    /// @brief Change the active line to the next in sequence
+    ///
+    /// If there is no logical next line in sequence, no change happens. This should
+    /// also reset the selected set to consist of exactly the active line, if the
+    /// active line was changed.
+    void NextLine();
+
+    /// @brief Change the active line to the previous in sequence
+    ///
+    /// If there is no logical previous line in sequence, no change happens. This
+    /// should also reset the selected set to consist of exactly the active line, if
+    /// the active line was changed.
+    void PrevLine();
+
+    DEFINE_SIGNAL_ADDERS(AnnounceSelectedSetChanged, AddSelectionListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceActiveLineChanged, AddActiveLineListener)
 };
diff --git a/src/spellchecker.cpp b/src/spellchecker.cpp
index 4e328ab22aaf79c4b2fdc259f620470dd5eb0a13..0a48be28c6e13f7361c17a02fe23c4917ae7e477 100644
--- a/src/spellchecker.cpp
+++ b/src/spellchecker.cpp
@@ -27,12 +27,13 @@ class OptionValue;
 std::unique_ptr<agi::SpellChecker> CreateCocoaSpellChecker(OptionValue *opt);
 }
 
-std::unique_ptr<agi::SpellChecker> SpellCheckerFactory::GetSpellChecker() {
+std::unique_ptr<agi::SpellChecker> SpellCheckerFactory::GetSpellChecker()
+{
 #ifdef __APPLE__
-	return agi::CreateCocoaSpellChecker(OPT_SET("Tool/Spell Checker/Language"));
+    return agi::CreateCocoaSpellChecker(OPT_SET("Tool/Spell Checker/Language"));
 #elif defined(WITH_HUNSPELL)
-	return agi::make_unique<HunspellSpellChecker>();
+    return agi::make_unique<HunspellSpellChecker>();
 #else
-	return {};
+    return {};
 #endif
 }
diff --git a/src/spellchecker_hunspell.cpp b/src/spellchecker_hunspell.cpp
index ddb6c43391c93d79af79679941e76e2f0ef23e58..34d93dd64b2eb65924ddd9d5cc9d1861785d2eff 100644
--- a/src/spellchecker_hunspell.cpp
+++ b/src/spellchecker_hunspell.cpp
@@ -34,195 +34,209 @@
 #include <hunspell/hunspell.hxx>
 
 HunspellSpellChecker::HunspellSpellChecker()
-: lang_listener(OPT_SUB("Tool/Spell Checker/Language", &HunspellSpellChecker::OnLanguageChanged, this))
-, dict_path_listener(OPT_SUB("Path/Dictionary", &HunspellSpellChecker::OnPathChanged, this))
+    : lang_listener(OPT_SUB("Tool/Spell Checker/Language", &HunspellSpellChecker::OnLanguageChanged, this))
+    , dict_path_listener(OPT_SUB("Path/Dictionary", &HunspellSpellChecker::OnPathChanged, this))
 {
-	OnLanguageChanged();
+    OnLanguageChanged();
 }
 
-HunspellSpellChecker::~HunspellSpellChecker() {
+HunspellSpellChecker::~HunspellSpellChecker()
+{
 }
 
-bool HunspellSpellChecker::CanAddWord(std::string const& word) {
-	if (!hunspell) return false;
-	try {
-		conv->Convert(word);
-		return true;
-	}
-	catch (agi::charset::ConvError const&) {
-		return false;
-	}
+bool HunspellSpellChecker::CanAddWord(std::string const &word)
+{
+    if (!hunspell) return false;
+    try {
+        conv->Convert(word);
+        return true;
+    }
+    catch (agi::charset::ConvError const &) {
+        return false;
+    }
 }
 
-bool HunspellSpellChecker::CanRemoveWord(std::string const& word) {
-	return !!customWords.count(word);
+bool HunspellSpellChecker::CanRemoveWord(std::string const &word)
+{
+    return !!customWords.count(word);
 }
 
-void HunspellSpellChecker::AddWord(std::string const& word) {
-	if (!hunspell) return;
+void HunspellSpellChecker::AddWord(std::string const &word)
+{
+    if (!hunspell) return;
 
-	// Add it to the in-memory dictionary
-	hunspell->add(conv->Convert(word).c_str());
+    // Add it to the in-memory dictionary
+    hunspell->add(conv->Convert(word).c_str());
 
-	// Add the word
-	if (customWords.insert(word).second)
-		WriteUserDictionary();
+    // Add the word
+    if (customWords.insert(word).second)
+        WriteUserDictionary();
 }
 
-void HunspellSpellChecker::RemoveWord(std::string const& word) {
-	if (!hunspell) return;
+void HunspellSpellChecker::RemoveWord(std::string const &word)
+{
+    if (!hunspell) return;
 
-	// Remove it from the in-memory dictionary
-	hunspell->remove(conv->Convert(word).c_str());
+    // Remove it from the in-memory dictionary
+    hunspell->remove(conv->Convert(word).c_str());
 
-	auto word_iter = customWords.find(word);
-	if (word_iter != customWords.end()) {
-		customWords.erase(word_iter);
+    auto word_iter = customWords.find(word);
+    if (word_iter != customWords.end()) {
+        customWords.erase(word_iter);
 
-		WriteUserDictionary();
-	}
+        WriteUserDictionary();
+    }
 }
 
-void HunspellSpellChecker::ReadUserDictionary() {
-	customWords.clear();
-
-	// Read the old contents of the user's dictionary
-	try {
-		auto stream = agi::io::Open(userDicPath);
-		copy_if(
-			++agi::line_iterator<std::string>(*stream), agi::line_iterator<std::string>(),
-			inserter(customWords, customWords.end()),
-			[](std::string const& str) { return !str.empty(); });
-	}
-	catch (agi::fs::FileNotFound const&) {
-		// Not an error; user dictionary just doesn't exist
-	}
+void HunspellSpellChecker::ReadUserDictionary()
+{
+    customWords.clear();
+
+    // Read the old contents of the user's dictionary
+    try {
+        auto stream = agi::io::Open(userDicPath);
+        copy_if(
+            ++agi::line_iterator<std::string>(*stream), agi::line_iterator<std::string>(),
+            inserter(customWords, customWords.end()),
+        [](std::string const & str) { return !str.empty(); });
+    }
+    catch (agi::fs::FileNotFound const &) {
+        // Not an error; user dictionary just doesn't exist
+    }
 }
 
-void HunspellSpellChecker::WriteUserDictionary() {
-	// Ensure that the path exists
-	agi::fs::CreateDirectory(userDicPath.parent_path());
-
-	// Write the new dictionary
-	{
-		agi::io::Save writer(userDicPath);
-		writer.Get() << customWords.size() << "\n";
-		copy(customWords.begin(), customWords.end(), std::ostream_iterator<std::string>(writer.Get(), "\n"));
-	}
-
-	// Announce a language change so that any other spellcheckers reload the
-	// current dictionary to get the addition/removal
-	lang_listener.Block();
-	OPT_SET("Tool/Spell Checker/Language")->SetString(OPT_GET("Tool/Spell Checker/Language")->GetString());
-	lang_listener.Unblock();
+void HunspellSpellChecker::WriteUserDictionary()
+{
+    // Ensure that the path exists
+    agi::fs::CreateDirectory(userDicPath.parent_path());
+
+    // Write the new dictionary
+    {
+        agi::io::Save writer(userDicPath);
+        writer.Get() << customWords.size() << "\n";
+        copy(customWords.begin(), customWords.end(), std::ostream_iterator<std::string>(writer.Get(), "\n"));
+    }
+
+    // Announce a language change so that any other spellcheckers reload the
+    // current dictionary to get the addition/removal
+    lang_listener.Block();
+    OPT_SET("Tool/Spell Checker/Language")->SetString(OPT_GET("Tool/Spell Checker/Language")->GetString());
+    lang_listener.Unblock();
 }
 
-bool HunspellSpellChecker::CheckWord(std::string const& word) {
-	if (!hunspell) return true;
-	try {
-		return hunspell->spell(conv->Convert(word).c_str()) == 1;
-	}
-	catch (agi::charset::ConvError const&) {
-		return false;
-	}
+bool HunspellSpellChecker::CheckWord(std::string const &word)
+{
+    if (!hunspell) return true;
+    try {
+        return hunspell->spell(conv->Convert(word).c_str()) == 1;
+    }
+    catch (agi::charset::ConvError const &) {
+        return false;
+    }
 }
 
-std::vector<std::string> HunspellSpellChecker::GetSuggestions(std::string const& word) {
-	std::vector<std::string> suggestions;
-	if (!hunspell) return suggestions;
-
-	char **results;
-	int n = hunspell->suggest(&results, conv->Convert(word).c_str());
-
-	suggestions.reserve(n);
-	// Convert suggestions to UTF-8
-	for (int i = 0; i < n; ++i) {
-		try {
-			suggestions.push_back(rconv->Convert(results[i]));
-		}
-		catch (agi::charset::ConvError const&) {
-			// Shouldn't ever actually happen...
-		}
-		free(results[i]);
-	}
-
-	free(results);
-
-	return suggestions;
+std::vector<std::string> HunspellSpellChecker::GetSuggestions(std::string const &word)
+{
+    std::vector<std::string> suggestions;
+    if (!hunspell) return suggestions;
+
+    char **results;
+    int n = hunspell->suggest(&results, conv->Convert(word).c_str());
+
+    suggestions.reserve(n);
+    // Convert suggestions to UTF-8
+    for (int i = 0; i < n; ++i) {
+        try {
+            suggestions.push_back(rconv->Convert(results[i]));
+        }
+        catch (agi::charset::ConvError const &) {
+            // Shouldn't ever actually happen...
+        }
+        free(results[i]);
+    }
+
+    free(results);
+
+    return suggestions;
 }
 
-static std::vector<std::string> langs(const char *filter) {
-	std::vector<std::string> paths;
-	auto data_path = config::path->Decode("?dictionary/");
-	auto user_path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString());
+static std::vector<std::string> langs(const char *filter)
+{
+    std::vector<std::string> paths;
+    auto data_path = config::path->Decode("?dictionary/");
+    auto user_path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString());
 
-	agi::fs::DirectoryIterator(data_path, filter).GetAll(paths);
-	agi::fs::DirectoryIterator(user_path, filter).GetAll(paths);
+    agi::fs::DirectoryIterator(data_path, filter).GetAll(paths);
+    agi::fs::DirectoryIterator(user_path, filter).GetAll(paths);
 
-	// Drop extensions
-	for (auto& fn : paths) fn.resize(fn.size() - 4);
+    // Drop extensions
+    for (auto &fn : paths) fn.resize(fn.size() - 4);
 
-	boost::sort(paths);
-	paths.erase(unique(begin(paths), end(paths)), end(paths));
+    boost::sort(paths);
+    paths.erase(unique(begin(paths), end(paths)), end(paths));
 
-	return paths;
+    return paths;
 }
 
-std::vector<std::string> HunspellSpellChecker::GetLanguageList() {
-	if (languages.empty())
-		boost::set_intersection(langs("*.dic"), langs("*.aff"), back_inserter(languages));
-	return languages;
+std::vector<std::string> HunspellSpellChecker::GetLanguageList()
+{
+    if (languages.empty())
+        boost::set_intersection(langs("*.dic"), langs("*.aff"), back_inserter(languages));
+    return languages;
 }
 
-static bool check_path(agi::fs::path const& path, std::string const& language, agi::fs::path& aff, agi::fs::path& dic) {
-	aff = path/agi::format("%s.aff", language);
-	dic = path/agi::format("%s.dic", language);
-	return agi::fs::FileExists(aff) && agi::fs::FileExists(dic);
+static bool check_path(agi::fs::path const &path, std::string const &language, agi::fs::path &aff, agi::fs::path &dic)
+{
+    aff = path / agi::format("%s.aff", language);
+    dic = path / agi::format("%s.dic", language);
+    return agi::fs::FileExists(aff) && agi::fs::FileExists(dic);
 }
 
-void HunspellSpellChecker::OnLanguageChanged() {
-	hunspell.reset();
+void HunspellSpellChecker::OnLanguageChanged()
+{
+    hunspell.reset();
 
-	auto language = OPT_GET("Tool/Spell Checker/Language")->GetString();
-	if (language.empty()) return;
+    auto language = OPT_GET("Tool/Spell Checker/Language")->GetString();
+    if (language.empty()) return;
 
-	agi::fs::path aff, dic;
-	auto path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString() + "/");
-	if (!check_path(path, language, aff, dic)) {
-		path = config::path->Decode("?dictionary/");
-		if (!check_path(path, language, aff, dic))
-			return;
-	}
+    agi::fs::path aff, dic;
+    auto path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString() + "/");
+    if (!check_path(path, language, aff, dic)) {
+        path = config::path->Decode("?dictionary/");
+        if (!check_path(path, language, aff, dic))
+            return;
+    }
 
-	LOG_I("dictionary/file") << dic;
+    LOG_I("dictionary/file") << dic;
 
 #ifdef _WIN32
-	// The prefix makes hunspell assume the paths are UTF-8 and use _wfopen
-	hunspell = agi::make_unique<Hunspell>(("\\\\?\\" + aff.string()).c_str(), ("\\\\?\\" + dic.string()).c_str());
+    // The prefix makes hunspell assume the paths are UTF-8 and use _wfopen
+    hunspell = agi::make_unique<Hunspell>(("\\\\?\\" + aff.string()).c_str(), ("\\\\?\\" + dic.string()).c_str());
 #else
-	hunspell = agi::make_unique<Hunspell>(aff.string().c_str(), dic.string().c_str());
+    hunspell = agi::make_unique<Hunspell>(aff.string().c_str(), dic.string().c_str());
 #endif
-	if (!hunspell) return;
-
-	conv = agi::make_unique<agi::charset::IconvWrapper>("utf-8", hunspell->get_dic_encoding());
-	rconv = agi::make_unique<agi::charset::IconvWrapper>(hunspell->get_dic_encoding(), "utf-8");
-
-	userDicPath = config::path->Decode("?user/dictionaries")/agi::format("user_%s.dic", language);
-	ReadUserDictionary();
-
-	for (auto const& word : customWords) {
-		try {
-			hunspell->add(conv->Convert(word).c_str());
-		}
-		catch (agi::charset::ConvError const&) {
-			// Normally this shouldn't happen, but some versions of Aegisub
-			// wrote words in the wrong charset
-		}
-	}
+    if (!hunspell) return;
+
+    conv = agi::make_unique<agi::charset::IconvWrapper>("utf-8", hunspell->get_dic_encoding());
+    rconv = agi::make_unique<agi::charset::IconvWrapper>(hunspell->get_dic_encoding(), "utf-8");
+
+    userDicPath = config::path->Decode("?user/dictionaries") / agi::format("user_%s.dic", language);
+    ReadUserDictionary();
+
+    for (auto const &word : customWords) {
+        try {
+            hunspell->add(conv->Convert(word).c_str());
+        }
+        catch (agi::charset::ConvError const &) {
+            // Normally this shouldn't happen, but some versions of Aegisub
+            // wrote words in the wrong charset
+        }
+    }
 }
 
-void HunspellSpellChecker::OnPathChanged() {
-	languages.clear();
+void HunspellSpellChecker::OnPathChanged()
+{
+    languages.clear();
 }
 
 #endif // WITH_HUNSPELL
diff --git a/src/spellchecker_hunspell.h b/src/spellchecker_hunspell.h
index 1c204fd2f5e5e1190924aed34de9b60f1375065c..797110c014a9b6f540b01e426dee7f1ee10a3166 100644
--- a/src/spellchecker_hunspell.h
+++ b/src/spellchecker_hunspell.h
@@ -34,48 +34,48 @@ class Hunspell;
 
 /// @brief Hunspell-based spell checker implementation
 class HunspellSpellChecker final : public agi::SpellChecker {
-	/// Hunspell instance
-	std::unique_ptr<Hunspell> hunspell;
+    /// Hunspell instance
+    std::unique_ptr<Hunspell> hunspell;
 
-	/// Conversions between the dictionary charset and utf-8
-	std::unique_ptr<agi::charset::IconvWrapper> conv;
-	std::unique_ptr<agi::charset::IconvWrapper> rconv;
+    /// Conversions between the dictionary charset and utf-8
+    std::unique_ptr<agi::charset::IconvWrapper> conv;
+    std::unique_ptr<agi::charset::IconvWrapper> rconv;
 
-	/// Languages which we have dictionaries for
-	std::vector<std::string> languages;
+    /// Languages which we have dictionaries for
+    std::vector<std::string> languages;
 
-	/// Path to user-local dictionary.
-	agi::fs::path userDicPath;
+    /// Path to user-local dictionary.
+    agi::fs::path userDicPath;
 
-	/// Words in the custom user dictionary
-	std::set<std::string> customWords;
+    /// Words in the custom user dictionary
+    std::set<std::string> customWords;
 
-	/// Dictionary language change connection
-	agi::signal::Connection lang_listener;
-	/// Dictionary language change handler
-	void OnLanguageChanged();
+    /// Dictionary language change connection
+    agi::signal::Connection lang_listener;
+    /// Dictionary language change handler
+    void OnLanguageChanged();
 
-	/// Dictionary path change connection
-	agi::signal::Connection dict_path_listener;
-	/// Dictionary path change handler
-	void OnPathChanged();
+    /// Dictionary path change connection
+    agi::signal::Connection dict_path_listener;
+    /// Dictionary path change handler
+    void OnPathChanged();
 
-	/// Load words from custom dictionary
-	void ReadUserDictionary();
-	/// Save words to custom dictionary
-	void WriteUserDictionary();
+    /// Load words from custom dictionary
+    void ReadUserDictionary();
+    /// Save words to custom dictionary
+    void WriteUserDictionary();
 
 public:
-	HunspellSpellChecker();
-	~HunspellSpellChecker();
-
-	void AddWord(std::string const& word) override;
-	void RemoveWord(std::string const& word) override;
-	bool CanAddWord(std::string const& word) override;
-	bool CanRemoveWord(std::string const& word) override;
-	bool CheckWord(std::string const& word) override;
-	std::vector<std::string> GetSuggestions(std::string const& word) override;
-	std::vector<std::string> GetLanguageList() override;
+    HunspellSpellChecker();
+    ~HunspellSpellChecker();
+
+    void AddWord(std::string const &word) override;
+    void RemoveWord(std::string const &word) override;
+    bool CanAddWord(std::string const &word) override;
+    bool CanRemoveWord(std::string const &word) override;
+    bool CheckWord(std::string const &word) override;
+    std::vector<std::string> GetSuggestions(std::string const &word) override;
+    std::vector<std::string> GetLanguageList() override;
 };
 
 #endif
diff --git a/src/spline.cpp b/src/spline.cpp
index 395bac084ee6352445655fcec8eeb98e24d1fde1..db3e16d8c1f79bcfb16c37af741cfc07df8d89b8 100644
--- a/src/spline.cpp
+++ b/src/spline.cpp
@@ -42,239 +42,250 @@
 #include <limits>
 
 Spline::Spline(const VisualToolBase &tl)
-: coord_translator(tl)
+    : coord_translator(tl)
 {
 }
 
-Vector2D Spline::ToScript(Vector2D vec) const {
-	return coord_translator.ToScriptCoords(vec) * scale;
+Vector2D Spline::ToScript(Vector2D vec) const
+{
+    return coord_translator.ToScriptCoords(vec) * scale;
 }
 
-Vector2D Spline::FromScript(Vector2D vec) const {
-	return coord_translator.FromScriptCoords(vec / scale);
+Vector2D Spline::FromScript(Vector2D vec) const
+{
+    return coord_translator.FromScriptCoords(vec / scale);
 }
 
-void Spline::SetScale(int new_scale) {
-	raw_scale = new_scale;
-	scale = 1 << (raw_scale - 1);
+void Spline::SetScale(int new_scale)
+{
+    raw_scale = new_scale;
+    scale = 1 << (raw_scale - 1);
 }
 
-std::string Spline::EncodeToAss() const {
-	std::string result;
-	result.reserve(size() * 10);
-	char last = 0;
-
-	for (auto const& pt : *this) {
-		switch (pt.type) {
-			case SplineCurve::POINT:
-				if (last != 'm') {
-					result += "m ";
-					last = 'm';
-				}
-				result += ToScript(pt.p1).DStr(' ');
-				break;
-
-			case SplineCurve::LINE:
-				if (last != 'l') {
-					result += "l ";
-					last = 'l';
-				}
-				result += ToScript(pt.p2).DStr(' ');
-				break;
-
-			case SplineCurve::BICUBIC:
-				if (last != 'b') {
-					result += "b ";
-					last = 'b';
-				}
-				result += ToScript(pt.p2).DStr(' ') + " ";
-				result += ToScript(pt.p3).DStr(' ') + " ";
-				result += ToScript(pt.p4).DStr(' ');
-				break;
-
-			default: break;
-		}
-		result += " ";
-	}
-	return result;
+std::string Spline::EncodeToAss() const
+{
+    std::string result;
+    result.reserve(size() * 10);
+    char last = 0;
+
+    for (auto const &pt : *this) {
+        switch (pt.type) {
+        case SplineCurve::POINT:
+            if (last != 'm') {
+                result += "m ";
+                last = 'm';
+            }
+            result += ToScript(pt.p1).DStr(' ');
+            break;
+
+        case SplineCurve::LINE:
+            if (last != 'l') {
+                result += "l ";
+                last = 'l';
+            }
+            result += ToScript(pt.p2).DStr(' ');
+            break;
+
+        case SplineCurve::BICUBIC:
+            if (last != 'b') {
+                result += "b ";
+                last = 'b';
+            }
+            result += ToScript(pt.p2).DStr(' ') + " ";
+            result += ToScript(pt.p3).DStr(' ') + " ";
+            result += ToScript(pt.p4).DStr(' ');
+            break;
+
+        default: break;
+        }
+        result += " ";
+    }
+    return result;
 }
 
-void Spline::DecodeFromAss(std::string const& str) {
-	// Clear current
-	clear();
-	std::vector<float> stack;
-
-	// Prepare
-	char command = 'm';
-	Vector2D pt{0, 0};
-
-	// Tokenize the string
-	for (auto token : agi::Split(str, ' ')) {
-		double n;
-		if (agi::util::try_parse(agi::str(token), &n)) {
-			stack.push_back(n);
-
-			// Move
-			if (stack.size() == 2 && command == 'm') {
-				pt = FromScript(Vector2D(stack[0], stack[1]));
-				stack.clear();
-
-				push_back(pt);
-			}
-
-			// Line
-			if (stack.size() == 2 && command == 'l') {
-				if (empty()) push_back(pt);
-
-				SplineCurve curve(pt, FromScript(Vector2D(stack[0], stack[1])));
-				push_back(curve);
-
-				pt = curve.p2;
-				stack.clear();
-			}
-
-			// Bicubic
-			else if (stack.size() == 6 && command == 'b') {
-				if (empty()) push_back(pt);
-
-				SplineCurve curve(pt,
-					FromScript(Vector2D(stack[0], stack[1])),
-					FromScript(Vector2D(stack[2], stack[3])),
-					FromScript(Vector2D(stack[4], stack[5])));
-				push_back(curve);
-
-				pt = curve.p4;
-				stack.clear();
-			}
-		}
-		// Got something else
-		else if (token.size() == 1) {
-			command = token[0];
-			stack.clear();
-		}
-	}
+void Spline::DecodeFromAss(std::string const &str)
+{
+    // Clear current
+    clear();
+    std::vector<float> stack;
+
+    // Prepare
+    char command = 'm';
+    Vector2D pt{0, 0};
+
+    // Tokenize the string
+    for (auto token : agi::Split(str, ' ')) {
+        double n;
+        if (agi::util::try_parse(agi::str(token), &n)) {
+            stack.push_back(n);
+
+            // Move
+            if (stack.size() == 2 && command == 'm') {
+                pt = FromScript(Vector2D(stack[0], stack[1]));
+                stack.clear();
+
+                push_back(pt);
+            }
+
+            // Line
+            if (stack.size() == 2 && command == 'l') {
+                if (empty()) push_back(pt);
+
+                SplineCurve curve(pt, FromScript(Vector2D(stack[0], stack[1])));
+                push_back(curve);
+
+                pt = curve.p2;
+                stack.clear();
+            }
+
+            // Bicubic
+            else if (stack.size() == 6 && command == 'b') {
+                if (empty()) push_back(pt);
+
+                SplineCurve curve(pt,
+                                  FromScript(Vector2D(stack[0], stack[1])),
+                                  FromScript(Vector2D(stack[2], stack[3])),
+                                  FromScript(Vector2D(stack[4], stack[5])));
+                push_back(curve);
+
+                pt = curve.p4;
+                stack.clear();
+            }
+        }
+        // Got something else
+        else if (token.size() == 1) {
+            command = token[0];
+            stack.clear();
+        }
+    }
 }
 
-void Spline::MovePoint(iterator curve,int point,Vector2D pos) {
-	auto prev = std::prev(curve, curve != begin());
-	auto next = std::next(curve);
-	if (next != end() && next->type == SplineCurve::POINT)
-		next = end();
-
-	// Modify
-	if (point == 0) {
-		curve->p1 = pos;
-		if (curve != begin() && curve->type != SplineCurve::POINT)
-			prev->EndPoint() = pos;
-		if (next != end() && curve->type == SplineCurve::POINT)
-			next->p1 = pos;
-	}
-	else if (point == 1) {
-		curve->p2 = pos;
-		if (next != end() && curve->type == SplineCurve::LINE)
-			next->p1 = pos;
-	}
-	else if (point == 2) {
-		curve->p3 = pos;
-	}
-	else if (point == 3) {
-		curve->p4 = pos;
-		if (next != end())
-			next->p1 = pos;
-	}
+void Spline::MovePoint(iterator curve, int point, Vector2D pos)
+{
+    auto prev = std::prev(curve, curve != begin());
+    auto next = std::next(curve);
+    if (next != end() && next->type == SplineCurve::POINT)
+        next = end();
+
+    // Modify
+    if (point == 0) {
+        curve->p1 = pos;
+        if (curve != begin() && curve->type != SplineCurve::POINT)
+            prev->EndPoint() = pos;
+        if (next != end() && curve->type == SplineCurve::POINT)
+            next->p1 = pos;
+    }
+    else if (point == 1) {
+        curve->p2 = pos;
+        if (next != end() && curve->type == SplineCurve::LINE)
+            next->p1 = pos;
+    }
+    else if (point == 2) {
+        curve->p3 = pos;
+    }
+    else if (point == 3) {
+        curve->p4 = pos;
+        if (next != end())
+            next->p1 = pos;
+    }
 }
 
-std::vector<float> Spline::GetPointList(std::vector<int>& first, std::vector<int>& count) {
-	first.clear();
-	count.clear();
-
-	std::vector<float> points;
-	points.reserve((size() + 1) * 2);
-	int curCount = 0;
-
-	// Generate points for each curve
-	for (auto const& elem : *this) {
-		if (elem.type == SplineCurve::POINT) {
-			if (curCount > 0)
-				count.push_back(curCount);
-
-			// start new path
-			first.push_back(points.size() / 2);
-			curCount = 0;
-		}
-		curCount += elem.GetPoints(points);
-	}
-
-	count.push_back(curCount);
-	return points;
+std::vector<float> Spline::GetPointList(std::vector<int> &first, std::vector<int> &count)
+{
+    first.clear();
+    count.clear();
+
+    std::vector<float> points;
+    points.reserve((size() + 1) * 2);
+    int curCount = 0;
+
+    // Generate points for each curve
+    for (auto const &elem : *this) {
+        if (elem.type == SplineCurve::POINT) {
+            if (curCount > 0)
+                count.push_back(curCount);
+
+            // start new path
+            first.push_back(points.size() / 2);
+            curCount = 0;
+        }
+        curCount += elem.GetPoints(points);
+    }
+
+    count.push_back(curCount);
+    return points;
 }
 
-std::vector<float> Spline::GetPointList(iterator curve) {
-	std::vector<float> points;
-	if (curve == end()) return points;
-	switch (curve->type) {
-		case SplineCurve::LINE:
-			points.push_back(curve->p1.X());
-			points.push_back(curve->p1.Y());
-			points.push_back(curve->p2.X());
-			points.push_back(curve->p2.Y());
-			break;
-
-		case SplineCurve::BICUBIC:
-			curve->GetPoints(points);
-			break;
-
-		default: break;
-	}
-	return points;
+std::vector<float> Spline::GetPointList(iterator curve)
+{
+    std::vector<float> points;
+    if (curve == end()) return points;
+    switch (curve->type) {
+    case SplineCurve::LINE:
+        points.push_back(curve->p1.X());
+        points.push_back(curve->p1.Y());
+        points.push_back(curve->p2.X());
+        points.push_back(curve->p2.Y());
+        break;
+
+    case SplineCurve::BICUBIC:
+        curve->GetPoints(points);
+        break;
+
+    default: break;
+    }
+    return points;
 }
 
-void Spline::GetClosestParametricPoint(Vector2D reference, iterator &curve, float &t, Vector2D &pt) {
-	curve = end();
-	t = 0.f;
-	if (empty()) return;
-
-	// Close the shape
-	emplace_back(back().EndPoint(), front().p1);
-
-	float closest = std::numeric_limits<float>::infinity();
-	size_t idx = 0;
-	for (size_t i = 0; i < size(); ++i) {
-		auto& cur = (*this)[i];
-		float param = cur.GetClosestParam(reference);
-		Vector2D p1 = cur.GetPoint(param);
-		float dist = (p1-reference).SquareLen();
-		if (dist < closest) {
-			closest = dist;
-			t = param;
-			idx = i;
-			pt = p1;
-		}
-	}
-
-	pop_back();
-	curve = begin() + idx;
+void Spline::GetClosestParametricPoint(Vector2D reference, iterator &curve, float &t, Vector2D &pt)
+{
+    curve = end();
+    t = 0.f;
+    if (empty()) return;
+
+    // Close the shape
+    emplace_back(back().EndPoint(), front().p1);
+
+    float closest = std::numeric_limits<float>::infinity();
+    size_t idx = 0;
+    for (size_t i = 0; i < size(); ++i) {
+        auto &cur = (*this)[i];
+        float param = cur.GetClosestParam(reference);
+        Vector2D p1 = cur.GetPoint(param);
+        float dist = (p1 - reference).SquareLen();
+        if (dist < closest) {
+            closest = dist;
+            t = param;
+            idx = i;
+            pt = p1;
+        }
+    }
+
+    pop_back();
+    curve = begin() + idx;
 }
 
-Vector2D Spline::GetClosestPoint(Vector2D reference) {
-	iterator curve;
-	float t;
-	Vector2D point;
-	GetClosestParametricPoint(reference, curve, t, point);
-	return point;
+Vector2D Spline::GetClosestPoint(Vector2D reference)
+{
+    iterator curve;
+    float t;
+    Vector2D point;
+    GetClosestParametricPoint(reference, curve, t, point);
+    return point;
 }
 
-void Spline::Smooth(float smooth) {
-	// See if there are enough curves
-	if (size() < 3) return;
-
-	// Smooth curve
-	for (auto cur = begin(); cur != end(); ++cur) {
-		auto prev_curve = prev(cur != begin() ? cur : end());
-		auto next_curve = next(cur);
-		if (next_curve == end())
-			next_curve = begin();
-
-		cur->Smooth(prev_curve->p1, next_curve->EndPoint(), smooth);
-	}
+void Spline::Smooth(float smooth)
+{
+    // See if there are enough curves
+    if (size() < 3) return;
+
+    // Smooth curve
+    for (auto cur = begin(); cur != end(); ++cur) {
+        auto prev_curve = prev(cur != begin() ? cur : end());
+        auto next_curve = next(cur);
+        if (next_curve == end())
+            next_curve = begin();
+
+        cur->Smooth(prev_curve->p1, next_curve->EndPoint(), smooth);
+    }
 }
diff --git a/src/spline.h b/src/spline.h
index 2594837e8ce5c27b7c9913ad617646ca1c638463..375b4ade4aca8f6b57ebc5c658b2e8b5e6bfce22 100644
--- a/src/spline.h
+++ b/src/spline.h
@@ -35,76 +35,76 @@
 class VisualToolBase;
 
 class Spline final : private std::vector<SplineCurve> {
-	/// Visual tool to do the conversion between script and video pixels
-	const VisualToolBase &coord_translator;
-	/// Spline scale
-	int scale = 0;
-	int raw_scale = 0;
+    /// Visual tool to do the conversion between script and video pixels
+    const VisualToolBase &coord_translator;
+    /// Spline scale
+    int scale = 0;
+    int raw_scale = 0;
 
-	/// Video coordinates -> Script coordinates
-	Vector2D ToScript(Vector2D vec) const;
+    /// Video coordinates -> Script coordinates
+    Vector2D ToScript(Vector2D vec) const;
 
-	/// Script coordinates -> Video coordinates
-	Vector2D FromScript(Vector2D vec) const;
+    /// Script coordinates -> Video coordinates
+    Vector2D FromScript(Vector2D vec) const;
 public:
-	Spline(const VisualToolBase &scale);
+    Spline(const VisualToolBase &scale);
 
-	/// Encode to an ASS vector drawing
-	std::string EncodeToAss() const;
+    /// Encode to an ASS vector drawing
+    std::string EncodeToAss() const;
 
-	/// Decode an ASS vector drawing
-	void DecodeFromAss(std::string const& str);
+    /// Decode an ASS vector drawing
+    void DecodeFromAss(std::string const &str);
 
-	/// Set the scale
-	/// @param new_scale Power-of-two to scale coordinates by
-	void SetScale(int new_scale);
-	/// Get the current scale
-	int GetScale() const { return raw_scale; }
+    /// Set the scale
+    /// @param new_scale Power-of-two to scale coordinates by
+    void SetScale(int new_scale);
+    /// Get the current scale
+    int GetScale() const { return raw_scale; }
 
-	/// @brief Moves a specific point in the spline
-	/// @param curve Curve which the point is in
-	/// @param point Index in the curve
-	/// @param pos New position
-	void MovePoint(iterator curve, int point, Vector2D pos);
+    /// @brief Moves a specific point in the spline
+    /// @param curve Curve which the point is in
+    /// @param point Index in the curve
+    /// @param pos New position
+    void MovePoint(iterator curve, int point, Vector2D pos);
 
-	/// Smooth the spline
-	void Smooth(float smooth=1.0f);
+    /// Smooth the spline
+    void Smooth(float smooth = 1.0f);
 
-	/// Gets a list of points in the curve
-	std::vector<float> GetPointList(std::vector<int>& first, std::vector<int>& count);
-	/// Gets a list of points in the curve
-	std::vector<float> GetPointList(iterator curve);
+    /// Gets a list of points in the curve
+    std::vector<float> GetPointList(std::vector<int> &first, std::vector<int> &count);
+    /// Gets a list of points in the curve
+    std::vector<float> GetPointList(iterator curve);
 
-	/// Get t value and curve of the point closest to reference
-	void GetClosestParametricPoint(Vector2D reference, iterator& curve, float &t, Vector2D &point);
-	/// Get closest point on the curve to reference
-	Vector2D GetClosestPoint(Vector2D reference);
-	Vector2D GetClosestControlPoint(Vector2D reference);
+    /// Get t value and curve of the point closest to reference
+    void GetClosestParametricPoint(Vector2D reference, iterator &curve, float &t, Vector2D &point);
+    /// Get closest point on the curve to reference
+    Vector2D GetClosestPoint(Vector2D reference);
+    Vector2D GetClosestControlPoint(Vector2D reference);
 
-	using std::vector<SplineCurve>::value_type;
-	using std::vector<SplineCurve>::pointer;
-	using std::vector<SplineCurve>::reference;
-	using std::vector<SplineCurve>::const_reference;
-	using std::vector<SplineCurve>::size_type;
-	using std::vector<SplineCurve>::difference_type;
-	using std::vector<SplineCurve>::iterator;
-	using std::vector<SplineCurve>::const_iterator;
-	using std::vector<SplineCurve>::reverse_iterator;
-	using std::vector<SplineCurve>::const_reverse_iterator;
+    using std::vector<SplineCurve>::value_type;
+    using std::vector<SplineCurve>::pointer;
+    using std::vector<SplineCurve>::reference;
+    using std::vector<SplineCurve>::const_reference;
+    using std::vector<SplineCurve>::size_type;
+    using std::vector<SplineCurve>::difference_type;
+    using std::vector<SplineCurve>::iterator;
+    using std::vector<SplineCurve>::const_iterator;
+    using std::vector<SplineCurve>::reverse_iterator;
+    using std::vector<SplineCurve>::const_reverse_iterator;
 
-	using std::vector<SplineCurve>::back;
-	using std::vector<SplineCurve>::begin;
-	using std::vector<SplineCurve>::clear;
-	using std::vector<SplineCurve>::emplace_back;
-	using std::vector<SplineCurve>::empty;
-	using std::vector<SplineCurve>::end;
-	using std::vector<SplineCurve>::erase;
-	using std::vector<SplineCurve>::front;
-	using std::vector<SplineCurve>::insert;
-	using std::vector<SplineCurve>::operator[];
-	using std::vector<SplineCurve>::pop_back;
-	using std::vector<SplineCurve>::push_back;
-	using std::vector<SplineCurve>::rbegin;
-	using std::vector<SplineCurve>::rend;
-	using std::vector<SplineCurve>::size;
+    using std::vector<SplineCurve>::back;
+    using std::vector<SplineCurve>::begin;
+    using std::vector<SplineCurve>::clear;
+    using std::vector<SplineCurve>::emplace_back;
+    using std::vector<SplineCurve>::empty;
+    using std::vector<SplineCurve>::end;
+    using std::vector<SplineCurve>::erase;
+    using std::vector<SplineCurve>::front;
+    using std::vector<SplineCurve>::insert;
+    using std::vector<SplineCurve>::operator[];
+    using std::vector<SplineCurve>::pop_back;
+    using std::vector<SplineCurve>::push_back;
+    using std::vector<SplineCurve>::rbegin;
+    using std::vector<SplineCurve>::rend;
+    using std::vector<SplineCurve>::size;
 };
diff --git a/src/spline_curve.cpp b/src/spline_curve.cpp
index b1a799a0f538df5b964ffe080e168c26b8452da2..6471d2477287a6bbc4c81936f0463cab0e6da132 100644
--- a/src/spline_curve.cpp
+++ b/src/spline_curve.cpp
@@ -40,162 +40,172 @@
 SplineCurve::SplineCurve(Vector2D p1) : p1(p1), type(POINT) { }
 SplineCurve::SplineCurve(Vector2D p1, Vector2D p2) : p1(p1), p2(p2), type(LINE) { }
 SplineCurve::SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4)
-: p1(p1), p2(p2), p3(p3), p4(p4), type(BICUBIC)
+    : p1(p1), p2(p2), p3(p3), p4(p4), type(BICUBIC)
 {
 }
 
-std::pair<SplineCurve, SplineCurve> SplineCurve::Split(float t) {
-	if (type == LINE) {
-		Vector2D m = p1 * (1 - t) + p2 * t;
-		return {
-			SplineCurve(p1, m),
-			SplineCurve(m, p2)
-		};
-	}
-	else if (type == BICUBIC) {
-		float u = 1 - t;
-		Vector2D p12   = p1   * u + p2   * t;
-		Vector2D p23   = p2   * u + p3   * t;
-		Vector2D p34   = p3   * u + p4   * t;
-		Vector2D p123  = p12  * u + p23  * t;
-		Vector2D p234  = p23  * u + p34  * t;
-		Vector2D p1234 = p123 * u + p234 * t;
-
-		return {
-			SplineCurve(p1, p12, p123, p1234),
-			SplineCurve(p1234, p234, p34, p4)
-		};
-	}
-	return {SplineCurve(p1), SplineCurve(p1)};
+std::pair<SplineCurve, SplineCurve> SplineCurve::Split(float t)
+{
+    if (type == LINE) {
+        Vector2D m = p1 * (1 - t) + p2 * t;
+        return {
+            SplineCurve(p1, m),
+            SplineCurve(m, p2)
+        };
+    }
+    else if (type == BICUBIC) {
+        float u = 1 - t;
+        Vector2D p12   = p1   * u + p2   * t;
+        Vector2D p23   = p2   * u + p3   * t;
+        Vector2D p34   = p3   * u + p4   * t;
+        Vector2D p123  = p12  * u + p23  * t;
+        Vector2D p234  = p23  * u + p34  * t;
+        Vector2D p1234 = p123 * u + p234 * t;
+
+        return {
+            SplineCurve(p1, p12, p123, p1234),
+            SplineCurve(p1234, p234, p34, p4)
+        };
+    }
+    return {SplineCurve(p1), SplineCurve(p1)};
 }
 
-void SplineCurve::Smooth(Vector2D p0, Vector2D p5, float smooth) {
-	if (type != LINE || p1 == p2) return;
-	smooth = mid(0.f, smooth, 1.f);
+void SplineCurve::Smooth(Vector2D p0, Vector2D p5, float smooth)
+{
+    if (type != LINE || p1 == p2) return;
+    smooth = mid(0.f, smooth, 1.f);
 
-	// Calculate intermediate points
-	Vector2D c1 = (p0 + p1) / 2.f;
-	Vector2D c2 = (p1 + p2) / 2.f;
-	Vector2D c3 = (p2 + p5) / 2.f;
+    // Calculate intermediate points
+    Vector2D c1 = (p0 + p1) / 2.f;
+    Vector2D c2 = (p1 + p2) / 2.f;
+    Vector2D c3 = (p2 + p5) / 2.f;
 
-	float len1 = (p1 - p0).Len();
-	float len2 = (p2 - p1).Len();
-	float len3 = (p5 - p2).Len();
+    float len1 = (p1 - p0).Len();
+    float len2 = (p2 - p1).Len();
+    float len3 = (p5 - p2).Len();
 
-	float k1 = len1 / (len1 + len2);
-	float k2 = len2 / (len2 + len3);
+    float k1 = len1 / (len1 + len2);
+    float k2 = len2 / (len2 + len3);
 
-	Vector2D m1 = c1 + (c2 - c1) * k1;
-	Vector2D m2 = c2 + (c3 - c2) * k2;
+    Vector2D m1 = c1 + (c2 - c1) * k1;
+    Vector2D m2 = c2 + (c3 - c2) * k2;
 
-	// Set curve points
-	p4 = p2;
-	p3 = m2 + (c2 - m2) * smooth + p2 - m2;
-	p2 = m1 + (c2 - m1) * smooth + p1 - m1;
-	type = BICUBIC;
+    // Set curve points
+    p4 = p2;
+    p3 = m2 + (c2 - m2) * smooth + p2 - m2;
+    p2 = m1 + (c2 - m1) * smooth + p1 - m1;
+    type = BICUBIC;
 }
 
-Vector2D SplineCurve::GetPoint(float t) const {
-	float u = 1.f - t;
+Vector2D SplineCurve::GetPoint(float t) const
+{
+    float u = 1.f - t;
 
-	if (type == POINT)
-		return p1;
-	if (type == LINE)
-		return p1 * u + p2 * t;
+    if (type == POINT)
+        return p1;
+    if (type == LINE)
+        return p1 * u + p2 * t;
 
-	return p1*u*u*u + 3*p2*t*u*u + 3*p3*t*t*u + p4*t*t*t;
+    return p1 * u * u * u + 3 * p2 * t * u * u + 3 * p3 * t * t * u + p4 * t * t * t;
 }
 
-Vector2D& SplineCurve::EndPoint() {
-	switch (type) {
-		case POINT:   return p1;
-		case LINE:    return p2;
-		case BICUBIC: return p4;
-		default:      return p1;
-	}
+Vector2D &SplineCurve::EndPoint()
+{
+    switch (type) {
+    case POINT:   return p1;
+    case LINE:    return p2;
+    case BICUBIC: return p4;
+    default:      return p1;
+    }
 }
 
-Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const {
-	return GetPoint(GetClosestParam(ref));
+Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const
+{
+    return GetPoint(GetClosestParam(ref));
 }
 
-float SplineCurve::GetClosestParam(Vector2D ref) const {
-	if (type == LINE)
-		return GetClosestSegmentPart(p1, p2, ref);
-
-	if (type == BICUBIC) {
-		int steps = 100;
-		float bestDist = std::numeric_limits<float>::max();
-		float bestT = 0.f;
-		for (int i = 0; i <= steps; ++i) {
-			float t = i / float(steps);
-			float dist = (GetPoint(t) - ref).SquareLen();
-			if (dist < bestDist) {
-				bestDist = dist;
-				bestT = t;
-			}
-		}
-		return bestT;
-	}
-
-	return 0.f;
+float SplineCurve::GetClosestParam(Vector2D ref) const
+{
+    if (type == LINE)
+        return GetClosestSegmentPart(p1, p2, ref);
+
+    if (type == BICUBIC) {
+        int steps = 100;
+        float bestDist = std::numeric_limits<float>::max();
+        float bestT = 0.f;
+        for (int i = 0; i <= steps; ++i) {
+            float t = i / float(steps);
+            float dist = (GetPoint(t) - ref).SquareLen();
+            if (dist < bestDist) {
+                bestDist = dist;
+                bestT = t;
+            }
+        }
+        return bestT;
+    }
+
+    return 0.f;
 }
 
-float SplineCurve::GetQuickDistance(Vector2D ref) const {
-	if (type == BICUBIC) {
-		float lens[] = {
-			GetClosestSegmentDistance(p1, p2, ref),
-			GetClosestSegmentDistance(p2, p3, ref),
-			GetClosestSegmentDistance(p3, p4, ref),
-			GetClosestSegmentDistance(p4, p1, ref),
-			GetClosestSegmentDistance(p1, p3, ref),
-			GetClosestSegmentDistance(p2, p4, ref)
-		};
-		return *std::min_element(lens, lens + 6);
-	}
-	return (GetClosestPoint(ref) - ref).Len();
+float SplineCurve::GetQuickDistance(Vector2D ref) const
+{
+    if (type == BICUBIC) {
+        float lens[] = {
+            GetClosestSegmentDistance(p1, p2, ref),
+            GetClosestSegmentDistance(p2, p3, ref),
+            GetClosestSegmentDistance(p3, p4, ref),
+            GetClosestSegmentDistance(p4, p1, ref),
+            GetClosestSegmentDistance(p1, p3, ref),
+            GetClosestSegmentDistance(p2, p4, ref)
+        };
+        return *std::min_element(lens, lens + 6);
+    }
+    return (GetClosestPoint(ref) - ref).Len();
 }
 
-float SplineCurve::GetClosestSegmentPart(Vector2D pt1, Vector2D pt2, Vector2D pt3) const {
-	return mid(0.f, (pt3 - pt1).Dot(pt2 - pt1) / (pt2 - pt1).SquareLen(), 1.f);
+float SplineCurve::GetClosestSegmentPart(Vector2D pt1, Vector2D pt2, Vector2D pt3) const
+{
+    return mid(0.f, (pt3 - pt1).Dot(pt2 - pt1) / (pt2 - pt1).SquareLen(), 1.f);
 }
 
-float SplineCurve::GetClosestSegmentDistance(Vector2D pt1, Vector2D pt2, Vector2D pt3) const {
-	float t = GetClosestSegmentPart(pt1, pt2, pt3);
-	return (pt1 * (1.f - t) + pt2 * t - pt3).Len();
+float SplineCurve::GetClosestSegmentDistance(Vector2D pt1, Vector2D pt2, Vector2D pt3) const
+{
+    float t = GetClosestSegmentPart(pt1, pt2, pt3);
+    return (pt1 * (1.f - t) + pt2 * t - pt3).Len();
 }
 
-int SplineCurve::GetPoints(std::vector<float> &points) const {
-	switch (type) {
-		case POINT:
-			points.push_back(p1.X());
-			points.push_back(p1.Y());
-			return 1;
-
-		case LINE:
-			points.push_back(p2.X());
-			points.push_back(p2.Y());
-			return 1;
-
-		case BICUBIC: {
-			int len = int(
-				(p2 - p1).Len() +
-				(p3 - p2).Len() +
-				(p4 - p3).Len());
-			int steps = len/8;
-
-			for (int i = 0; i <= steps; ++i) {
-				// Get t and t-1 (u)
-				float t = i / float(steps);
-				Vector2D p = GetPoint(t);
-				points.push_back(p.X());
-				points.push_back(p.Y());
-			}
-
-			return steps + 1;
-		}
-
-		default:
-			return 0;
-	}
+int SplineCurve::GetPoints(std::vector<float> &points) const
+{
+    switch (type) {
+    case POINT:
+        points.push_back(p1.X());
+        points.push_back(p1.Y());
+        return 1;
+
+    case LINE:
+        points.push_back(p2.X());
+        points.push_back(p2.Y());
+        return 1;
+
+    case BICUBIC: {
+        int len = int(
+                      (p2 - p1).Len() +
+                      (p3 - p2).Len() +
+                      (p4 - p3).Len());
+        int steps = len / 8;
+
+        for (int i = 0; i <= steps; ++i) {
+            // Get t and t-1 (u)
+            float t = i / float(steps);
+            Vector2D p = GetPoint(t);
+            points.push_back(p.X());
+            points.push_back(p.Y());
+        }
+
+        return steps + 1;
+    }
+
+    default:
+        return 0;
+    }
 }
diff --git a/src/spline_curve.h b/src/spline_curve.h
index c6c3c0b35075f0fff0143228e9ceffd002744a62..4fb297dd3067ce1eb14041623a41828af871429e 100644
--- a/src/spline_curve.h
+++ b/src/spline_curve.h
@@ -37,49 +37,49 @@
 #include <vector>
 
 class SplineCurve {
-	/// Closest t in segment p1-p2 to point p3
-	float GetClosestSegmentPart(Vector2D p1, Vector2D p2, Vector2D p3) const;
+    /// Closest t in segment p1-p2 to point p3
+    float GetClosestSegmentPart(Vector2D p1, Vector2D p2, Vector2D p3) const;
 
-	/// Closest distance between p3 and segment p1-p2
-	float GetClosestSegmentDistance(Vector2D p1, Vector2D p2, Vector2D p3) const;
+    /// Closest distance between p3 and segment p1-p2
+    float GetClosestSegmentDistance(Vector2D p1, Vector2D p2, Vector2D p3) const;
 public:
-	enum CurveType {
-		POINT,
-		LINE,
-		BICUBIC
-	};
+    enum CurveType {
+        POINT,
+        LINE,
+        BICUBIC
+    };
 
-	Vector2D p1;
-	Vector2D p2;
-	Vector2D p3;
-	Vector2D p4;
+    Vector2D p1;
+    Vector2D p2;
+    Vector2D p3;
+    Vector2D p4;
 
-	CurveType type;
+    CurveType type;
 
-	SplineCurve(Vector2D p1 = Vector2D(0, 0));
-	SplineCurve(Vector2D p1, Vector2D p2);
-	SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4);
+    SplineCurve(Vector2D p1 = Vector2D(0, 0));
+    SplineCurve(Vector2D p1, Vector2D p2);
+    SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4);
 
-	/// @brief Split a curve in two using the de Casteljau algorithm
-	/// @param t Split point from 0-1
-	/// @return Curve before and after the split point
-	std::pair<SplineCurve, SplineCurve> Split(float t = 0.5f);
+    /// @brief Split a curve in two using the de Casteljau algorithm
+    /// @param t Split point from 0-1
+    /// @return Curve before and after the split point
+    std::pair<SplineCurve, SplineCurve> Split(float t = 0.5f);
 
-	/// @brief Smooths the curve
-	/// @note Based on http://antigrain.com/research/bezier_interpolation/index.html
-	void Smooth(Vector2D prev, Vector2D next, float smooth = 1.0f);
+    /// @brief Smooths the curve
+    /// @note Based on http://antigrain.com/research/bezier_interpolation/index.html
+    void Smooth(Vector2D prev, Vector2D next, float smooth = 1.0f);
 
-	Vector2D GetPoint(float t) const;
-	Vector2D& EndPoint();
-	/// Get point on the curve closest to reference
-	Vector2D GetClosestPoint(Vector2D ref) const;
-	/// Get t value for the closest point to reference
-	float GetClosestParam(Vector2D ref) const;
-	/// Get distance from ref to the closest point on the curve
-	float GetQuickDistance(Vector2D ref) const;
+    Vector2D GetPoint(float t) const;
+    Vector2D &EndPoint();
+    /// Get point on the curve closest to reference
+    Vector2D GetClosestPoint(Vector2D ref) const;
+    /// Get t value for the closest point to reference
+    float GetClosestParam(Vector2D ref) const;
+    /// Get distance from ref to the closest point on the curve
+    float GetQuickDistance(Vector2D ref) const;
 
-	/// Get the coordinates of each point on this curve
-	/// @param[out] points Vector to add points to
-	/// @return Number of points in the curve
-	int GetPoints(std::vector<float> &points) const;
+    /// Get the coordinates of each point on this curve
+    /// @param[out] points Vector to add points to
+    /// @return Number of points in the curve
+    int GetPoints(std::vector<float> &points) const;
 };
diff --git a/src/string_codec.cpp b/src/string_codec.cpp
index 6ad7c7ca702151e50a4bf6dcad3de14abde6883f..d7a177f13810b6fe80d80a253b1ccb0bfc22e9a8 100644
--- a/src/string_codec.cpp
+++ b/src/string_codec.cpp
@@ -39,28 +39,30 @@
 
 #include <libaegisub/format.h>
 
-std::string inline_string_encode(const std::string &input) {
-	std::string output;
-	output.reserve(input.size());
-	for (char c : input) {
-		if (c <= 0x1F || c == 0x23 || c == 0x2C || c == 0x3A || c == 0x7C)
-			output += agi::format("#%02X", (unsigned char)c);
-		else
-			output += c;
-	}
-	return output;
+std::string inline_string_encode(const std::string &input)
+{
+    std::string output;
+    output.reserve(input.size());
+    for (char c : input) {
+        if (c <= 0x1F || c == 0x23 || c == 0x2C || c == 0x3A || c == 0x7C)
+            output += agi::format("#%02X", (unsigned char)c);
+        else
+            output += c;
+    }
+    return output;
 }
 
-std::string inline_string_decode(const std::string &input) {
-	std::string output;
-	output.reserve(input.size());
-	for (size_t i = 0; i < input.size(); ++i) {
-		if (input[i] != '#' || i + 2 > input.size())
-			output += input[i];
-		else {
-			output += (char)strtol(input.substr(i + 1, 2).c_str(), nullptr, 16);
-			i += 2;
-		}
-	}
-	return output;
+std::string inline_string_decode(const std::string &input)
+{
+    std::string output;
+    output.reserve(input.size());
+    for (size_t i = 0; i < input.size(); ++i) {
+        if (input[i] != '#' || i + 2 > input.size())
+            output += input[i];
+        else {
+            output += (char)strtol(input.substr(i + 1, 2).c_str(), nullptr, 16);
+            i += 2;
+        }
+    }
+    return output;
 }
diff --git a/src/subs_controller.cpp b/src/subs_controller.cpp
index 212d2dbadca964c09bdc0b9309279c5a11f62f88..2cee84e7834c958447a895846a8c3f6f36b678cc 100644
--- a/src/subs_controller.cpp
+++ b/src/subs_controller.cpp
@@ -41,368 +41,389 @@
 #include <wx/msgdlg.h>
 
 namespace {
-	void autosave_timer_changed(wxTimer *timer) {
-		int freq = OPT_GET("App/Auto/Save Every Seconds")->GetInt();
-		if (freq > 0 && OPT_GET("App/Auto/Save")->GetBool())
-			timer->Start(freq * 1000);
-		else
-			timer->Stop();
-	}
+void autosave_timer_changed(wxTimer *timer)
+{
+    int freq = OPT_GET("App/Auto/Save Every Seconds")->GetInt();
+    if (freq > 0 && OPT_GET("App/Auto/Save")->GetBool())
+        timer->Start(freq * 1000);
+    else
+        timer->Stop();
+}
 }
 
 struct SubsController::UndoInfo {
-	wxString undo_description;
-	int commit_id;
-
-	std::vector<std::pair<std::string, std::string>> script_info;
-	std::vector<AssStyle> styles;
-	std::vector<AssDialogueBase> events;
-	std::vector<AssAttachment> attachments;
-	std::vector<ExtradataEntry> extradata;
-
-	mutable std::vector<int> selection;
-	int active_line_id = 0;
-	int pos = 0, sel_start = 0, sel_end = 0;
-
-	UndoInfo(const agi::Context *c, wxString const& d, int commit_id)
-	: undo_description(d)
-	, commit_id(commit_id)
-	, attachments(c->ass->Attachments)
-	, extradata(c->ass->Extradata)
-	{
-		script_info.reserve(c->ass->Info.size());
-		for (auto const& info : c->ass->Info)
-			script_info.emplace_back(info.Key(), info.Value());
-
-		styles.reserve(c->ass->Styles.size());
-		styles.assign(c->ass->Styles.begin(), c->ass->Styles.end());
-
-		events.reserve(c->ass->Events.size());
-		events.assign(c->ass->Events.begin(), c->ass->Events.end());
-
-		UpdateActiveLine(c);
-		UpdateSelection(c);
-		UpdateTextSelection(c);
-	}
-
-	void Apply(agi::Context *c) const {
-		// Keep old dialogue lines alive until after the commit is complete
-		// since a bunch of stuff holds references to them
-		AssFile old;
-		old.Events.swap(c->ass->Events);
-		c->ass->Info.clear();
-		c->ass->Attachments.clear();
-		c->ass->Styles.clear();
-		c->ass->Extradata.clear();
-
-		sort(begin(selection), end(selection));
-
-		AssDialogue *active_line = nullptr;
-		Selection new_sel;
-
-		for (auto const& info : script_info)
-			c->ass->Info.push_back(*new AssInfo(info.first, info.second));
-		for (auto const& style : styles)
-			c->ass->Styles.push_back(*new AssStyle(style));
-		c->ass->Attachments = attachments;
-		for (auto const& event : events) {
-			auto copy = new AssDialogue(event);
-			c->ass->Events.push_back(*copy);
-			if (copy->Id == active_line_id)
-				active_line = copy;
-			if (binary_search(begin(selection), end(selection), copy->Id))
-				new_sel.insert(copy);
-		}
-		c->ass->Extradata = extradata;
-
-		c->ass->Commit("", AssFile::COMMIT_NEW);
-		c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
-
-		c->textSelectionController->SetInsertionPoint(pos);
-		c->textSelectionController->SetSelection(sel_start, sel_end);
-	}
-
-	void UpdateActiveLine(const agi::Context *c) {
-		auto line = c->selectionController->GetActiveLine();
-		if (line)
-			active_line_id = line->Id;
-	}
-
-	void UpdateSelection(const agi::Context *c) {
-		auto const& sel = c->selectionController->GetSelectedSet();
-		selection.clear();
-		selection.reserve(sel.size());
-		for (const auto diag : sel)
-			selection.push_back(diag->Id);
-	}
-
-	void UpdateTextSelection(const agi::Context *c) {
-		pos = c->textSelectionController->GetInsertionPoint();
-		sel_start = c->textSelectionController->GetSelectionStart();
-		sel_end = c->textSelectionController->GetSelectionEnd();
-	}
+    wxString undo_description;
+    int commit_id;
+
+    std::vector<std::pair<std::string, std::string>> script_info;
+    std::vector<AssStyle> styles;
+    std::vector<AssDialogueBase> events;
+    std::vector<AssAttachment> attachments;
+    std::vector<ExtradataEntry> extradata;
+
+    mutable std::vector<int> selection;
+    int active_line_id = 0;
+    int pos = 0, sel_start = 0, sel_end = 0;
+
+    UndoInfo(const agi::Context *c, wxString const &d, int commit_id)
+        : undo_description(d)
+        , commit_id(commit_id)
+        , attachments(c->ass->Attachments)
+        , extradata(c->ass->Extradata) {
+        script_info.reserve(c->ass->Info.size());
+        for (auto const &info : c->ass->Info)
+            script_info.emplace_back(info.Key(), info.Value());
+
+        styles.reserve(c->ass->Styles.size());
+        styles.assign(c->ass->Styles.begin(), c->ass->Styles.end());
+
+        events.reserve(c->ass->Events.size());
+        events.assign(c->ass->Events.begin(), c->ass->Events.end());
+
+        UpdateActiveLine(c);
+        UpdateSelection(c);
+        UpdateTextSelection(c);
+    }
+
+    void Apply(agi::Context *c) const {
+        // Keep old dialogue lines alive until after the commit is complete
+        // since a bunch of stuff holds references to them
+        AssFile old;
+        old.Events.swap(c->ass->Events);
+        c->ass->Info.clear();
+        c->ass->Attachments.clear();
+        c->ass->Styles.clear();
+        c->ass->Extradata.clear();
+
+        sort(begin(selection), end(selection));
+
+        AssDialogue *active_line = nullptr;
+        Selection new_sel;
+
+        for (auto const &info : script_info)
+            c->ass->Info.push_back(*new AssInfo(info.first, info.second));
+        for (auto const &style : styles)
+            c->ass->Styles.push_back(*new AssStyle(style));
+        c->ass->Attachments = attachments;
+        for (auto const &event : events) {
+            auto copy = new AssDialogue(event);
+            c->ass->Events.push_back(*copy);
+            if (copy->Id == active_line_id)
+                active_line = copy;
+            if (binary_search(begin(selection), end(selection), copy->Id))
+                new_sel.insert(copy);
+        }
+        c->ass->Extradata = extradata;
+
+        c->ass->Commit("", AssFile::COMMIT_NEW);
+        c->selectionController->SetSelectionAndActive(std::move(new_sel), active_line);
+
+        c->textSelectionController->SetInsertionPoint(pos);
+        c->textSelectionController->SetSelection(sel_start, sel_end);
+    }
+
+    void UpdateActiveLine(const agi::Context *c) {
+        auto line = c->selectionController->GetActiveLine();
+        if (line)
+            active_line_id = line->Id;
+    }
+
+    void UpdateSelection(const agi::Context *c) {
+        auto const &sel = c->selectionController->GetSelectedSet();
+        selection.clear();
+        selection.reserve(sel.size());
+        for (const auto diag : sel)
+            selection.push_back(diag->Id);
+    }
+
+    void UpdateTextSelection(const agi::Context *c) {
+        pos = c->textSelectionController->GetInsertionPoint();
+        sel_start = c->textSelectionController->GetSelectionStart();
+        sel_end = c->textSelectionController->GetSelectionEnd();
+    }
 };
 
 SubsController::SubsController(agi::Context *context)
-: context(context)
-, undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this))
-, text_selection_connection(context->textSelectionController->AddSelectionListener(&SubsController::OnTextSelectionChanged, this))
-, autosave_queue(agi::dispatch::Create())
+    : context(context)
+    , undo_connection(context->ass->AddUndoManager(&SubsController::OnCommit, this))
+    , text_selection_connection(context->textSelectionController->AddSelectionListener(&SubsController::OnTextSelectionChanged, this))
+    , autosave_queue(agi::dispatch::Create())
 {
-	autosave_timer_changed(&autosave_timer);
-	OPT_SUB("App/Auto/Save", [=] { autosave_timer_changed(&autosave_timer); });
-	OPT_SUB("App/Auto/Save Every Seconds", [=] { autosave_timer_changed(&autosave_timer); });
-	autosave_timer.Bind(wxEVT_TIMER, [=](wxTimerEvent&) { AutoSave(); });
+    autosave_timer_changed(&autosave_timer);
+    OPT_SUB("App/Auto/Save", [ = ] { autosave_timer_changed(&autosave_timer); });
+    OPT_SUB("App/Auto/Save Every Seconds", [ = ] { autosave_timer_changed(&autosave_timer); });
+    autosave_timer.Bind(wxEVT_TIMER, [ = ](wxTimerEvent &) { AutoSave(); });
 }
 
-SubsController::~SubsController() {
-	// Make sure there are no autosaves in progress
-	autosave_queue->Sync([]{ });
+SubsController::~SubsController()
+{
+    // Make sure there are no autosaves in progress
+    autosave_queue->Sync([] { });
 }
 
-void SubsController::SetSelectionController(SelectionController *selection_controller) {
-	active_line_connection = context->selectionController->AddActiveLineListener(&SubsController::OnActiveLineChanged, this);
-	selection_connection = context->selectionController->AddSelectionListener(&SubsController::OnSelectionChanged, this);
+void SubsController::SetSelectionController(SelectionController *selection_controller)
+{
+    active_line_connection = context->selectionController->AddActiveLineListener(&SubsController::OnActiveLineChanged, this);
+    selection_connection = context->selectionController->AddSelectionListener(&SubsController::OnSelectionChanged, this);
 }
 
-ProjectProperties SubsController::Load(agi::fs::path const& filename, std::string charset) {
-	AssFile temp;
-
-	SubtitleFormat::GetReader(filename, charset)->ReadFile(&temp, filename, context->project->Timecodes(), charset);
-
-	context->ass->swap(temp);
-	auto props = context->ass->Properties;
-
-	SetFileName(filename);
-
-	// Push the initial state of the file onto the undo stack
-	undo_stack.clear();
-	redo_stack.clear();
-	autosaved_commit_id = saved_commit_id = commit_id + 1;
-	context->ass->Commit("", AssFile::COMMIT_NEW);
-
-	// Save backup of file
-	if (CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) {
-		auto path_str = OPT_GET("Path/Auto/Backup")->GetString();
-		agi::fs::path path;
-		if (path_str.empty())
-			path = filename.parent_path();
-		else
-			path = context->path->Decode(path_str);
-		agi::fs::CreateDirectory(path);
-		agi::fs::Copy(filename, path/(filename.stem().string() + ".ORIGINAL" + filename.extension().string()));
-	}
-
-	FileOpen(filename);
-	return props;
+ProjectProperties SubsController::Load(agi::fs::path const &filename, std::string charset)
+{
+    AssFile temp;
+
+    SubtitleFormat::GetReader(filename, charset)->ReadFile(&temp, filename, context->project->Timecodes(), charset);
+
+    context->ass->swap(temp);
+    auto props = context->ass->Properties;
+
+    SetFileName(filename);
+
+    // Push the initial state of the file onto the undo stack
+    undo_stack.clear();
+    redo_stack.clear();
+    autosaved_commit_id = saved_commit_id = commit_id + 1;
+    context->ass->Commit("", AssFile::COMMIT_NEW);
+
+    // Save backup of file
+    if (CanSave() && OPT_GET("App/Auto/Backup")->GetBool()) {
+        auto path_str = OPT_GET("Path/Auto/Backup")->GetString();
+        agi::fs::path path;
+        if (path_str.empty())
+            path = filename.parent_path();
+        else
+            path = context->path->Decode(path_str);
+        agi::fs::CreateDirectory(path);
+        agi::fs::Copy(filename, path / (filename.stem().string() + ".ORIGINAL" + filename.extension().string()));
+    }
+
+    FileOpen(filename);
+    return props;
 }
 
-void SubsController::Save(agi::fs::path const& filename, std::string const& encoding) {
-	const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
-	if (!writer)
-		throw agi::InvalidInputException("Unknown file type.");
-
-	int old_autosaved_commit_id = autosaved_commit_id, old_saved_commit_id = saved_commit_id;
-	try {
-		autosaved_commit_id = saved_commit_id = commit_id;
-
-		// Have to set this now for the sake of things that want to save paths
-		// relative to the script in the header
-		this->filename = filename;
-		context->path->SetToken("?script", filename.parent_path());
-
-		context->ass->CleanExtradata();
-		writer->WriteFile(context->ass.get(), filename, 0, encoding);
-		FileSave();
-	}
-	catch (...) {
-		autosaved_commit_id = old_autosaved_commit_id;
-		saved_commit_id = old_saved_commit_id;
-		throw;
-	}
-
-	SetFileName(filename);
+void SubsController::Save(agi::fs::path const &filename, std::string const &encoding)
+{
+    const SubtitleFormat *writer = SubtitleFormat::GetWriter(filename);
+    if (!writer)
+        throw agi::InvalidInputException("Unknown file type.");
+
+    int old_autosaved_commit_id = autosaved_commit_id, old_saved_commit_id = saved_commit_id;
+    try {
+        autosaved_commit_id = saved_commit_id = commit_id;
+
+        // Have to set this now for the sake of things that want to save paths
+        // relative to the script in the header
+        this->filename = filename;
+        context->path->SetToken("?script", filename.parent_path());
+
+        context->ass->CleanExtradata();
+        writer->WriteFile(context->ass.get(), filename, 0, encoding);
+        FileSave();
+    }
+    catch (...) {
+        autosaved_commit_id = old_autosaved_commit_id;
+        saved_commit_id = old_saved_commit_id;
+        throw;
+    }
+
+    SetFileName(filename);
 }
 
-void SubsController::Close() {
-	undo_stack.clear();
-	redo_stack.clear();
-	autosaved_commit_id = saved_commit_id = commit_id + 1;
-	filename.clear();
-	AssFile blank;
-	blank.swap(*context->ass);
-	context->ass->LoadDefault(true, OPT_GET("Subtitle Format/ASS/Default Style Catalog")->GetString());
-	context->ass->Commit("", AssFile::COMMIT_NEW);
-	FileOpen(filename);
+void SubsController::Close()
+{
+    undo_stack.clear();
+    redo_stack.clear();
+    autosaved_commit_id = saved_commit_id = commit_id + 1;
+    filename.clear();
+    AssFile blank;
+    blank.swap(*context->ass);
+    context->ass->LoadDefault(true, OPT_GET("Subtitle Format/ASS/Default Style Catalog")->GetString());
+    context->ass->Commit("", AssFile::COMMIT_NEW);
+    FileOpen(filename);
 }
 
-int SubsController::TryToClose(bool allow_cancel) const {
-	if (!IsModified())
-		return wxYES;
-
-	int flags = wxYES_NO;
-	if (allow_cancel)
-		flags |= wxCANCEL;
-	int result = wxMessageBox(fmt_tl("Do you want to save changes to %s?", Filename()), _("Unsaved changes"), flags, context->parent);
-	if (result == wxYES) {
-		cmd::call("subtitle/save", context);
-		// If it fails saving, return cancel anyway
-		return IsModified() ? wxCANCEL : wxYES;
-	}
-	return result;
+int SubsController::TryToClose(bool allow_cancel) const
+{
+    if (!IsModified())
+        return wxYES;
+
+    int flags = wxYES_NO;
+    if (allow_cancel)
+        flags |= wxCANCEL;
+    int result = wxMessageBox(fmt_tl("Do you want to save changes to %s?", Filename()), _("Unsaved changes"), flags, context->parent);
+    if (result == wxYES) {
+        cmd::call("subtitle/save", context);
+        // If it fails saving, return cancel anyway
+        return IsModified() ? wxCANCEL : wxYES;
+    }
+    return result;
 }
 
-void SubsController::AutoSave() {
-	if (commit_id == autosaved_commit_id)
-		return;
-
-	auto directory = context->path->Decode(OPT_GET("Path/Auto/Save")->GetString());
-	if (directory.empty())
-		directory = filename.parent_path();
-
-	auto name = filename.filename();
-	if (name.empty())
-		name = "Untitled";
-
-	autosaved_commit_id = commit_id;
-	auto frame = context->frame;
-	auto subs_copy = new AssFile(*context->ass);
-	autosave_queue->Async([subs_copy, name, directory, frame] {
-		wxString msg;
-		std::unique_ptr<AssFile> subs(subs_copy);
-
-		try {
-			agi::fs::CreateDirectory(directory);
-			auto path = directory /  agi::format("%s.%s.AUTOSAVE.ass", name.string(),
-			                                     agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
-			SubtitleFormat::GetWriter(path)->WriteFile(subs.get(), path, 0);
-			msg = fmt_tl("File backup saved as \"%s\".", path);
-		}
-		catch (const agi::Exception& err) {
-			msg = to_wx("Exception when attempting to autosave file: " + err.GetMessage());
-		}
-		catch (...) {
-			msg = "Unhandled exception when attempting to autosave file.";
-		}
-
-		agi::dispatch::Main().Async([frame, msg] {
-			frame->StatusTimeout(msg);
-		});
-	});
+void SubsController::AutoSave()
+{
+    if (commit_id == autosaved_commit_id)
+        return;
+
+    auto directory = context->path->Decode(OPT_GET("Path/Auto/Save")->GetString());
+    if (directory.empty())
+        directory = filename.parent_path();
+
+    auto name = filename.filename();
+    if (name.empty())
+        name = "Untitled";
+
+    autosaved_commit_id = commit_id;
+    auto frame = context->frame;
+    auto subs_copy = new AssFile(*context->ass);
+    autosave_queue->Async([subs_copy, name, directory, frame] {
+        wxString msg;
+        std::unique_ptr<AssFile> subs(subs_copy);
+
+        try
+        {
+            agi::fs::CreateDirectory(directory);
+            auto path = directory /  agi::format("%s.%s.AUTOSAVE.ass", name.string(),
+                                                 agi::util::strftime("%Y-%m-%d-%H-%M-%S"));
+            SubtitleFormat::GetWriter(path)->WriteFile(subs.get(), path, 0);
+            msg = fmt_tl("File backup saved as \"%s\".", path);
+        }
+        catch (const agi::Exception &err)
+        {
+            msg = to_wx("Exception when attempting to autosave file: " + err.GetMessage());
+        }
+        catch (...)
+        {
+            msg = "Unhandled exception when attempting to autosave file.";
+        }
+
+        agi::dispatch::Main().Async([frame, msg] {
+            frame->StatusTimeout(msg);
+        });
+    });
 }
 
-bool SubsController::CanSave() const {
-	try {
-		return SubtitleFormat::GetWriter(filename)->CanSave(context->ass.get());
-	}
-	catch (...) {
-		return false;
-	}
+bool SubsController::CanSave() const
+{
+    try {
+        return SubtitleFormat::GetWriter(filename)->CanSave(context->ass.get());
+    }
+    catch (...) {
+        return false;
+    }
 }
 
-void SubsController::SetFileName(agi::fs::path const& path) {
-	filename = path;
-	context->path->SetToken("?script", path.parent_path());
-	config::mru->Add("Subtitle", path);
-	OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string());
+void SubsController::SetFileName(agi::fs::path const &path)
+{
+    filename = path;
+    context->path->SetToken("?script", path.parent_path());
+    config::mru->Add("Subtitle", path);
+    OPT_SET("Path/Last/Subtitles")->SetString(filename.parent_path().string());
 }
 
-void SubsController::OnCommit(AssFileCommit c) {
-	if (c.message.empty() && !undo_stack.empty()) return;
-
-	commit_id = next_commit_id++;
-	// Allow coalescing only if it's the last change and the file has not been
-	// saved since the last change
-	if (commit_id == *c.commit_id+1 && redo_stack.empty() && saved_commit_id+1 != commit_id) {
-		// If only one line changed just modify it instead of copying the file
-		if (c.single_line && c.single_line->Group() == AssEntryGroup::DIALOGUE) {
-			for (auto& diag : undo_stack.back().events) {
-				if (diag.Id == c.single_line->Id) {
-					diag = *c.single_line;
-					break;
-				}
-			}
-			*c.commit_id = commit_id;
-			return;
-		}
-
-		undo_stack.pop_back();
-	}
-
-	// Make sure the file has at least one style and one dialogue line
-	if (context->ass->Styles.empty())
-		context->ass->Styles.push_back(*new AssStyle);
-	if (context->ass->Events.empty()) {
-		context->ass->Events.push_back(*new AssDialogue);
-		context->ass->Events.back().Row = 0;
-	}
-
-	redo_stack.clear();
-
-	undo_stack.emplace_back(context, c.message, commit_id);
-
-	int depth = std::max<int>(OPT_GET("Limits/Undo Levels")->GetInt(), 2);
-	while ((int)undo_stack.size() > depth)
-		undo_stack.pop_front();
-
-	if (undo_stack.size() > 1 && OPT_GET("App/Auto/Save on Every Change")->GetBool() && !filename.empty() && CanSave())
-		Save(filename);
-
-	*c.commit_id = commit_id;
+void SubsController::OnCommit(AssFileCommit c)
+{
+    if (c.message.empty() && !undo_stack.empty()) return;
+
+    commit_id = next_commit_id++;
+    // Allow coalescing only if it's the last change and the file has not been
+    // saved since the last change
+    if (commit_id == *c.commit_id + 1 && redo_stack.empty() && saved_commit_id + 1 != commit_id) {
+        // If only one line changed just modify it instead of copying the file
+        if (c.single_line && c.single_line->Group() == AssEntryGroup::DIALOGUE) {
+            for (auto &diag : undo_stack.back().events) {
+                if (diag.Id == c.single_line->Id) {
+                    diag = *c.single_line;
+                    break;
+                }
+            }
+            *c.commit_id = commit_id;
+            return;
+        }
+
+        undo_stack.pop_back();
+    }
+
+    // Make sure the file has at least one style and one dialogue line
+    if (context->ass->Styles.empty())
+        context->ass->Styles.push_back(*new AssStyle);
+    if (context->ass->Events.empty()) {
+        context->ass->Events.push_back(*new AssDialogue);
+        context->ass->Events.back().Row = 0;
+    }
+
+    redo_stack.clear();
+
+    undo_stack.emplace_back(context, c.message, commit_id);
+
+    int depth = std::max<int>(OPT_GET("Limits/Undo Levels")->GetInt(), 2);
+    while ((int)undo_stack.size() > depth)
+        undo_stack.pop_front();
+
+    if (undo_stack.size() > 1 && OPT_GET("App/Auto/Save on Every Change")->GetBool() && !filename.empty() && CanSave())
+        Save(filename);
+
+    *c.commit_id = commit_id;
 }
 
-void SubsController::OnActiveLineChanged() {
-	if (!undo_stack.empty())
-		undo_stack.back().UpdateActiveLine(context);
+void SubsController::OnActiveLineChanged()
+{
+    if (!undo_stack.empty())
+        undo_stack.back().UpdateActiveLine(context);
 }
 
-void SubsController::OnSelectionChanged() {
-	if (!undo_stack.empty())
-		undo_stack.back().UpdateSelection(context);
+void SubsController::OnSelectionChanged()
+{
+    if (!undo_stack.empty())
+        undo_stack.back().UpdateSelection(context);
 }
 
-void SubsController::OnTextSelectionChanged() {
-	if (!undo_stack.empty())
-		undo_stack.back().UpdateTextSelection(context);
+void SubsController::OnTextSelectionChanged()
+{
+    if (!undo_stack.empty())
+        undo_stack.back().UpdateTextSelection(context);
 }
 
-void SubsController::Undo() {
-	if (undo_stack.size() <= 1) return;
-	redo_stack.splice(redo_stack.end(), undo_stack, std::prev(undo_stack.end()));
+void SubsController::Undo()
+{
+    if (undo_stack.size() <= 1) return;
+    redo_stack.splice(redo_stack.end(), undo_stack, std::prev(undo_stack.end()));
 
-	commit_id = undo_stack.back().commit_id;
+    commit_id = undo_stack.back().commit_id;
 
-	text_selection_connection.Block();
-	undo_stack.back().Apply(context);
-	text_selection_connection.Unblock();
+    text_selection_connection.Block();
+    undo_stack.back().Apply(context);
+    text_selection_connection.Unblock();
 }
 
-void SubsController::Redo() {
-	if (redo_stack.empty()) return;
-	undo_stack.splice(undo_stack.end(), redo_stack, std::prev(redo_stack.end()));
+void SubsController::Redo()
+{
+    if (redo_stack.empty()) return;
+    undo_stack.splice(undo_stack.end(), redo_stack, std::prev(redo_stack.end()));
 
-	commit_id = undo_stack.back().commit_id;
+    commit_id = undo_stack.back().commit_id;
 
-	text_selection_connection.Block();
-	undo_stack.back().Apply(context);
-	text_selection_connection.Unblock();
+    text_selection_connection.Block();
+    undo_stack.back().Apply(context);
+    text_selection_connection.Unblock();
 }
 
-wxString SubsController::GetUndoDescription() const {
-	return IsUndoStackEmpty() ? "" : undo_stack.back().undo_description;
+wxString SubsController::GetUndoDescription() const
+{
+    return IsUndoStackEmpty() ? "" : undo_stack.back().undo_description;
 }
 
-wxString SubsController::GetRedoDescription() const {
-	return IsRedoStackEmpty() ? "" : redo_stack.back().undo_description;
+wxString SubsController::GetRedoDescription() const
+{
+    return IsRedoStackEmpty() ? "" : redo_stack.back().undo_description;
 }
 
-agi::fs::path SubsController::Filename() const {
-	if (!filename.empty()) return filename;
+agi::fs::path SubsController::Filename() const
+{
+    if (!filename.empty()) return filename;
 
-	// Apple HIG says "untitled" should not be capitalised
+    // Apple HIG says "untitled" should not be capitalised
 #ifndef __WXMAC__
-	return _("Untitled").wx_str();
+    return _("Untitled").wx_str();
 #else
-	return _("untitled").wx_str();
+    return _("untitled").wx_str();
 #endif
 }
diff --git a/src/subs_controller.h b/src/subs_controller.h
index ec1c86b07faf12e3855fb105263c06b5881585ca..11e7110336d394cd8f12809f47bd950fa2b3a47c 100644
--- a/src/subs_controller.h
+++ b/src/subs_controller.h
@@ -23,116 +23,116 @@
 
 class SelectionController;
 namespace agi {
-	namespace dispatch {
-		class Queue;
-	}
-	struct Context;
+namespace dispatch {
+class Queue;
+}
+struct Context;
 }
 struct AssFileCommit;
 struct ProjectProperties;
 
 class SubsController {
-	agi::Context *context;
-	agi::signal::Connection undo_connection;
-	agi::signal::Connection active_line_connection;
-	agi::signal::Connection selection_connection;
-	agi::signal::Connection text_selection_connection;
-
-	struct UndoInfo;
-	boost::container::list<UndoInfo> undo_stack;
-	boost::container::list<UndoInfo> redo_stack;
-
-	/// Revision counter for undo coalescing and modified state tracking
-	int commit_id = 0;
-	/// Last saved version of this file
-	int saved_commit_id = 0;
-	/// Last autosaved version of this file
-	int autosaved_commit_id = 0;
-	/// Version to use for the next commit
-	/// Needed to handle Save -> Undo -> Edit, which would result in the file
-	/// being marked unmodified if we reused commit IDs
-	int next_commit_id = 1;
-
-	/// Timer for triggering autosaves
-	wxTimer autosave_timer;
-
-	/// Queue which autosaves are performed on
-	std::unique_ptr<agi::dispatch::Queue> autosave_queue;
-
-	/// A new file has been opened (filename)
-	agi::signal::Signal<agi::fs::path> FileOpen;
-	/// The file has been saved
-	agi::signal::Signal<> FileSave;
-
-	/// The filename of the currently open file, if any
-	agi::fs::path filename;
-
-	/// Set the filename, updating things like the MRU and last used path
-	void SetFileName(agi::fs::path const& file);
-
-	/// Autosave the file if there have been any chances since the last autosave
-	void AutoSave();
-
-	void OnCommit(AssFileCommit c);
-	void OnActiveLineChanged();
-	void OnSelectionChanged();
-	void OnTextSelectionChanged();
+    agi::Context *context;
+    agi::signal::Connection undo_connection;
+    agi::signal::Connection active_line_connection;
+    agi::signal::Connection selection_connection;
+    agi::signal::Connection text_selection_connection;
+
+    struct UndoInfo;
+    boost::container::list<UndoInfo> undo_stack;
+    boost::container::list<UndoInfo> redo_stack;
+
+    /// Revision counter for undo coalescing and modified state tracking
+    int commit_id = 0;
+    /// Last saved version of this file
+    int saved_commit_id = 0;
+    /// Last autosaved version of this file
+    int autosaved_commit_id = 0;
+    /// Version to use for the next commit
+    /// Needed to handle Save -> Undo -> Edit, which would result in the file
+    /// being marked unmodified if we reused commit IDs
+    int next_commit_id = 1;
+
+    /// Timer for triggering autosaves
+    wxTimer autosave_timer;
+
+    /// Queue which autosaves are performed on
+    std::unique_ptr<agi::dispatch::Queue> autosave_queue;
+
+    /// A new file has been opened (filename)
+    agi::signal::Signal<agi::fs::path> FileOpen;
+    /// The file has been saved
+    agi::signal::Signal<> FileSave;
+
+    /// The filename of the currently open file, if any
+    agi::fs::path filename;
+
+    /// Set the filename, updating things like the MRU and last used path
+    void SetFileName(agi::fs::path const &file);
+
+    /// Autosave the file if there have been any chances since the last autosave
+    void AutoSave();
+
+    void OnCommit(AssFileCommit c);
+    void OnActiveLineChanged();
+    void OnSelectionChanged();
+    void OnTextSelectionChanged();
 
 public:
-	SubsController(agi::Context *context);
-	~SubsController();
-
-	/// Set the selection controller to use
-	///
-	/// Required due to that the selection controller is the subtitles grid, and
-	/// so is created long after the subtitles controller
-	void SetSelectionController(SelectionController *selection_controller);
-
-	/// The file's path and filename if any, or platform-appropriate "untitled"
-	agi::fs::path Filename() const;
-
-	/// Does the file have unsaved changes?
-	bool IsModified() const { return commit_id != saved_commit_id; };
-
-	/// @brief Load from a file
-	/// @param file File name
-	/// @param charset Character set of file
-	ProjectProperties Load(agi::fs::path const& file, std::string charset);
-
-	/// @brief Save to a file
-	/// @param file Path to save to
-	/// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
-	void Save(agi::fs::path const& file, std::string const& encoding="");
-
-	/// Close the currently open file (i.e. open a new blank file)
-	void Close();
-
-	/// If there are unsaved changes, asl the user if they want to save them
-	/// @param allow_cancel Let the user cancel the closing
-	/// @return wxYES, wxNO or wxCANCEL (note: all three are true in a boolean context)
-	int TryToClose(bool allow_cancel = true) const;
-
-	/// Can the file be saved in its current format?
-	bool CanSave() const;
-
-	/// The file is about to be saved
-	/// This signal is intended for adding metadata which is awkward or
-	/// expensive to always keep up to date
-	agi::signal::Signal<> UpdateProperties;
-
-	DEFINE_SIGNAL_ADDERS(FileOpen, AddFileOpenListener)
-	DEFINE_SIGNAL_ADDERS(FileSave, AddFileSaveListener)
-
-	/// @brief Undo the last set of changes to the file
-	void Undo();
-	/// @brief Redo the last undone changes
-	void Redo();
-	/// Check if undo stack is empty
-	bool IsUndoStackEmpty() const { return undo_stack.size() <= 1; };
-	/// Check if redo stack is empty
-	bool IsRedoStackEmpty() const { return redo_stack.empty(); };
-	/// Get the description of the first undoable change
-	wxString GetUndoDescription() const;
-	/// Get the description of the first redoable change
-	wxString GetRedoDescription() const;
+    SubsController(agi::Context *context);
+    ~SubsController();
+
+    /// Set the selection controller to use
+    ///
+    /// Required due to that the selection controller is the subtitles grid, and
+    /// so is created long after the subtitles controller
+    void SetSelectionController(SelectionController *selection_controller);
+
+    /// The file's path and filename if any, or platform-appropriate "untitled"
+    agi::fs::path Filename() const;
+
+    /// Does the file have unsaved changes?
+    bool IsModified() const { return commit_id != saved_commit_id; };
+
+    /// @brief Load from a file
+    /// @param file File name
+    /// @param charset Character set of file
+    ProjectProperties Load(agi::fs::path const &file, std::string charset);
+
+    /// @brief Save to a file
+    /// @param file Path to save to
+    /// @param encoding Encoding to use, or empty to let the writer decide (which usually means "App/Save Charset")
+    void Save(agi::fs::path const &file, std::string const &encoding = "");
+
+    /// Close the currently open file (i.e. open a new blank file)
+    void Close();
+
+    /// If there are unsaved changes, asl the user if they want to save them
+    /// @param allow_cancel Let the user cancel the closing
+    /// @return wxYES, wxNO or wxCANCEL (note: all three are true in a boolean context)
+    int TryToClose(bool allow_cancel = true) const;
+
+    /// Can the file be saved in its current format?
+    bool CanSave() const;
+
+    /// The file is about to be saved
+    /// This signal is intended for adding metadata which is awkward or
+    /// expensive to always keep up to date
+    agi::signal::Signal<> UpdateProperties;
+
+    DEFINE_SIGNAL_ADDERS(FileOpen, AddFileOpenListener)
+    DEFINE_SIGNAL_ADDERS(FileSave, AddFileSaveListener)
+
+    /// @brief Undo the last set of changes to the file
+    void Undo();
+    /// @brief Redo the last undone changes
+    void Redo();
+    /// Check if undo stack is empty
+    bool IsUndoStackEmpty() const { return undo_stack.size() <= 1; };
+    /// Check if redo stack is empty
+    bool IsRedoStackEmpty() const { return redo_stack.empty(); };
+    /// Get the description of the first undoable change
+    wxString GetUndoDescription() const;
+    /// Get the description of the first redoable change
+    wxString GetRedoDescription() const;
 };
diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp
index ca08b4177c748438149b0b15596944e3e6b363e5..bd0860b71cb4a8bb9f7844a3ab2b865f982bf848 100644
--- a/src/subs_edit_box.cpp
+++ b/src/subs_edit_box.cpp
@@ -73,27 +73,30 @@
 namespace {
 
 /// Work around wxGTK's fondness for generating events from ChangeValue
-void change_value(wxTextCtrl *ctrl, wxString const& value) {
-	if (value != ctrl->GetValue())
-		ctrl->ChangeValue(value);
+void change_value(wxTextCtrl *ctrl, wxString const &value)
+{
+    if (value != ctrl->GetValue())
+        ctrl->ChangeValue(value);
 }
 
-wxString new_value(wxComboBox *ctrl, wxCommandEvent &evt) {
+wxString new_value(wxComboBox *ctrl, wxCommandEvent &evt)
+{
 #ifdef __WXGTK__
-	return ctrl->GetValue();
+    return ctrl->GetValue();
 #else
-	return evt.GetString();
+    return evt.GetString();
 #endif
 }
 
-void time_edit_char_hook(wxKeyEvent &event) {
-	// Force a modified event on Enter
-	if (event.GetKeyCode() == WXK_RETURN) {
-		TimeEdit *edit = static_cast<TimeEdit*>(event.GetEventObject());
-		edit->SetValue(edit->GetValue());
-	}
-	else
-		event.Skip();
+void time_edit_char_hook(wxKeyEvent &event)
+{
+    // Force a modified event on Enter
+    if (event.GetKeyCode() == WXK_RETURN) {
+        TimeEdit *edit = static_cast<TimeEdit *>(event.GetEventObject());
+        edit->SetValue(edit->GetValue());
+    }
+    else
+        event.Skip();
 }
 
 // Passing a pointer-to-member directly to a function sometimes does not work
@@ -103,530 +106,564 @@ const auto AssDialogue_Effect = &AssDialogue::Effect;
 }
 
 SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
-: wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxRAISED_BORDER, "SubsEditBox")
-, c(context)
-, undo_timer(GetEventHandler())
+    : wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxRAISED_BORDER, "SubsEditBox")
+    , c(context)
+    , undo_timer(GetEventHandler())
 {
-	using std::bind;
+    using std::bind;
 
-	// Top controls
-	top_sizer = new wxBoxSizer(wxHORIZONTAL);
+    // Top controls
+    top_sizer = new wxBoxSizer(wxHORIZONTAL);
 
-	comment_box = new wxCheckBox(this,-1,_("&Comment"));
-	comment_box->SetToolTip(_("Comment this line out. Commented lines don't show up on screen."));
+    comment_box = new wxCheckBox(this, -1, _("&Comment"));
+    comment_box->SetToolTip(_("Comment this line out. Commented lines don't show up on screen."));
 #ifdef __WXGTK__
-	// Only supported in wxgtk
-	comment_box->SetCanFocus(false);
+    // Only supported in wxgtk
+    comment_box->SetCanFocus(false);
 #endif
-	top_sizer->Add(comment_box, 0, wxRIGHT | wxALIGN_CENTER, 5);
-
-	style_box = MakeComboBox("Default", wxCB_READONLY, &SubsEditBox::OnStyleChange, _("Style for this line"));
-
-	style_edit_button = new wxButton(this, -1, _("Edit"), wxDefaultPosition,
-		wxSize(GetTextExtent(_("Edit")).GetWidth() + 20, -1));
-	style_edit_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
-		if (active_style) {
-			wxArrayString font_list = wxFontEnumerator::GetFacenames();
-			font_list.Sort();
-			DialogStyleEditor(this, active_style, c, nullptr, "", font_list).ShowModal();
-		}
-	});
-	top_sizer->Add(style_edit_button, wxSizerFlags().Center().Border(wxRIGHT));
-
-	actor_box = new Placeholder<wxComboBox>(this, _("Actor"), wxSize(110, -1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Actor name for this speech. This is only for reference, and is mainly useless."));
-	Bind(wxEVT_TEXT, &SubsEditBox::OnActorChange, this, actor_box->GetId());
-	Bind(wxEVT_COMBOBOX, &SubsEditBox::OnActorChange, this, actor_box->GetId());
-	top_sizer->Add(actor_box, wxSizerFlags(2).Center().Border(wxRIGHT));
-
-	effect_box = new Placeholder<wxComboBox>(this, _("Effect"), wxSize(80,-1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Effect for this line. This can be used to store extra information for karaoke scripts, or for the effects supported by the renderer."));
-	Bind(wxEVT_TEXT, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
-	Bind(wxEVT_COMBOBOX, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
-	top_sizer->Add(effect_box, 3, wxALIGN_CENTER, 5);
-
-	char_count = new wxTextCtrl(this, -1, "0", wxDefaultPosition, wxSize(30, -1), wxTE_READONLY | wxTE_CENTER);
-	char_count->SetToolTip(_("Number of characters in the longest line of this subtitle."));
-	top_sizer->Add(char_count, 0, wxALIGN_CENTER, 5);
-
-	// Middle controls
-	middle_left_sizer = new wxBoxSizer(wxHORIZONTAL);
-
-	layer = new wxSpinCtrl(this,-1,"",wxDefaultPosition,wxSize(50,-1), wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER,0,0x7FFFFFFF,0);
-	layer->SetToolTip(_("Layer number"));
-	middle_left_sizer->Add(layer, wxSizerFlags().Center());
-	middle_left_sizer->AddSpacer(5);
-
-	start_time = MakeTimeCtrl(_("Start time"), TIME_START);
-	end_time   = MakeTimeCtrl(_("End time"), TIME_END);
-	middle_left_sizer->AddSpacer(5);
-	duration   = MakeTimeCtrl(_("Line duration"), TIME_DURATION);
-	middle_left_sizer->AddSpacer(5);
-
-	margin[0] = MakeMarginCtrl(_("Left Margin (0 = default from style)"), 0, _("left margin change"));
-	margin[1] = MakeMarginCtrl(_("Right Margin (0 = default from style)"), 1, _("right margin change"));
-	margin[2] = MakeMarginCtrl(_("Vertical Margin (0 = default from style)"), 2, _("vertical margin change"));
-	middle_left_sizer->AddSpacer(5);
-
-	// Middle-bottom controls
-	middle_right_sizer = new wxBoxSizer(wxHORIZONTAL);
-	MakeButton("edit/style/bold");
-	MakeButton("edit/style/italic");
-	MakeButton("edit/style/underline");
-	MakeButton("edit/style/strikeout");
-	MakeButton("edit/font");
-	middle_right_sizer->AddSpacer(5);
-	MakeButton("edit/color/primary");
-	MakeButton("edit/color/secondary");
-	MakeButton("edit/color/outline");
-	MakeButton("edit/color/shadow");
-	middle_right_sizer->AddSpacer(5);
-	MakeButton("grid/line/next/create");
-	middle_right_sizer->AddSpacer(10);
-
-	by_time = MakeRadio(_("T&ime"), true, _("Time by h:mm:ss.cs"));
-	by_frame = MakeRadio(_("F&rame"), false, _("Time by frame number"));
-	by_frame->Enable(false);
-
-	split_box = new wxCheckBox(this,-1,_("Show Original"));
-	split_box->SetToolTip(_("Show the contents of the subtitle line when it was first selected above the edit box. This is sometimes useful when editing subtitles or translating subtitles into another language."));
-	split_box->Bind(wxEVT_CHECKBOX, &SubsEditBox::OnSplit, this);
-	middle_right_sizer->Add(split_box, wxSizerFlags().Center().Left());
-
-	// Main sizer
-	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
-	main_sizer->Add(top_sizer,0,wxEXPAND | wxALL,3);
-	main_sizer->Add(middle_left_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
-	main_sizer->Add(middle_right_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
-
-	// Text editor
-	edit_ctrl = new SubsTextEditCtrl(this, wxSize(300,50), wxBORDER_SUNKEN, c);
-	edit_ctrl->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
-
-	secondary_editor = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(300,50), wxBORDER_SUNKEN | wxTE_MULTILINE | wxTE_READONLY);
-
-	main_sizer->Add(secondary_editor,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
-	main_sizer->Add(edit_ctrl,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
-	main_sizer->Hide(secondary_editor);
-
-	bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
-	bottom_sizer->Add(MakeBottomButton("edit/revert"), wxSizerFlags().Border(wxRIGHT));
-	bottom_sizer->Add(MakeBottomButton("edit/clear"), wxSizerFlags().Border(wxRIGHT));
-	bottom_sizer->Add(MakeBottomButton("edit/clear/text"), wxSizerFlags().Border(wxRIGHT));
-	bottom_sizer->Add(MakeBottomButton("edit/insert_original"));
-	main_sizer->Add(bottom_sizer);
-	main_sizer->Hide(bottom_sizer);
-
-	SetSizerAndFit(main_sizer);
-
-	edit_ctrl->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChange, this);
-	edit_ctrl->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION);
-
-	Bind(wxEVT_TEXT, &SubsEditBox::OnLayerEnter, this, layer->GetId());
-	Bind(wxEVT_SPINCTRL, &SubsEditBox::OnLayerEnter, this, layer->GetId());
-	Bind(wxEVT_CHECKBOX, &SubsEditBox::OnCommentChange, this, comment_box->GetId());
-
-	Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
-	Bind(wxEVT_SIZE, &SubsEditBox::OnSize, this);
-	Bind(wxEVT_TIMER, [=](wxTimerEvent&) { commit_id = -1; });
-
-	wxSizeEvent evt;
-	OnSize(evt);
-
-	file_changed_slot = c->ass->AddCommitListener(&SubsEditBox::OnCommit, this);
-	connections = agi::signal::make_vector({
-		context->project->AddTimecodesListener(&SubsEditBox::UpdateFrameTiming, this),
-		context->selectionController->AddActiveLineListener(&SubsEditBox::OnActiveLineChanged, this),
-		context->selectionController->AddSelectionListener(&SubsEditBox::OnSelectedSetChanged, this),
-		context->initialLineState->AddChangeListener(&SubsEditBox::OnLineInitialTextChanged, this),
-	 });
-
-	context->textSelectionController->SetControl(edit_ctrl);
-	edit_ctrl->SetFocus();
-}
-
-SubsEditBox::~SubsEditBox() {
-	c->textSelectionController->SetControl(nullptr);
-}
-
-wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) {
-	wxTextCtrl *ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(40,-1), wxTE_CENTRE | wxTE_PROCESS_ENTER, IntValidator());
-	ctrl->SetMaxLength(4);
-	ctrl->SetToolTip(tooltip);
-	middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
-
-	Bind(wxEVT_TEXT, [=](wxCommandEvent&) {
-		int value = agi::util::mid(0, atoi(ctrl->GetValue().utf8_str()), 9999);
-		SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; },
-			commit_msg, AssFile::COMMIT_DIAG_META);
-	}, ctrl->GetId());
-
-	return ctrl;
-}
-
-TimeEdit *SubsEditBox::MakeTimeCtrl(wxString const& tooltip, TimeField field) {
-	TimeEdit *ctrl = new TimeEdit(this, -1, c, "", wxSize(GetTextExtent(wxS(" 0:00:00.000 ")).GetWidth(),-1), field == TIME_END);
-	ctrl->SetToolTip(tooltip);
-	Bind(wxEVT_TEXT, [=](wxCommandEvent&) { CommitTimes(field); }, ctrl->GetId());
-	ctrl->Bind(wxEVT_CHAR_HOOK, time_edit_char_hook);
-	middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
-	return ctrl;
-}
-
-void SubsEditBox::MakeButton(const char *cmd_name) {
-	cmd::Command *command = cmd::get(cmd_name);
-	wxBitmapButton *btn = new wxBitmapButton(this, -1, command->Icon(16));
-	ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
-
-	middle_right_sizer->Add(btn, wxSizerFlags().Expand());
-	btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
+    top_sizer->Add(comment_box, 0, wxRIGHT | wxALIGN_CENTER, 5);
+
+    style_box = MakeComboBox("Default", wxCB_READONLY, &SubsEditBox::OnStyleChange, _("Style for this line"));
+
+    style_edit_button = new wxButton(this, -1, _("Edit"), wxDefaultPosition,
+                                     wxSize(GetTextExtent(_("Edit")).GetWidth() + 20, -1));
+    style_edit_button->Bind(wxEVT_BUTTON, [ = ](wxCommandEvent &) {
+        if (active_style) {
+            wxArrayString font_list = wxFontEnumerator::GetFacenames();
+            font_list.Sort();
+            DialogStyleEditor(this, active_style, c, nullptr, "", font_list).ShowModal();
+        }
+    });
+    top_sizer->Add(style_edit_button, wxSizerFlags().Center().Border(wxRIGHT));
+
+    actor_box = new Placeholder<wxComboBox>(this, _("Actor"), wxSize(110, -1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Actor name for this speech. This is only for reference, and is mainly useless."));
+    Bind(wxEVT_TEXT, &SubsEditBox::OnActorChange, this, actor_box->GetId());
+    Bind(wxEVT_COMBOBOX, &SubsEditBox::OnActorChange, this, actor_box->GetId());
+    top_sizer->Add(actor_box, wxSizerFlags(2).Center().Border(wxRIGHT));
+
+    effect_box = new Placeholder<wxComboBox>(this, _("Effect"), wxSize(80, -1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Effect for this line. This can be used to store extra information for karaoke scripts, or for the effects supported by the renderer."));
+    Bind(wxEVT_TEXT, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
+    Bind(wxEVT_COMBOBOX, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
+    top_sizer->Add(effect_box, 3, wxALIGN_CENTER, 5);
+
+    char_count = new wxTextCtrl(this, -1, "0", wxDefaultPosition, wxSize(30, -1), wxTE_READONLY | wxTE_CENTER);
+    char_count->SetToolTip(_("Number of characters in the longest line of this subtitle."));
+    top_sizer->Add(char_count, 0, wxALIGN_CENTER, 5);
+
+    // Middle controls
+    middle_left_sizer = new wxBoxSizer(wxHORIZONTAL);
+
+    layer = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, 0x7FFFFFFF, 0);
+    layer->SetToolTip(_("Layer number"));
+    middle_left_sizer->Add(layer, wxSizerFlags().Center());
+    middle_left_sizer->AddSpacer(5);
+
+    start_time = MakeTimeCtrl(_("Start time"), TIME_START);
+    end_time   = MakeTimeCtrl(_("End time"), TIME_END);
+    middle_left_sizer->AddSpacer(5);
+    duration   = MakeTimeCtrl(_("Line duration"), TIME_DURATION);
+    middle_left_sizer->AddSpacer(5);
+
+    margin[0] = MakeMarginCtrl(_("Left Margin (0 = default from style)"), 0, _("left margin change"));
+    margin[1] = MakeMarginCtrl(_("Right Margin (0 = default from style)"), 1, _("right margin change"));
+    margin[2] = MakeMarginCtrl(_("Vertical Margin (0 = default from style)"), 2, _("vertical margin change"));
+    middle_left_sizer->AddSpacer(5);
+
+    // Middle-bottom controls
+    middle_right_sizer = new wxBoxSizer(wxHORIZONTAL);
+    MakeButton("edit/style/bold");
+    MakeButton("edit/style/italic");
+    MakeButton("edit/style/underline");
+    MakeButton("edit/style/strikeout");
+    MakeButton("edit/font");
+    middle_right_sizer->AddSpacer(5);
+    MakeButton("edit/color/primary");
+    MakeButton("edit/color/secondary");
+    MakeButton("edit/color/outline");
+    MakeButton("edit/color/shadow");
+    middle_right_sizer->AddSpacer(5);
+    MakeButton("grid/line/next/create");
+    middle_right_sizer->AddSpacer(10);
+
+    by_time = MakeRadio(_("T&ime"), true, _("Time by h:mm:ss.cs"));
+    by_frame = MakeRadio(_("F&rame"), false, _("Time by frame number"));
+    by_frame->Enable(false);
+
+    split_box = new wxCheckBox(this, -1, _("Show Original"));
+    split_box->SetToolTip(_("Show the contents of the subtitle line when it was first selected above the edit box. This is sometimes useful when editing subtitles or translating subtitles into another language."));
+    split_box->Bind(wxEVT_CHECKBOX, &SubsEditBox::OnSplit, this);
+    middle_right_sizer->Add(split_box, wxSizerFlags().Center().Left());
+
+    // Main sizer
+    wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
+    main_sizer->Add(top_sizer, 0, wxEXPAND | wxALL, 3);
+    main_sizer->Add(middle_left_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3);
+    main_sizer->Add(middle_right_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3);
+
+    // Text editor
+    edit_ctrl = new SubsTextEditCtrl(this, wxSize(300, 50), wxBORDER_SUNKEN, c);
+    edit_ctrl->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
+
+    secondary_editor = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(300, 50), wxBORDER_SUNKEN | wxTE_MULTILINE | wxTE_READONLY);
+
+    main_sizer->Add(secondary_editor, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3);
+    main_sizer->Add(edit_ctrl, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3);
+    main_sizer->Hide(secondary_editor);
+
+    bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
+    bottom_sizer->Add(MakeBottomButton("edit/revert"), wxSizerFlags().Border(wxRIGHT));
+    bottom_sizer->Add(MakeBottomButton("edit/clear"), wxSizerFlags().Border(wxRIGHT));
+    bottom_sizer->Add(MakeBottomButton("edit/clear/text"), wxSizerFlags().Border(wxRIGHT));
+    bottom_sizer->Add(MakeBottomButton("edit/insert_original"));
+    main_sizer->Add(bottom_sizer);
+    main_sizer->Hide(bottom_sizer);
+
+    SetSizerAndFit(main_sizer);
+
+    edit_ctrl->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChange, this);
+    edit_ctrl->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION);
+
+    Bind(wxEVT_TEXT, &SubsEditBox::OnLayerEnter, this, layer->GetId());
+    Bind(wxEVT_SPINCTRL, &SubsEditBox::OnLayerEnter, this, layer->GetId());
+    Bind(wxEVT_CHECKBOX, &SubsEditBox::OnCommentChange, this, comment_box->GetId());
+
+    Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
+    Bind(wxEVT_SIZE, &SubsEditBox::OnSize, this);
+    Bind(wxEVT_TIMER, [ = ](wxTimerEvent &) { commit_id = -1; });
+
+    wxSizeEvent evt;
+    OnSize(evt);
+
+    file_changed_slot = c->ass->AddCommitListener(&SubsEditBox::OnCommit, this);
+    connections = agi::signal::make_vector({
+        context->project->AddTimecodesListener(&SubsEditBox::UpdateFrameTiming, this),
+        context->selectionController->AddActiveLineListener(&SubsEditBox::OnActiveLineChanged, this),
+        context->selectionController->AddSelectionListener(&SubsEditBox::OnSelectedSetChanged, this),
+        context->initialLineState->AddChangeListener(&SubsEditBox::OnLineInitialTextChanged, this),
+    });
+
+    context->textSelectionController->SetControl(edit_ctrl);
+    edit_ctrl->SetFocus();
+}
+
+SubsEditBox::~SubsEditBox()
+{
+    c->textSelectionController->SetControl(nullptr);
 }
 
-wxButton *SubsEditBox::MakeBottomButton(const char *cmd_name) {
-	cmd::Command *command = cmd::get(cmd_name);
-	wxButton *btn = new wxButton(this, -1, command->StrDisplay(c));
-	ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
+wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const &tooltip, int margin, wxString const &commit_msg)
+{
+    wxTextCtrl *ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(40, -1), wxTE_CENTRE | wxTE_PROCESS_ENTER, IntValidator());
+    ctrl->SetMaxLength(4);
+    ctrl->SetToolTip(tooltip);
+    middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
 
-	btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
-	return btn;
-}
+    Bind(wxEVT_TEXT, [ = ](wxCommandEvent &) {
+        int value = agi::util::mid(0, atoi(ctrl->GetValue().utf8_str()), 9999);
+        SetSelectedRows([&](AssDialogue * d) { d->Margin[margin] = value; },
+        commit_msg, AssFile::COMMIT_DIAG_META);
+    }, ctrl->GetId());
 
-wxComboBox *SubsEditBox::MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip) {
-	wxString styles[] = { "Default" };
-	wxComboBox *ctrl = new wxComboBox(this, -1, initial_text, wxDefaultPosition, wxSize(110,-1), 1, styles, style | wxTE_PROCESS_ENTER);
-	ctrl->SetToolTip(tooltip);
-	top_sizer->Add(ctrl, wxSizerFlags(2).Center().Border(wxRIGHT));
-	Bind(wxEVT_COMBOBOX, handler, this, ctrl->GetId());
-	return ctrl;
+    return ctrl;
 }
 
-wxRadioButton *SubsEditBox::MakeRadio(wxString const& text, bool start, wxString const& tooltip) {
-	wxRadioButton *ctrl = new wxRadioButton(this, -1, text, wxDefaultPosition, wxDefaultSize, start ? wxRB_GROUP : 0);
-	ctrl->SetValue(start);
-	ctrl->SetToolTip(tooltip);
-	Bind(wxEVT_RADIOBUTTON, &SubsEditBox::OnFrameTimeRadio, this, ctrl->GetId());
-	middle_right_sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
-	return ctrl;
+TimeEdit *SubsEditBox::MakeTimeCtrl(wxString const &tooltip, TimeField field)
+{
+    TimeEdit *ctrl = new TimeEdit(this, -1, c, "", wxSize(GetTextExtent(wxS(" 0:00:00.000 ")).GetWidth(), -1), field == TIME_END);
+    ctrl->SetToolTip(tooltip);
+    Bind(wxEVT_TEXT, [ = ](wxCommandEvent &) { CommitTimes(field); }, ctrl->GetId());
+    ctrl->Bind(wxEVT_CHAR_HOOK, time_edit_char_hook);
+    middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
+    return ctrl;
 }
 
-void SubsEditBox::OnCommit(int type) {
-	wxEventBlocker blocker(this);
+void SubsEditBox::MakeButton(const char *cmd_name)
+{
+    cmd::Command *command = cmd::get(cmd_name);
+    wxBitmapButton *btn = new wxBitmapButton(this, -1, command->Icon(16));
+    ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
 
-	initial_times.clear();
+    middle_right_sizer->Add(btn, wxSizerFlags().Expand());
+    btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
+}
 
-	if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_STYLES) {
-		wxString style = style_box->GetValue();
-		style_box->Clear();
-		style_box->Append(to_wx(c->ass->GetStyles()));
-		style_box->Select(style_box->FindString(style));
-		active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
-	}
+wxButton *SubsEditBox::MakeBottomButton(const char *cmd_name)
+{
+    cmd::Command *command = cmd::get(cmd_name);
+    wxButton *btn = new wxButton(this, -1, command->StrDisplay(c));
+    ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
 
-	if (type == AssFile::COMMIT_NEW) {
-		PopulateList(effect_box, AssDialogue_Effect);
-		PopulateList(actor_box, AssDialogue_Actor);
-		return;
-	}
-	else if (type & AssFile::COMMIT_STYLES)
-		style_box->Select(style_box->FindString(to_wx(line->Style)));
+    btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
+    return btn;
+}
 
-	if (!(type ^ AssFile::COMMIT_ORDER)) return;
+wxComboBox *SubsEditBox::MakeComboBox(wxString const &initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent &), wxString const &tooltip)
+{
+    wxString styles[] = { "Default" };
+    wxComboBox *ctrl = new wxComboBox(this, -1, initial_text, wxDefaultPosition, wxSize(110, -1), 1, styles, style | wxTE_PROCESS_ENTER);
+    ctrl->SetToolTip(tooltip);
+    top_sizer->Add(ctrl, wxSizerFlags(2).Center().Border(wxRIGHT));
+    Bind(wxEVT_COMBOBOX, handler, this, ctrl->GetId());
+    return ctrl;
+}
 
-	SetControlsState(!!line);
-	UpdateFields(type, true);
+wxRadioButton *SubsEditBox::MakeRadio(wxString const &text, bool start, wxString const &tooltip)
+{
+    wxRadioButton *ctrl = new wxRadioButton(this, -1, text, wxDefaultPosition, wxDefaultSize, start ? wxRB_GROUP : 0);
+    ctrl->SetValue(start);
+    ctrl->SetToolTip(tooltip);
+    Bind(wxEVT_RADIOBUTTON, &SubsEditBox::OnFrameTimeRadio, this, ctrl->GetId());
+    middle_right_sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
+    return ctrl;
 }
 
-void SubsEditBox::UpdateFields(int type, bool repopulate_lists) {
-	if (!line) return;
+void SubsEditBox::OnCommit(int type)
+{
+    wxEventBlocker blocker(this);
 
-	if (type & AssFile::COMMIT_DIAG_TIME) {
-		start_time->SetTime(line->Start);
-		end_time->SetTime(line->End);
-		SetDurationField();
-	}
+    initial_times.clear();
 
-	if (type & AssFile::COMMIT_DIAG_TEXT) {
-		edit_ctrl->SetTextTo(line->Text);
-		UpdateCharacterCount(line->Text);
-	}
+    if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_STYLES) {
+        wxString style = style_box->GetValue();
+        style_box->Clear();
+        style_box->Append(to_wx(c->ass->GetStyles()));
+        style_box->Select(style_box->FindString(style));
+        active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
+    }
 
-	if (type & AssFile::COMMIT_DIAG_META) {
-		layer->SetValue(line->Layer);
-		for (size_t i = 0; i < margin.size(); ++i)
-			change_value(margin[i], std::to_wstring(line->Margin[i]));
-		comment_box->SetValue(line->Comment);
-		style_box->Select(style_box->FindString(to_wx(line->Style)));
-		active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
-		style_edit_button->Enable(active_style != nullptr);
+    if (type == AssFile::COMMIT_NEW) {
+        PopulateList(effect_box, AssDialogue_Effect);
+        PopulateList(actor_box, AssDialogue_Actor);
+        return;
+    }
+    else if (type & AssFile::COMMIT_STYLES)
+        style_box->Select(style_box->FindString(to_wx(line->Style)));
 
-		if (repopulate_lists) PopulateList(effect_box, AssDialogue_Effect);
-		effect_box->ChangeValue(to_wx(line->Effect));
-		effect_box->SetStringSelection(to_wx(line->Effect));
+    if (!(type ^ AssFile::COMMIT_ORDER)) return;
 
-		if (repopulate_lists) PopulateList(actor_box, AssDialogue_Actor);
-		actor_box->ChangeValue(to_wx(line->Actor));
-		actor_box->SetStringSelection(to_wx(line->Actor));
-	}
+    SetControlsState(!!line);
+    UpdateFields(type, true);
 }
 
-void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<std::string> AssDialogue::*field) {
-	wxEventBlocker blocker(this);
+void SubsEditBox::UpdateFields(int type, bool repopulate_lists)
+{
+    if (!line) return;
+
+    if (type & AssFile::COMMIT_DIAG_TIME) {
+        start_time->SetTime(line->Start);
+        end_time->SetTime(line->End);
+        SetDurationField();
+    }
+
+    if (type & AssFile::COMMIT_DIAG_TEXT) {
+        edit_ctrl->SetTextTo(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], std::to_wstring(line->Margin[i]));
+        comment_box->SetValue(line->Comment);
+        style_box->Select(style_box->FindString(to_wx(line->Style)));
+        active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
+        style_edit_button->Enable(active_style != nullptr);
+
+        if (repopulate_lists) PopulateList(effect_box, AssDialogue_Effect);
+        effect_box->ChangeValue(to_wx(line->Effect));
+        effect_box->SetStringSelection(to_wx(line->Effect));
+
+        if (repopulate_lists) PopulateList(actor_box, AssDialogue_Actor);
+        actor_box->ChangeValue(to_wx(line->Actor));
+        actor_box->SetStringSelection(to_wx(line->Actor));
+    }
+}
+
+void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<std::string> AssDialogue::*field)
+{
+    wxEventBlocker blocker(this);
 
-	std::unordered_set<boost::flyweight<std::string>> values;
-	for (auto const& line : c->ass->Events) {
-		auto const& value = line.*field;
-		if (!value.get().empty())
-			values.insert(value);
-	}
+    std::unordered_set<boost::flyweight<std::string>> values;
+    for (auto const &line : c->ass->Events) {
+        auto const &value = line.*field;
+        if (!value.get().empty())
+            values.insert(value);
+    }
 
-	wxArrayString arrstr;
-	arrstr.reserve(values.size());
-	transform(values.begin(), values.end(), std::back_inserter(arrstr),
-		(wxString (*)(std::string const&))to_wx);
+    wxArrayString arrstr;
+    arrstr.reserve(values.size());
+    transform(values.begin(), values.end(), std::back_inserter(arrstr),
+              (wxString (*)(std::string const &))to_wx);
 
-	arrstr.Sort();
+    arrstr.Sort();
 
-	combo->Freeze();
-	long pos = combo->GetInsertionPoint();
-	wxString value = combo->GetValue();
+    combo->Freeze();
+    long pos = combo->GetInsertionPoint();
+    wxString value = combo->GetValue();
 
-	combo->Set(arrstr);
-	combo->ChangeValue(value);
-	combo->SetStringSelection(value);
-	combo->SetInsertionPoint(pos);
-	combo->Thaw();
+    combo->Set(arrstr);
+    combo->ChangeValue(value);
+    combo->SetStringSelection(value);
+    combo->SetInsertionPoint(pos);
+    combo->Thaw();
 }
 
-void SubsEditBox::OnActiveLineChanged(AssDialogue *new_line) {
-	wxEventBlocker blocker(this);
-	line = new_line;
-	commit_id = -1;
-
-	UpdateFields(AssFile::COMMIT_DIAG_FULL, false);
-}
+void SubsEditBox::OnActiveLineChanged(AssDialogue *new_line)
+{
+    wxEventBlocker blocker(this);
+    line = new_line;
+    commit_id = -1;
 
-void SubsEditBox::OnSelectedSetChanged() {
-	initial_times.clear();
+    UpdateFields(AssFile::COMMIT_DIAG_FULL, false);
 }
 
-void SubsEditBox::OnLineInitialTextChanged(std::string const& new_text) {
-	if (split_box->IsChecked())
-		secondary_editor->SetValue(to_wx(new_text));
+void SubsEditBox::OnSelectedSetChanged()
+{
+    initial_times.clear();
 }
 
-void SubsEditBox::UpdateFrameTiming(agi::vfr::Framerate const& fps) {
-	if (fps.IsLoaded()) {
-		by_frame->Enable(true);
-	}
-	else {
-		by_frame->Enable(false);
-		by_time->SetValue(true);
-		start_time->SetByFrame(false);
-		end_time->SetByFrame(false);
-		duration->SetByFrame(false);
-		c->subsGrid->SetByFrame(false);
-	}
+void SubsEditBox::OnLineInitialTextChanged(std::string const &new_text)
+{
+    if (split_box->IsChecked())
+        secondary_editor->SetValue(to_wx(new_text));
 }
 
-void SubsEditBox::OnKeyDown(wxKeyEvent &event) {
-	if (!osx::ime::process_key_event(edit_ctrl, event))
-		hotkey::check("Subtitle Edit Box", c, event);
+void SubsEditBox::UpdateFrameTiming(agi::vfr::Framerate const &fps)
+{
+    if (fps.IsLoaded()) {
+        by_frame->Enable(true);
+    }
+    else {
+        by_frame->Enable(false);
+        by_time->SetValue(true);
+        start_time->SetByFrame(false);
+        end_time->SetByFrame(false);
+        duration->SetByFrame(false);
+        c->subsGrid->SetByFrame(false);
+    }
+}
+
+void SubsEditBox::OnKeyDown(wxKeyEvent &event)
+{
+    if (!osx::ime::process_key_event(edit_ctrl, event))
+        hotkey::check("Subtitle Edit Box", c, event);
 }
 
-void SubsEditBox::OnChange(wxStyledTextEvent &event) {
-	if (line && edit_ctrl->GetTextRaw().data() != line->Text.get()) {
-		if (event.GetModificationType() & wxSTC_STARTACTION)
-			commit_id = -1;
-		CommitText(_("modify text"));
-		UpdateCharacterCount(line->Text);
-	}
+void SubsEditBox::OnChange(wxStyledTextEvent &event)
+{
+    if (line && edit_ctrl->GetTextRaw().data() != line->Text.get()) {
+        if (event.GetModificationType() & wxSTC_STARTACTION)
+            commit_id = -1;
+        CommitText(_("modify text"));
+        UpdateCharacterCount(line->Text);
+    }
 }
 
-void SubsEditBox::Commit(wxString const& desc, int type, bool amend, AssDialogue *line) {
-	file_changed_slot.Block();
-	commit_id = c->ass->Commit(desc, type, (amend && desc == last_commit_type) ? commit_id : -1, line);
-	file_changed_slot.Unblock();
-	last_commit_type = desc;
-	last_time_commit_type = -1;
-	initial_times.clear();
-	undo_timer.Start(30000, wxTIMER_ONE_SHOT);
+void SubsEditBox::Commit(wxString const &desc, int type, bool amend, AssDialogue *line)
+{
+    file_changed_slot.Block();
+    commit_id = c->ass->Commit(desc, type, (amend && desc == last_commit_type) ? commit_id : -1, line);
+    file_changed_slot.Unblock();
+    last_commit_type = desc;
+    last_time_commit_type = -1;
+    initial_times.clear();
+    undo_timer.Start(30000, wxTIMER_ONE_SHOT);
 }
 
 template<class setter>
-void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-	for_each(sel.begin(), sel.end(), set);
-	Commit(desc, type, amend, sel.size() == 1 ? *sel.begin() : nullptr);
+void SubsEditBox::SetSelectedRows(setter set, wxString const &desc, int type, bool amend)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    for_each(sel.begin(), sel.end(), set);
+    Commit(desc, type, amend, sel.size() == 1 ? *sel.begin() : nullptr);
 }
 
 template<class T>
-void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
-	SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, amend);
+void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const &desc, int type, bool amend)
+{
+    SetSelectedRows([&](AssDialogue * d) { d->*field = value; }, desc, type, amend);
 }
 
 template<class T>
-void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, wxString const& value, wxString const& desc, int type, bool amend) {
-	boost::flyweight<std::string> conv_value(from_wx(value));
-	SetSelectedRows([&](AssDialogue *d) { d->*field = conv_value; }, desc, type, amend);
-}
-
-void SubsEditBox::CommitText(wxString const& desc) {
-	auto data = edit_ctrl->GetTextRaw();
-	SetSelectedRows(&AssDialogue::Text, boost::flyweight<std::string>(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true);
-}
-
-void SubsEditBox::CommitTimes(TimeField field) {
-	auto const& sel = c->selectionController->GetSelectedSet();
-	for (AssDialogue *d : sel) {
-		if (!initial_times.count(d))
-			initial_times[d] = {d->Start, d->End};
-
-		switch (field) {
-			case TIME_START:
-				initial_times[d].first = d->Start = start_time->GetTime();
-				d->End = std::max(d->Start, initial_times[d].second);
-				break;
-
-			case TIME_END:
-				initial_times[d].second = d->End = end_time->GetTime();
-				d->Start = std::min(d->End, initial_times[d].first);
-				break;
-
-			case TIME_DURATION:
-				if (by_frame->GetValue()) {
-					auto const& fps = c->project->Timecodes();
-					d->End = fps.TimeAtFrame(fps.FrameAtTime(d->Start, agi::vfr::START) + duration->GetFrame() - 1, agi::vfr::END);
-				}
-				else
-					d->End = d->Start + duration->GetTime();
-				initial_times[d].second = d->End;
-				break;
-		}
-	}
-
-	start_time->SetTime(line->Start);
-	end_time->SetTime(line->End);
-
-	if (field != TIME_DURATION)
-		SetDurationField();
-
-	if (field != last_time_commit_type)
-		commit_id = -1;
+void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, wxString const &value, wxString const &desc, int type, bool amend)
+{
+    boost::flyweight<std::string> conv_value(from_wx(value));
+    SetSelectedRows([&](AssDialogue * d) { d->*field = conv_value; }, desc, type, amend);
+}
 
-	last_time_commit_type = field;
-	file_changed_slot.Block();
-	commit_id = c->ass->Commit(_("modify times"), AssFile::COMMIT_DIAG_TIME, commit_id, sel.size() == 1 ? *sel.begin() : nullptr);
-	file_changed_slot.Unblock();
+void SubsEditBox::CommitText(wxString const &desc)
+{
+    auto data = edit_ctrl->GetTextRaw();
+    SetSelectedRows(&AssDialogue::Text, boost::flyweight<std::string>(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true);
 }
 
-void SubsEditBox::SetDurationField() {
-	// With VFR, the frame count calculated from the duration in time can be
-	// completely wrong (since the duration is calculated as if it were a start
-	// time), so we need to explicitly set it with the correct units.
-	if (by_frame->GetValue())
-		duration->SetFrame(end_time->GetFrame() - start_time->GetFrame() + 1);
-	else
-		duration->SetTime(end_time->GetTime() - start_time->GetTime());
+void SubsEditBox::CommitTimes(TimeField field)
+{
+    auto const &sel = c->selectionController->GetSelectedSet();
+    for (AssDialogue *d : sel) {
+        if (!initial_times.count(d))
+            initial_times[d] = {d->Start, d->End};
+
+        switch (field) {
+        case TIME_START:
+            initial_times[d].first = d->Start = start_time->GetTime();
+            d->End = std::max(d->Start, initial_times[d].second);
+            break;
+
+        case TIME_END:
+            initial_times[d].second = d->End = end_time->GetTime();
+            d->Start = std::min(d->End, initial_times[d].first);
+            break;
+
+        case TIME_DURATION:
+            if (by_frame->GetValue()) {
+                auto const &fps = c->project->Timecodes();
+                d->End = fps.TimeAtFrame(fps.FrameAtTime(d->Start, agi::vfr::START) + duration->GetFrame() - 1, agi::vfr::END);
+            }
+            else
+                d->End = d->Start + duration->GetTime();
+            initial_times[d].second = d->End;
+            break;
+        }
+    }
+
+    start_time->SetTime(line->Start);
+    end_time->SetTime(line->End);
+
+    if (field != TIME_DURATION)
+        SetDurationField();
+
+    if (field != last_time_commit_type)
+        commit_id = -1;
+
+    last_time_commit_type = field;
+    file_changed_slot.Block();
+    commit_id = c->ass->Commit(_("modify times"), AssFile::COMMIT_DIAG_TIME, commit_id, sel.size() == 1 ? *sel.begin() : nullptr);
+    file_changed_slot.Unblock();
+}
+
+void SubsEditBox::SetDurationField()
+{
+    // With VFR, the frame count calculated from the duration in time can be
+    // completely wrong (since the duration is calculated as if it were a start
+    // time), so we need to explicitly set it with the correct units.
+    if (by_frame->GetValue())
+        duration->SetFrame(end_time->GetFrame() - start_time->GetFrame() + 1);
+    else
+        duration->SetTime(end_time->GetTime() - start_time->GetTime());
 }
 
-void SubsEditBox::OnSize(wxSizeEvent &evt) {
-	int availableWidth = GetVirtualSize().GetWidth();
-	int midMin = middle_left_sizer->GetMinSize().GetWidth();
-	int botMin = middle_right_sizer->GetMinSize().GetWidth();
+void SubsEditBox::OnSize(wxSizeEvent &evt)
+{
+    int availableWidth = GetVirtualSize().GetWidth();
+    int midMin = middle_left_sizer->GetMinSize().GetWidth();
+    int botMin = middle_right_sizer->GetMinSize().GetWidth();
+
+    if (button_bar_split) {
+        if (availableWidth > midMin + botMin) {
+            GetSizer()->Detach(middle_right_sizer);
+            middle_left_sizer->Add(middle_right_sizer, 0, wxALIGN_CENTER_VERTICAL);
+            button_bar_split = false;
+        }
+    }
+    else {
+        if (availableWidth < midMin) {
+            middle_left_sizer->Detach(middle_right_sizer);
+            GetSizer()->Insert(2, middle_right_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 3);
+            button_bar_split = true;
+        }
+    }
+
+    evt.Skip();
+}
+
+void SubsEditBox::OnFrameTimeRadio(wxCommandEvent &event)
+{
+    event.Skip();
 
-	if (button_bar_split) {
-		if (availableWidth > midMin + botMin) {
-			GetSizer()->Detach(middle_right_sizer);
-			middle_left_sizer->Add(middle_right_sizer,0,wxALIGN_CENTER_VERTICAL);
-			button_bar_split = false;
-		}
-	}
-	else {
-		if (availableWidth < midMin) {
-			middle_left_sizer->Detach(middle_right_sizer);
-			GetSizer()->Insert(2,middle_right_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
-			button_bar_split = true;
-		}
-	}
+    bool byFrame = by_frame->GetValue();
+    start_time->SetByFrame(byFrame);
+    end_time->SetByFrame(byFrame);
+    duration->SetByFrame(byFrame);
+    c->subsGrid->SetByFrame(byFrame);
 
-	evt.Skip();
+    SetDurationField();
 }
 
-void SubsEditBox::OnFrameTimeRadio(wxCommandEvent &event) {
-	event.Skip();
-
-	bool byFrame = by_frame->GetValue();
-	start_time->SetByFrame(byFrame);
-	end_time->SetByFrame(byFrame);
-	duration->SetByFrame(byFrame);
-	c->subsGrid->SetByFrame(byFrame);
+void SubsEditBox::SetControlsState(bool state)
+{
+    if (state == controls_enabled) return;
+    controls_enabled = state;
 
-	SetDurationField();
+    Enable(state);
+    if (!state) {
+        wxEventBlocker blocker(this);
+        edit_ctrl->SetTextTo("");
+    }
 }
 
-void SubsEditBox::SetControlsState(bool state) {
-	if (state == controls_enabled) return;
-	controls_enabled = state;
+void SubsEditBox::OnSplit(wxCommandEvent &)
+{
+    Freeze();
+    GetSizer()->Show(secondary_editor, split_box->IsChecked());
+    GetSizer()->Show(bottom_sizer, split_box->IsChecked());
+    Fit();
+    SetMinSize(GetSize());
+    GetParent()->GetSizer()->Layout();
+    Thaw();
 
-	Enable(state);
-	if (!state) {
-		wxEventBlocker blocker(this);
-		edit_ctrl->SetTextTo("");
-	}
+    if (split_box->IsChecked())
+        secondary_editor->SetValue(to_wx(c->initialLineState->GetInitialText()));
 }
-
-void SubsEditBox::OnSplit(wxCommandEvent&) {
-	Freeze();
-	GetSizer()->Show(secondary_editor, split_box->IsChecked());
-	GetSizer()->Show(bottom_sizer, split_box->IsChecked());
-	Fit();
-	SetMinSize(GetSize());
-	GetParent()->GetSizer()->Layout();
-	Thaw();
-
-	if (split_box->IsChecked())
-		secondary_editor->SetValue(to_wx(c->initialLineState->GetInitialText()));
-}
-
-void SubsEditBox::OnStyleChange(wxCommandEvent &evt) {
-	SetSelectedRows(&AssDialogue::Style, new_value(style_box, evt), _("style change"), AssFile::COMMIT_DIAG_META);
-	active_style = c->ass->GetStyle(line->Style);
+
+void SubsEditBox::OnStyleChange(wxCommandEvent &evt)
+{
+    SetSelectedRows(&AssDialogue::Style, new_value(style_box, evt), _("style change"), AssFile::COMMIT_DIAG_META);
+    active_style = c->ass->GetStyle(line->Style);
 }
 
-void SubsEditBox::OnActorChange(wxCommandEvent &evt) {
-	bool amend = evt.GetEventType() == wxEVT_TEXT;
-	SetSelectedRows(AssDialogue_Actor, new_value(actor_box, evt), _("actor change"), AssFile::COMMIT_DIAG_META, amend);
-	PopulateList(actor_box, AssDialogue_Actor);
+void SubsEditBox::OnActorChange(wxCommandEvent &evt)
+{
+    bool amend = evt.GetEventType() == wxEVT_TEXT;
+    SetSelectedRows(AssDialogue_Actor, new_value(actor_box, evt), _("actor change"), AssFile::COMMIT_DIAG_META, amend);
+    PopulateList(actor_box, AssDialogue_Actor);
 }
 
-void SubsEditBox::OnLayerEnter(wxCommandEvent &evt) {
-	SetSelectedRows(&AssDialogue::Layer, evt.GetInt(), _("layer change"), AssFile::COMMIT_DIAG_META);
+void SubsEditBox::OnLayerEnter(wxCommandEvent &evt)
+{
+    SetSelectedRows(&AssDialogue::Layer, evt.GetInt(), _("layer change"), AssFile::COMMIT_DIAG_META);
 }
 
-void SubsEditBox::OnEffectChange(wxCommandEvent &evt) {
-	bool amend = evt.GetEventType() == wxEVT_TEXT;
-	SetSelectedRows(AssDialogue_Effect, new_value(effect_box, evt), _("effect change"), AssFile::COMMIT_DIAG_META, amend);
-	PopulateList(effect_box, AssDialogue_Effect);
+void SubsEditBox::OnEffectChange(wxCommandEvent &evt)
+{
+    bool amend = evt.GetEventType() == wxEVT_TEXT;
+    SetSelectedRows(AssDialogue_Effect, new_value(effect_box, evt), _("effect change"), AssFile::COMMIT_DIAG_META, amend);
+    PopulateList(effect_box, AssDialogue_Effect);
 }
 
-void SubsEditBox::OnCommentChange(wxCommandEvent &evt) {
-	SetSelectedRows(&AssDialogue::Comment, !!evt.GetInt(), _("comment change"), AssFile::COMMIT_DIAG_META);
+void SubsEditBox::OnCommentChange(wxCommandEvent &evt)
+{
+    SetSelectedRows(&AssDialogue::Comment, !!evt.GetInt(), _("comment change"), AssFile::COMMIT_DIAG_META);
 }
 
-void SubsEditBox::CallCommand(const char *cmd_name) {
-	cmd::call(cmd_name, c);
-	edit_ctrl->SetFocus();
+void SubsEditBox::CallCommand(const char *cmd_name)
+{
+    cmd::call(cmd_name, c);
+    edit_ctrl->SetFocus();
 }
 
-void SubsEditBox::UpdateCharacterCount(std::string const& text) {
-	int ignore = agi::IGNORE_BLOCKS;
-	if (OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool())
-		ignore |= agi::IGNORE_WHITESPACE;
-	if (OPT_GET("Subtitle/Character Counter/Ignore Punctuation")->GetBool())
-		ignore |= agi::IGNORE_PUNCTUATION;
-	size_t length = agi::MaxLineLength(text, ignore);
-	char_count->SetValue(std::to_wstring(length));
-	size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();
-	if (limit && length > limit)
-		char_count->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle/Syntax/Background/Error")->GetColor()));
-	else
-		char_count->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+void SubsEditBox::UpdateCharacterCount(std::string const &text)
+{
+    int ignore = agi::IGNORE_BLOCKS;
+    if (OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool())
+        ignore |= agi::IGNORE_WHITESPACE;
+    if (OPT_GET("Subtitle/Character Counter/Ignore Punctuation")->GetBool())
+        ignore |= agi::IGNORE_PUNCTUATION;
+    size_t length = agi::MaxLineLength(text, ignore);
+    char_count->SetValue(std::to_wstring(length));
+    size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();
+    if (limit && length > limit)
+        char_count->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle/Syntax/Background/Error")->GetColor()));
+    else
+        char_count->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
 }
diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h
index 3cf9497e195b3a8e6a6bc93710126314bab1d228..5303d66e7ce40f3db2000430506af44b6b2902fe 100644
--- a/src/subs_edit_box.h
+++ b/src/subs_edit_box.h
@@ -61,146 +61,146 @@ template<class Base> class Placeholder;
 ///
 /// Controls the text edit and all surrounding controls
 class SubsEditBox final : public wxPanel {
-	enum TimeField {
-		TIME_START = 0,
-		TIME_END,
-		TIME_DURATION
-	};
-
-	std::vector<agi::signal::Connection> connections;
-
-	/// Currently active dialogue line
-	AssDialogue *line = nullptr;
-	AssStyle *active_style = nullptr;
-
-	/// Are the buttons currently split into two lines?
-	bool button_bar_split = true;
-	/// Are the controls currently enabled?
-	bool controls_enabled = true;
-
-	agi::Context *c;
-
-	agi::signal::Connection file_changed_slot;
-
-	// Box controls
-	wxCheckBox *comment_box;
-	wxComboBox *style_box;
-	wxButton *style_edit_button;
-	Placeholder<wxComboBox> *actor_box;
-	TimeEdit *start_time;
-	TimeEdit *end_time;
-	TimeEdit *duration;
-	wxSpinCtrl *layer;
-	std::array<wxTextCtrl *, 3> margin;
-	Placeholder<wxComboBox> *effect_box;
-	wxRadioButton *by_time;
-	wxRadioButton *by_frame;
-	wxTextCtrl *char_count;
-	wxCheckBox *split_box;
-
-	wxSizer *top_sizer;
-	wxSizer *middle_right_sizer;
-	wxSizer *middle_left_sizer;
-	wxSizer *bottom_sizer;
-
-	void SetControlsState(bool state);
-	/// @brief Update times of selected lines
-	/// @param field Field which changed
-	void CommitTimes(TimeField field);
-	/// @brief Commits the current edit box contents
-	/// @param desc Undo description to use
-	void CommitText(wxString const& desc);
-	void Commit(wxString const& desc, int type, bool amend, AssDialogue *line);
-
-	/// Last commit ID for undo coalescing
-	int commit_id = -1;
-
-	/// Last used commit message to avoid coalescing different types of changes
-	wxString last_commit_type;
-
-	/// Last field to get a time commit, as they all have the same commit message
-	int last_time_commit_type;
-
-	/// Timer to stop coalescing changes after a break with no edits
-	wxTimer undo_timer;
-
-	/// The start and end times of the selected lines without changes made to
-	/// avoid negative durations, so that they can be restored if future changes
-	/// eliminate the negative durations
-	boost::container::map<AssDialogue *, std::pair<agi::Time, agi::Time>> initial_times;
-
-	// Constructor helpers
-	wxTextCtrl *MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg);
-	TimeEdit *MakeTimeCtrl(wxString const& tooltip, TimeField field);
-	void MakeButton(const char *cmd_name);
-	wxButton *MakeBottomButton(const char *cmd_name);
-	wxComboBox *MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip);
-	wxRadioButton *MakeRadio(wxString const& text, bool start, wxString const& tooltip);
-
-	void OnChange(wxStyledTextEvent &event);
-	void OnKeyDown(wxKeyEvent &event);
-
-	void OnActiveLineChanged(AssDialogue *new_line);
-	void OnSelectedSetChanged();
-	void OnLineInitialTextChanged(std::string const& new_text);
-
-	void OnFrameTimeRadio(wxCommandEvent &event);
-	void OnStyleChange(wxCommandEvent &event);
-	void OnActorChange(wxCommandEvent &event);
-	void OnLayerEnter(wxCommandEvent &event);
-	void OnCommentChange(wxCommandEvent &);
-	void OnEffectChange(wxCommandEvent &);
-	void OnSize(wxSizeEvent &event);
-	void OnSplit(wxCommandEvent&);
-
-	void SetPlaceholderCtrl(wxControl *ctrl, wxString const& value);
-
-	/// @brief Set a field in each selected line to a specified value
-	/// @param set   Callable which updates a passed line
-	/// @param desc  Undo description to use
-	/// @param type  Commit type to use
-	/// @param amend Coalesce sequences of commits of the same type
-	template<class setter>
-	void SetSelectedRows(setter set, wxString const& desc, int type, bool amend = false);
-
-	/// @brief Set a field in each selected line to a specified value
-	/// @param field Field to set
-	/// @param value Value to set the field to
-	/// @param desc  Undo description to use
-	/// @param type  Commit type to use
-	/// @param amend Coalesce sequences of commits of the same type
-	template<class T>
-	void SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend = false);
-
-	template<class T>
-	void SetSelectedRows(T AssDialogueBase::*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);
-
-	void UpdateFields(int type, bool repopulate_lists);
-
-	/// Regenerate a dropdown list with the unique values of a dialogue field
-	void PopulateList(wxComboBox *combo, boost::flyweight<std::string> 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(std::string const& text);
-
-	/// Call a command the restore focus to the edit box
-	void CallCommand(const char *cmd_name);
-
-	void SetDurationField();
-
-	SubsTextEditCtrl *edit_ctrl;
-	wxTextCtrl *secondary_editor;
+    enum TimeField {
+        TIME_START = 0,
+        TIME_END,
+        TIME_DURATION
+    };
+
+    std::vector<agi::signal::Connection> connections;
+
+    /// Currently active dialogue line
+    AssDialogue *line = nullptr;
+    AssStyle *active_style = nullptr;
+
+    /// Are the buttons currently split into two lines?
+    bool button_bar_split = true;
+    /// Are the controls currently enabled?
+    bool controls_enabled = true;
+
+    agi::Context *c;
+
+    agi::signal::Connection file_changed_slot;
+
+    // Box controls
+    wxCheckBox *comment_box;
+    wxComboBox *style_box;
+    wxButton *style_edit_button;
+    Placeholder<wxComboBox> *actor_box;
+    TimeEdit *start_time;
+    TimeEdit *end_time;
+    TimeEdit *duration;
+    wxSpinCtrl *layer;
+    std::array<wxTextCtrl *, 3> margin;
+    Placeholder<wxComboBox> *effect_box;
+    wxRadioButton *by_time;
+    wxRadioButton *by_frame;
+    wxTextCtrl *char_count;
+    wxCheckBox *split_box;
+
+    wxSizer *top_sizer;
+    wxSizer *middle_right_sizer;
+    wxSizer *middle_left_sizer;
+    wxSizer *bottom_sizer;
+
+    void SetControlsState(bool state);
+    /// @brief Update times of selected lines
+    /// @param field Field which changed
+    void CommitTimes(TimeField field);
+    /// @brief Commits the current edit box contents
+    /// @param desc Undo description to use
+    void CommitText(wxString const &desc);
+    void Commit(wxString const &desc, int type, bool amend, AssDialogue *line);
+
+    /// Last commit ID for undo coalescing
+    int commit_id = -1;
+
+    /// Last used commit message to avoid coalescing different types of changes
+    wxString last_commit_type;
+
+    /// Last field to get a time commit, as they all have the same commit message
+    int last_time_commit_type;
+
+    /// Timer to stop coalescing changes after a break with no edits
+    wxTimer undo_timer;
+
+    /// The start and end times of the selected lines without changes made to
+    /// avoid negative durations, so that they can be restored if future changes
+    /// eliminate the negative durations
+    boost::container::map<AssDialogue *, std::pair<agi::Time, agi::Time>> initial_times;
+
+    // Constructor helpers
+    wxTextCtrl *MakeMarginCtrl(wxString const &tooltip, int margin, wxString const &commit_msg);
+    TimeEdit *MakeTimeCtrl(wxString const &tooltip, TimeField field);
+    void MakeButton(const char *cmd_name);
+    wxButton *MakeBottomButton(const char *cmd_name);
+    wxComboBox *MakeComboBox(wxString const &initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent &), wxString const &tooltip);
+    wxRadioButton *MakeRadio(wxString const &text, bool start, wxString const &tooltip);
+
+    void OnChange(wxStyledTextEvent &event);
+    void OnKeyDown(wxKeyEvent &event);
+
+    void OnActiveLineChanged(AssDialogue *new_line);
+    void OnSelectedSetChanged();
+    void OnLineInitialTextChanged(std::string const &new_text);
+
+    void OnFrameTimeRadio(wxCommandEvent &event);
+    void OnStyleChange(wxCommandEvent &event);
+    void OnActorChange(wxCommandEvent &event);
+    void OnLayerEnter(wxCommandEvent &event);
+    void OnCommentChange(wxCommandEvent &);
+    void OnEffectChange(wxCommandEvent &);
+    void OnSize(wxSizeEvent &event);
+    void OnSplit(wxCommandEvent &);
+
+    void SetPlaceholderCtrl(wxControl *ctrl, wxString const &value);
+
+    /// @brief Set a field in each selected line to a specified value
+    /// @param set   Callable which updates a passed line
+    /// @param desc  Undo description to use
+    /// @param type  Commit type to use
+    /// @param amend Coalesce sequences of commits of the same type
+    template<class setter>
+    void SetSelectedRows(setter set, wxString const &desc, int type, bool amend = false);
+
+    /// @brief Set a field in each selected line to a specified value
+    /// @param field Field to set
+    /// @param value Value to set the field to
+    /// @param desc  Undo description to use
+    /// @param type  Commit type to use
+    /// @param amend Coalesce sequences of commits of the same type
+    template<class T>
+    void SetSelectedRows(T AssDialogueBase::*field, T value, wxString const &desc, int type, bool amend = false);
+
+    template<class T>
+    void SetSelectedRows(T AssDialogueBase::*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);
+
+    void UpdateFields(int type, bool repopulate_lists);
+
+    /// Regenerate a dropdown list with the unique values of a dialogue field
+    void PopulateList(wxComboBox *combo, boost::flyweight<std::string> 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(std::string const &text);
+
+    /// Call a command the restore focus to the edit box
+    void CallCommand(const char *cmd_name);
+
+    void SetDurationField();
+
+    SubsTextEditCtrl *edit_ctrl;
+    wxTextCtrl *secondary_editor;
 
 public:
-	/// @brief Constructor
-	/// @param parent Parent window
-	SubsEditBox(wxWindow *parent, agi::Context *context);
-	~SubsEditBox();
+    /// @brief Constructor
+    /// @param parent Parent window
+    SubsEditBox(wxWindow *parent, agi::Context *context);
+    ~SubsEditBox();
 };
diff --git a/src/subs_edit_ctrl.cpp b/src/subs_edit_ctrl.cpp
index 4618ea4097c80b7a08b1424bf0230f7fc962b322..bf43e3c983fc1ed95a00b372da8f18af9e14f4a8 100644
--- a/src/subs_edit_ctrl.cpp
+++ b/src/subs_edit_ctrl.cpp
@@ -58,515 +58,534 @@
 
 /// Event ids
 enum {
-	EDIT_MENU_SPLIT_PRESERVE = 1400,
-	EDIT_MENU_SPLIT_ESTIMATE,
-	EDIT_MENU_SPLIT_VIDEO,
-	EDIT_MENU_CUT,
-	EDIT_MENU_COPY,
-	EDIT_MENU_PASTE,
-	EDIT_MENU_SELECT_ALL,
-	EDIT_MENU_ADD_TO_DICT,
-	EDIT_MENU_REMOVE_FROM_DICT,
-	EDIT_MENU_SUGGESTION,
-	EDIT_MENU_SUGGESTIONS,
-	EDIT_MENU_THESAURUS = 1450,
-	EDIT_MENU_THESAURUS_SUGS,
-	EDIT_MENU_DIC_LANGUAGE = 1600,
-	EDIT_MENU_DIC_LANGS,
-	EDIT_MENU_THES_LANGUAGE = 1700,
-	EDIT_MENU_THES_LANGS
+    EDIT_MENU_SPLIT_PRESERVE = 1400,
+    EDIT_MENU_SPLIT_ESTIMATE,
+    EDIT_MENU_SPLIT_VIDEO,
+    EDIT_MENU_CUT,
+    EDIT_MENU_COPY,
+    EDIT_MENU_PASTE,
+    EDIT_MENU_SELECT_ALL,
+    EDIT_MENU_ADD_TO_DICT,
+    EDIT_MENU_REMOVE_FROM_DICT,
+    EDIT_MENU_SUGGESTION,
+    EDIT_MENU_SUGGESTIONS,
+    EDIT_MENU_THESAURUS = 1450,
+    EDIT_MENU_THESAURUS_SUGS,
+    EDIT_MENU_DIC_LANGUAGE = 1600,
+    EDIT_MENU_DIC_LANGS,
+    EDIT_MENU_THES_LANGUAGE = 1700,
+    EDIT_MENU_THES_LANGS
 };
 
-SubsTextEditCtrl::SubsTextEditCtrl(wxWindow* parent, wxSize wsize, long style, agi::Context *context)
-: wxStyledTextCtrl(parent, -1, wxDefaultPosition, wsize, style)
-, spellchecker(SpellCheckerFactory::GetSpellChecker())
-, thesaurus(agi::make_unique<Thesaurus>())
-, context(context)
+SubsTextEditCtrl::SubsTextEditCtrl(wxWindow *parent, wxSize wsize, long style, agi::Context *context)
+    : wxStyledTextCtrl(parent, -1, wxDefaultPosition, wsize, style)
+    , spellchecker(SpellCheckerFactory::GetSpellChecker())
+    , thesaurus(agi::make_unique<Thesaurus>())
+    , context(context)
 {
-	osx::ime::inject(this);
-
-	// Set properties
-	SetWrapMode(wxSTC_WRAP_WORD);
-	SetMarginWidth(1,0);
-	UsePopUp(false);
-	SetStyles();
-
-	// Set hotkeys
-	CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_CTRL);
-	CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_SHIFT);
-	CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_NORM);
-	CmdKeyClear(wxSTC_KEY_TAB,wxSTC_SCMOD_NORM);
-	CmdKeyClear(wxSTC_KEY_TAB,wxSTC_SCMOD_SHIFT);
-	CmdKeyClear('D',wxSTC_SCMOD_CTRL);
-	CmdKeyClear('L',wxSTC_SCMOD_CTRL);
-	CmdKeyClear('L',wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
-	CmdKeyClear('T',wxSTC_SCMOD_CTRL);
-	CmdKeyClear('T',wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
-	CmdKeyClear('U',wxSTC_SCMOD_CTRL);
-
-	using std::bind;
-
-	Bind(wxEVT_CHAR_HOOK, &SubsTextEditCtrl::OnKeyDown, this);
-
-	Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Cut, this), EDIT_MENU_CUT);
-	Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Copy, this), EDIT_MENU_COPY);
-	Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Paste, this), EDIT_MENU_PASTE);
-	Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::SelectAll, this), EDIT_MENU_SELECT_ALL);
-
-	if (context) {
-		Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/preserve", context), EDIT_MENU_SPLIT_PRESERVE);
-		Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/estimate", context), EDIT_MENU_SPLIT_ESTIMATE);
-		Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/video", context), EDIT_MENU_SPLIT_VIDEO);
-	}
-
-	Bind(wxEVT_CONTEXT_MENU, &SubsTextEditCtrl::OnContextMenu, this);
-	Bind(wxEVT_IDLE, std::bind(&SubsTextEditCtrl::UpdateCallTip, this));
-	Bind(wxEVT_STC_DOUBLECLICK, &SubsTextEditCtrl::OnDoubleClick, this);
-	Bind(wxEVT_STC_STYLENEEDED, [=](wxStyledTextEvent&) {
-		{
-			std::string text = GetTextRaw().data();
-			if (text == line_text) return;
-			line_text = move(text);
-		}
-
-		UpdateStyle();
-	});
-
-	OPT_SUB("Subtitle/Edit Box/Font Face", &SubsTextEditCtrl::SetStyles, this);
-	OPT_SUB("Subtitle/Edit Box/Font Size", &SubsTextEditCtrl::SetStyles, this);
-	Subscribe("Normal");
-	Subscribe("Comment");
-	Subscribe("Drawing");
-	Subscribe("Brackets");
-	Subscribe("Slashes");
-	Subscribe("Tags");
-	Subscribe("Error");
-	Subscribe("Parameters");
-	Subscribe("Line Break");
-	Subscribe("Karaoke Template");
-	Subscribe("Karaoke Variable");
-
-	OPT_SUB("Colour/Subtitle/Background", &SubsTextEditCtrl::SetStyles, this);
-	OPT_SUB("Subtitle/Highlight/Syntax", &SubsTextEditCtrl::UpdateStyle, this);
-	OPT_SUB("App/Call Tips", &SubsTextEditCtrl::UpdateCallTip, this);
-
-	Bind(wxEVT_MENU, [=](wxCommandEvent&) {
-		if (spellchecker) spellchecker->AddWord(currentWord);
-		UpdateStyle();
-		SetFocus();
-	}, EDIT_MENU_ADD_TO_DICT);
-
-	Bind(wxEVT_MENU, [=](wxCommandEvent&) {
-		if (spellchecker) spellchecker->RemoveWord(currentWord);
-		UpdateStyle();
-		SetFocus();
-	}, EDIT_MENU_REMOVE_FROM_DICT);
+    osx::ime::inject(this);
+
+    // Set properties
+    SetWrapMode(wxSTC_WRAP_WORD);
+    SetMarginWidth(1, 0);
+    UsePopUp(false);
+    SetStyles();
+
+    // Set hotkeys
+    CmdKeyClear(wxSTC_KEY_RETURN, wxSTC_SCMOD_CTRL);
+    CmdKeyClear(wxSTC_KEY_RETURN, wxSTC_SCMOD_SHIFT);
+    CmdKeyClear(wxSTC_KEY_RETURN, wxSTC_SCMOD_NORM);
+    CmdKeyClear(wxSTC_KEY_TAB, wxSTC_SCMOD_NORM);
+    CmdKeyClear(wxSTC_KEY_TAB, wxSTC_SCMOD_SHIFT);
+    CmdKeyClear('D', wxSTC_SCMOD_CTRL);
+    CmdKeyClear('L', wxSTC_SCMOD_CTRL);
+    CmdKeyClear('L', wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
+    CmdKeyClear('T', wxSTC_SCMOD_CTRL);
+    CmdKeyClear('T', wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
+    CmdKeyClear('U', wxSTC_SCMOD_CTRL);
+
+    using std::bind;
+
+    Bind(wxEVT_CHAR_HOOK, &SubsTextEditCtrl::OnKeyDown, this);
+
+    Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Cut, this), EDIT_MENU_CUT);
+    Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Copy, this), EDIT_MENU_COPY);
+    Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Paste, this), EDIT_MENU_PASTE);
+    Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::SelectAll, this), EDIT_MENU_SELECT_ALL);
+
+    if (context) {
+        Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/preserve", context), EDIT_MENU_SPLIT_PRESERVE);
+        Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/estimate", context), EDIT_MENU_SPLIT_ESTIMATE);
+        Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/video", context), EDIT_MENU_SPLIT_VIDEO);
+    }
+
+    Bind(wxEVT_CONTEXT_MENU, &SubsTextEditCtrl::OnContextMenu, this);
+    Bind(wxEVT_IDLE, std::bind(&SubsTextEditCtrl::UpdateCallTip, this));
+    Bind(wxEVT_STC_DOUBLECLICK, &SubsTextEditCtrl::OnDoubleClick, this);
+    Bind(wxEVT_STC_STYLENEEDED, [ = ](wxStyledTextEvent &) {
+        {
+            std::string text = GetTextRaw().data();
+            if (text == line_text) return;
+            line_text = move(text);
+        }
+
+        UpdateStyle();
+    });
+
+    OPT_SUB("Subtitle/Edit Box/Font Face", &SubsTextEditCtrl::SetStyles, this);
+    OPT_SUB("Subtitle/Edit Box/Font Size", &SubsTextEditCtrl::SetStyles, this);
+    Subscribe("Normal");
+    Subscribe("Comment");
+    Subscribe("Drawing");
+    Subscribe("Brackets");
+    Subscribe("Slashes");
+    Subscribe("Tags");
+    Subscribe("Error");
+    Subscribe("Parameters");
+    Subscribe("Line Break");
+    Subscribe("Karaoke Template");
+    Subscribe("Karaoke Variable");
+
+    OPT_SUB("Colour/Subtitle/Background", &SubsTextEditCtrl::SetStyles, this);
+    OPT_SUB("Subtitle/Highlight/Syntax", &SubsTextEditCtrl::UpdateStyle, this);
+    OPT_SUB("App/Call Tips", &SubsTextEditCtrl::UpdateCallTip, this);
+
+    Bind(wxEVT_MENU, [ = ](wxCommandEvent &) {
+        if (spellchecker) spellchecker->AddWord(currentWord);
+        UpdateStyle();
+        SetFocus();
+    }, EDIT_MENU_ADD_TO_DICT);
+
+    Bind(wxEVT_MENU, [ = ](wxCommandEvent &) {
+        if (spellchecker) spellchecker->RemoveWord(currentWord);
+        UpdateStyle();
+        SetFocus();
+    }, EDIT_MENU_REMOVE_FROM_DICT);
 }
 
-SubsTextEditCtrl::~SubsTextEditCtrl() {
+SubsTextEditCtrl::~SubsTextEditCtrl()
+{
 }
 
-void SubsTextEditCtrl::Subscribe(std::string const& name) {
-	OPT_SUB("Colour/Subtitle/Syntax/" + name, &SubsTextEditCtrl::SetStyles, this);
-	OPT_SUB("Colour/Subtitle/Syntax/Background/" + name, &SubsTextEditCtrl::SetStyles, this);
-	OPT_SUB("Colour/Subtitle/Syntax/Bold/" + name, &SubsTextEditCtrl::SetStyles, this);
+void SubsTextEditCtrl::Subscribe(std::string const &name)
+{
+    OPT_SUB("Colour/Subtitle/Syntax/" + name, &SubsTextEditCtrl::SetStyles, this);
+    OPT_SUB("Colour/Subtitle/Syntax/Background/" + name, &SubsTextEditCtrl::SetStyles, this);
+    OPT_SUB("Colour/Subtitle/Syntax/Bold/" + name, &SubsTextEditCtrl::SetStyles, this);
 }
 
-BEGIN_EVENT_TABLE(SubsTextEditCtrl,wxStyledTextCtrl)
-	EVT_KILL_FOCUS(SubsTextEditCtrl::OnLoseFocus)
+BEGIN_EVENT_TABLE(SubsTextEditCtrl, wxStyledTextCtrl)
+    EVT_KILL_FOCUS(SubsTextEditCtrl::OnLoseFocus)
 
-	EVT_MENU_RANGE(EDIT_MENU_SUGGESTIONS,EDIT_MENU_THESAURUS-1,SubsTextEditCtrl::OnUseSuggestion)
-	EVT_MENU_RANGE(EDIT_MENU_THESAURUS_SUGS,EDIT_MENU_DIC_LANGUAGE-1,SubsTextEditCtrl::OnUseSuggestion)
-	EVT_MENU_RANGE(EDIT_MENU_DIC_LANGS,EDIT_MENU_THES_LANGUAGE-1,SubsTextEditCtrl::OnSetDicLanguage)
-	EVT_MENU_RANGE(EDIT_MENU_THES_LANGS,EDIT_MENU_THES_LANGS+100,SubsTextEditCtrl::OnSetThesLanguage)
+    EVT_MENU_RANGE(EDIT_MENU_SUGGESTIONS, EDIT_MENU_THESAURUS - 1, SubsTextEditCtrl::OnUseSuggestion)
+    EVT_MENU_RANGE(EDIT_MENU_THESAURUS_SUGS, EDIT_MENU_DIC_LANGUAGE - 1, SubsTextEditCtrl::OnUseSuggestion)
+    EVT_MENU_RANGE(EDIT_MENU_DIC_LANGS, EDIT_MENU_THES_LANGUAGE - 1, SubsTextEditCtrl::OnSetDicLanguage)
+    EVT_MENU_RANGE(EDIT_MENU_THES_LANGS, EDIT_MENU_THES_LANGS + 100, SubsTextEditCtrl::OnSetThesLanguage)
 END_EVENT_TABLE()
 
-void SubsTextEditCtrl::OnLoseFocus(wxFocusEvent &event) {
-	CallTipCancel();
-	event.Skip();
+void SubsTextEditCtrl::OnLoseFocus(wxFocusEvent &event)
+{
+    CallTipCancel();
+    event.Skip();
 }
 
-void SubsTextEditCtrl::OnKeyDown(wxKeyEvent &event) {
-	if (osx::ime::process_key_event(this, event)) return;
-	event.Skip();
-
-	// Workaround for wxSTC eating tabs.
-	if (event.GetKeyCode() == WXK_TAB)
-		Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
-	else if (event.GetKeyCode() == WXK_RETURN && event.GetModifiers() == wxMOD_SHIFT) {
-		auto sel_start = GetSelectionStart(), sel_end = GetSelectionEnd();
-		wxCharBuffer old = GetTextRaw();
-		std::string data(old.data(), sel_start);
-		data.append("\\N");
-		data.append(old.data() + sel_end, old.length() - sel_end);
-		SetTextRaw(data.c_str());
-
-		SetSelection(sel_start + 2, sel_start + 2);
-		event.Skip(false);
-	}
+void SubsTextEditCtrl::OnKeyDown(wxKeyEvent &event)
+{
+    if (osx::ime::process_key_event(this, event)) return;
+    event.Skip();
+
+    // Workaround for wxSTC eating tabs.
+    if (event.GetKeyCode() == WXK_TAB)
+        Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
+    else if (event.GetKeyCode() == WXK_RETURN && event.GetModifiers() == wxMOD_SHIFT) {
+        auto sel_start = GetSelectionStart(), sel_end = GetSelectionEnd();
+        wxCharBuffer old = GetTextRaw();
+        std::string data(old.data(), sel_start);
+        data.append("\\N");
+        data.append(old.data() + sel_end, old.length() - sel_end);
+        SetTextRaw(data.c_str());
+
+        SetSelection(sel_start + 2, sel_start + 2);
+        event.Skip(false);
+    }
 }
 
-void SubsTextEditCtrl::SetSyntaxStyle(int id, wxFont &font, std::string const& name, wxColor const& default_background) {
-	StyleSetFont(id, font);
-	StyleSetBold(id, OPT_GET("Colour/Subtitle/Syntax/Bold/" + name)->GetBool());
-	StyleSetForeground(id, to_wx(OPT_GET("Colour/Subtitle/Syntax/" + name)->GetColor()));
-	const agi::OptionValue *background = OPT_GET("Colour/Subtitle/Syntax/Background/" + name);
-	if (background->GetType() == agi::OptionType::Color)
-		StyleSetBackground(id, to_wx(background->GetColor()));
-	else
-		StyleSetBackground(id, default_background);
+void SubsTextEditCtrl::SetSyntaxStyle(int id, wxFont &font, std::string const &name, wxColor const &default_background)
+{
+    StyleSetFont(id, font);
+    StyleSetBold(id, OPT_GET("Colour/Subtitle/Syntax/Bold/" + name)->GetBool());
+    StyleSetForeground(id, to_wx(OPT_GET("Colour/Subtitle/Syntax/" + name)->GetColor()));
+    const agi::OptionValue *background = OPT_GET("Colour/Subtitle/Syntax/Background/" + name);
+    if (background->GetType() == agi::OptionType::Color)
+        StyleSetBackground(id, to_wx(background->GetColor()));
+    else
+        StyleSetBackground(id, default_background);
 }
 
-void SubsTextEditCtrl::SetStyles() {
-	wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
-	font.SetEncoding(wxFONTENCODING_DEFAULT); // this solves problems with some fonts not working properly
-	wxString fontname = FontFace("Subtitle/Edit Box");
-	if (!fontname.empty()) font.SetFaceName(fontname);
-	font.SetPointSize(OPT_GET("Subtitle/Edit Box/Font Size")->GetInt());
-
-	auto default_background = to_wx(OPT_GET("Colour/Subtitle/Background")->GetColor());
-
-	namespace ss = agi::ass::SyntaxStyle;
-	SetSyntaxStyle(ss::NORMAL, font, "Normal", default_background);
-	SetSyntaxStyle(ss::COMMENT, font, "Comment", default_background);
-	SetSyntaxStyle(ss::DRAWING, font, "Drawing", default_background);
-	SetSyntaxStyle(ss::OVERRIDE, font, "Brackets", default_background);
-	SetSyntaxStyle(ss::PUNCTUATION, font, "Slashes", default_background);
-	SetSyntaxStyle(ss::TAG, font, "Tags", default_background);
-	SetSyntaxStyle(ss::ERROR, font, "Error", default_background);
-	SetSyntaxStyle(ss::PARAMETER, font, "Parameters", default_background);
-	SetSyntaxStyle(ss::LINE_BREAK, font, "Line Break", default_background);
-	SetSyntaxStyle(ss::KARAOKE_TEMPLATE, font, "Karaoke Template", default_background);
-	SetSyntaxStyle(ss::KARAOKE_VARIABLE, font, "Karaoke Variable", default_background);
-
-	SetCaretForeground(StyleGetForeground(ss::NORMAL));
-	StyleSetBackground(wxSTC_STYLE_DEFAULT, default_background);
-
-	// Misspelling indicator
-	IndicatorSetStyle(0,wxSTC_INDIC_SQUIGGLE);
-	IndicatorSetForeground(0,wxColour(255,0,0));
-
-	// IME pending text indicator
-	IndicatorSetStyle(1, wxSTC_INDIC_PLAIN);
-	IndicatorSetUnder(1, true);
+void SubsTextEditCtrl::SetStyles()
+{
+    wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
+    font.SetEncoding(wxFONTENCODING_DEFAULT); // this solves problems with some fonts not working properly
+    wxString fontname = FontFace("Subtitle/Edit Box");
+    if (!fontname.empty()) font.SetFaceName(fontname);
+    font.SetPointSize(OPT_GET("Subtitle/Edit Box/Font Size")->GetInt());
+
+    auto default_background = to_wx(OPT_GET("Colour/Subtitle/Background")->GetColor());
+
+    namespace ss = agi::ass::SyntaxStyle;
+    SetSyntaxStyle(ss::NORMAL, font, "Normal", default_background);
+    SetSyntaxStyle(ss::COMMENT, font, "Comment", default_background);
+    SetSyntaxStyle(ss::DRAWING, font, "Drawing", default_background);
+    SetSyntaxStyle(ss::OVERRIDE, font, "Brackets", default_background);
+    SetSyntaxStyle(ss::PUNCTUATION, font, "Slashes", default_background);
+    SetSyntaxStyle(ss::TAG, font, "Tags", default_background);
+    SetSyntaxStyle(ss::ERROR, font, "Error", default_background);
+    SetSyntaxStyle(ss::PARAMETER, font, "Parameters", default_background);
+    SetSyntaxStyle(ss::LINE_BREAK, font, "Line Break", default_background);
+    SetSyntaxStyle(ss::KARAOKE_TEMPLATE, font, "Karaoke Template", default_background);
+    SetSyntaxStyle(ss::KARAOKE_VARIABLE, font, "Karaoke Variable", default_background);
+
+    SetCaretForeground(StyleGetForeground(ss::NORMAL));
+    StyleSetBackground(wxSTC_STYLE_DEFAULT, default_background);
+
+    // Misspelling indicator
+    IndicatorSetStyle(0, wxSTC_INDIC_SQUIGGLE);
+    IndicatorSetForeground(0, wxColour(255, 0, 0));
+
+    // IME pending text indicator
+    IndicatorSetStyle(1, wxSTC_INDIC_PLAIN);
+    IndicatorSetUnder(1, true);
 }
 
-void SubsTextEditCtrl::UpdateStyle() {
-	AssDialogue *diag = context ? context->selectionController->GetActiveLine() : nullptr;
-	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);
-
-	cursor_pos = -1;
-	UpdateCallTip();
-
-	StartStyling(0,255);
-
-	if (!OPT_GET("Subtitle/Highlight/Syntax")->GetBool()) {
-		SetStyling(line_text.size(), 0);
-		return;
-	}
-
-	if (line_text.empty()) return;
-
-	SetIndicatorCurrent(0);
-	size_t pos = 0;
-	for (auto const& style_range : agi::ass::SyntaxHighlight(line_text, tokenized_line, spellchecker.get())) {
-		if (style_range.type == agi::ass::SyntaxStyle::SPELLING) {
-			SetStyling(style_range.length, agi::ass::SyntaxStyle::NORMAL);
-			IndicatorFillRange(pos, style_range.length);
-		}
-		else {
-			SetStyling(style_range.length, style_range.type);
-			IndicatorClearRange(pos, style_range.length);
-		}
-		pos += style_range.length;
-	}
+void SubsTextEditCtrl::UpdateStyle()
+{
+    AssDialogue *diag = context ? context->selectionController->GetActiveLine() : nullptr;
+    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);
+
+    cursor_pos = -1;
+    UpdateCallTip();
+
+    StartStyling(0, 255);
+
+    if (!OPT_GET("Subtitle/Highlight/Syntax")->GetBool()) {
+        SetStyling(line_text.size(), 0);
+        return;
+    }
+
+    if (line_text.empty()) return;
+
+    SetIndicatorCurrent(0);
+    size_t pos = 0;
+    for (auto const &style_range : agi::ass::SyntaxHighlight(line_text, tokenized_line, spellchecker.get())) {
+        if (style_range.type == agi::ass::SyntaxStyle::SPELLING) {
+            SetStyling(style_range.length, agi::ass::SyntaxStyle::NORMAL);
+            IndicatorFillRange(pos, style_range.length);
+        }
+        else {
+            SetStyling(style_range.length, style_range.type);
+            IndicatorClearRange(pos, style_range.length);
+        }
+        pos += style_range.length;
+    }
 }
 
-void SubsTextEditCtrl::UpdateCallTip() {
-	if (!OPT_GET("App/Call Tips")->GetBool()) return;
+void SubsTextEditCtrl::UpdateCallTip()
+{
+    if (!OPT_GET("App/Call Tips")->GetBool()) return;
 
-	int pos = GetCurrentPos();
-	if (pos == cursor_pos) return;
-	cursor_pos = pos;
+    int pos = GetCurrentPos();
+    if (pos == cursor_pos) return;
+    cursor_pos = pos;
 
-	agi::Calltip new_calltip = agi::GetCalltip(tokenized_line, line_text, pos);
+    agi::Calltip new_calltip = agi::GetCalltip(tokenized_line, line_text, pos);
 
-	if (!new_calltip.text) {
-		CallTipCancel();
-		return;
-	}
+    if (!new_calltip.text) {
+        CallTipCancel();
+        return;
+    }
 
-	if (!CallTipActive() || calltip_position != new_calltip.tag_position || calltip_text != new_calltip.text)
-		CallTipShow(new_calltip.tag_position, wxString::FromUTF8Unchecked(new_calltip.text));
+    if (!CallTipActive() || calltip_position != new_calltip.tag_position || calltip_text != new_calltip.text)
+        CallTipShow(new_calltip.tag_position, wxString::FromUTF8Unchecked(new_calltip.text));
 
-	calltip_position = new_calltip.tag_position;
-	calltip_text = new_calltip.text;
+    calltip_position = new_calltip.tag_position;
+    calltip_text = new_calltip.text;
 
-	CallTipSetHighlight(new_calltip.highlight_start, new_calltip.highlight_end);
+    CallTipSetHighlight(new_calltip.highlight_start, new_calltip.highlight_end);
 }
 
-void SubsTextEditCtrl::SetTextTo(std::string const& text) {
-	osx::ime::invalidate(this);
-	SetEvtHandlerEnabled(false);
-	Freeze();
-
-	auto insertion_point = GetInsertionPoint();
-	if (static_cast<size_t>(insertion_point) > line_text.size())
-		line_text = GetTextRaw().data();
-	auto old_pos = agi::CharacterCount(line_text.begin(), line_text.begin() + insertion_point, 0);
-	line_text.clear();
-
-	if (context) {
-		context->textSelectionController->SetSelection(0, 0);
-		SetTextRaw(text.c_str());
-		auto pos = agi::IndexOfCharacter(text, old_pos);
-		context->textSelectionController->SetSelection(pos, pos);
-	}
-	else {
-		SetSelection(0, 0);
-		SetTextRaw(text.c_str());
-		auto pos = agi::IndexOfCharacter(text, old_pos);
-		SetSelection(pos, pos);
-	}
-
-	SetEvtHandlerEnabled(true);
-	Thaw();
+void SubsTextEditCtrl::SetTextTo(std::string const &text)
+{
+    osx::ime::invalidate(this);
+    SetEvtHandlerEnabled(false);
+    Freeze();
+
+    auto insertion_point = GetInsertionPoint();
+    if (static_cast<size_t>(insertion_point) > line_text.size())
+        line_text = GetTextRaw().data();
+    auto old_pos = agi::CharacterCount(line_text.begin(), line_text.begin() + insertion_point, 0);
+    line_text.clear();
+
+    if (context) {
+        context->textSelectionController->SetSelection(0, 0);
+        SetTextRaw(text.c_str());
+        auto pos = agi::IndexOfCharacter(text, old_pos);
+        context->textSelectionController->SetSelection(pos, pos);
+    }
+    else {
+        SetSelection(0, 0);
+        SetTextRaw(text.c_str());
+        auto pos = agi::IndexOfCharacter(text, old_pos);
+        SetSelection(pos, pos);
+    }
+
+    SetEvtHandlerEnabled(true);
+    Thaw();
 }
 
-void SubsTextEditCtrl::Paste() {
-	std::string data = GetClipboard();
+void SubsTextEditCtrl::Paste()
+{
+    std::string data = GetClipboard();
 
-	boost::replace_all(data, "\r\n", "\\N");
-	boost::replace_all(data, "\n", "\\N");
-	boost::replace_all(data, "\r", "\\N");
+    boost::replace_all(data, "\r\n", "\\N");
+    boost::replace_all(data, "\n", "\\N");
+    boost::replace_all(data, "\r", "\\N");
 
-	wxCharBuffer old = GetTextRaw();
-	data.insert(0, old.data(), GetSelectionStart());
-	int sel_start = data.size();
-	data.append(old.data() + GetSelectionEnd());
+    wxCharBuffer old = GetTextRaw();
+    data.insert(0, old.data(), GetSelectionStart());
+    int sel_start = data.size();
+    data.append(old.data() + GetSelectionEnd());
 
-	SetTextRaw(data.c_str());
+    SetTextRaw(data.c_str());
 
-	SetSelectionStart(sel_start);
-	SetSelectionEnd(sel_start);
+    SetSelectionStart(sel_start);
+    SetSelectionEnd(sel_start);
 }
 
-void SubsTextEditCtrl::OnContextMenu(wxContextMenuEvent &event) {
-	wxPoint pos = event.GetPosition();
-	int activePos;
-	if (pos == wxDefaultPosition)
-		activePos = GetCurrentPos();
-	else
-		activePos = PositionFromPoint(ScreenToClient(pos));
-
-	currentWordPos = GetBoundsOfWordAtPosition(activePos);
-	currentWord = line_text.substr(currentWordPos.first, currentWordPos.second);
-
-	wxMenu menu;
-	if (spellchecker)
-		AddSpellCheckerEntries(menu);
-
-	// Append language list
-	menu.Append(-1,_("Spell checker language"), GetLanguagesMenu(
-		EDIT_MENU_DIC_LANGS,
-		to_wx(OPT_GET("Tool/Spell Checker/Language")->GetString()),
-		to_wx(spellchecker->GetLanguageList())));
-	menu.AppendSeparator();
-
-	AddThesaurusEntries(menu);
-
-	// Standard actions
-	menu.Append(EDIT_MENU_CUT,_("Cu&t"))->Enable(GetSelectionStart()-GetSelectionEnd() != 0);
-	menu.Append(EDIT_MENU_COPY,_("&Copy"))->Enable(GetSelectionStart()-GetSelectionEnd() != 0);
-	menu.Append(EDIT_MENU_PASTE,_("&Paste"))->Enable(CanPaste());
-	menu.AppendSeparator();
-	menu.Append(EDIT_MENU_SELECT_ALL,_("Select &All"));
-
-	// Split
-	if (context) {
-		menu.AppendSeparator();
-		menu.Append(EDIT_MENU_SPLIT_PRESERVE, _("Split at cursor (preserve times)"));
-		menu.Append(EDIT_MENU_SPLIT_ESTIMATE, _("Split at cursor (estimate times)"));
-		cmd::Command *split_video = cmd::get("edit/line/split/video");
-		menu.Append(EDIT_MENU_SPLIT_VIDEO, split_video->StrMenu(context))->Enable(split_video->Validate(context));
-	}
-
-	PopupMenu(&menu);
+void SubsTextEditCtrl::OnContextMenu(wxContextMenuEvent &event)
+{
+    wxPoint pos = event.GetPosition();
+    int activePos;
+    if (pos == wxDefaultPosition)
+        activePos = GetCurrentPos();
+    else
+        activePos = PositionFromPoint(ScreenToClient(pos));
+
+    currentWordPos = GetBoundsOfWordAtPosition(activePos);
+    currentWord = line_text.substr(currentWordPos.first, currentWordPos.second);
+
+    wxMenu menu;
+    if (spellchecker)
+        AddSpellCheckerEntries(menu);
+
+    // Append language list
+    menu.Append(-1, _("Spell checker language"), GetLanguagesMenu(
+                    EDIT_MENU_DIC_LANGS,
+                    to_wx(OPT_GET("Tool/Spell Checker/Language")->GetString()),
+                    to_wx(spellchecker->GetLanguageList())));
+    menu.AppendSeparator();
+
+    AddThesaurusEntries(menu);
+
+    // Standard actions
+    menu.Append(EDIT_MENU_CUT, _("Cu&t"))->Enable(GetSelectionStart() - GetSelectionEnd() != 0);
+    menu.Append(EDIT_MENU_COPY, _("&Copy"))->Enable(GetSelectionStart() - GetSelectionEnd() != 0);
+    menu.Append(EDIT_MENU_PASTE, _("&Paste"))->Enable(CanPaste());
+    menu.AppendSeparator();
+    menu.Append(EDIT_MENU_SELECT_ALL, _("Select &All"));
+
+    // Split
+    if (context) {
+        menu.AppendSeparator();
+        menu.Append(EDIT_MENU_SPLIT_PRESERVE, _("Split at cursor (preserve times)"));
+        menu.Append(EDIT_MENU_SPLIT_ESTIMATE, _("Split at cursor (estimate times)"));
+        cmd::Command *split_video = cmd::get("edit/line/split/video");
+        menu.Append(EDIT_MENU_SPLIT_VIDEO, split_video->StrMenu(context))->Enable(split_video->Validate(context));
+    }
+
+    PopupMenu(&menu);
 }
 
-void SubsTextEditCtrl::OnDoubleClick(wxStyledTextEvent &evt) {
-	int pos = evt.GetPosition();
-	if (pos == -1 && !tokenized_line.empty()) {
-		auto tok = tokenized_line.back();
-		SetSelection(line_text.size() - tok.length, line_text.size());
-	}
-	else {
-		auto bounds = GetBoundsOfWordAtPosition(evt.GetPosition());
-		if (bounds.second != 0)
-			SetSelection(bounds.first, bounds.first + bounds.second);
-		else
-			evt.Skip();
-	}
+void SubsTextEditCtrl::OnDoubleClick(wxStyledTextEvent &evt)
+{
+    int pos = evt.GetPosition();
+    if (pos == -1 && !tokenized_line.empty()) {
+        auto tok = tokenized_line.back();
+        SetSelection(line_text.size() - tok.length, line_text.size());
+    }
+    else {
+        auto bounds = GetBoundsOfWordAtPosition(evt.GetPosition());
+        if (bounds.second != 0)
+            SetSelection(bounds.first, bounds.first + bounds.second);
+        else
+            evt.Skip();
+    }
 }
 
-void SubsTextEditCtrl::AddSpellCheckerEntries(wxMenu &menu) {
-	if (currentWord.empty()) return;
-
-	if (spellchecker->CanRemoveWord(currentWord))
-		menu.Append(EDIT_MENU_REMOVE_FROM_DICT, fmt_tl("Remove \"%s\" from dictionary", currentWord));
-
-	sugs = spellchecker->GetSuggestions(currentWord);
-	if (spellchecker->CheckWord(currentWord)) {
-		if (sugs.empty())
-			menu.Append(EDIT_MENU_SUGGESTION,_("No spell checker suggestions"))->Enable(false);
-		else {
-			auto subMenu = new wxMenu;
-			for (size_t i = 0; i < sugs.size(); ++i)
-				subMenu->Append(EDIT_MENU_SUGGESTIONS+i, to_wx(sugs[i]));
-
-			menu.Append(-1, fmt_tl("Spell checker suggestions for \"%s\"", currentWord), subMenu);
-		}
-	}
-	else {
-		if (sugs.empty())
-			menu.Append(EDIT_MENU_SUGGESTION,_("No correction suggestions"))->Enable(false);
-
-		for (size_t i = 0; i < sugs.size(); ++i)
-			menu.Append(EDIT_MENU_SUGGESTIONS+i, to_wx(sugs[i]));
-
-		// Append "add word"
-		menu.Append(EDIT_MENU_ADD_TO_DICT, fmt_tl("Add \"%s\" to dictionary", currentWord))->Enable(spellchecker->CanAddWord(currentWord));
-	}
+void SubsTextEditCtrl::AddSpellCheckerEntries(wxMenu &menu)
+{
+    if (currentWord.empty()) return;
+
+    if (spellchecker->CanRemoveWord(currentWord))
+        menu.Append(EDIT_MENU_REMOVE_FROM_DICT, fmt_tl("Remove \"%s\" from dictionary", currentWord));
+
+    sugs = spellchecker->GetSuggestions(currentWord);
+    if (spellchecker->CheckWord(currentWord)) {
+        if (sugs.empty())
+            menu.Append(EDIT_MENU_SUGGESTION, _("No spell checker suggestions"))->Enable(false);
+        else {
+            auto subMenu = new wxMenu;
+            for (size_t i = 0; i < sugs.size(); ++i)
+                subMenu->Append(EDIT_MENU_SUGGESTIONS + i, to_wx(sugs[i]));
+
+            menu.Append(-1, fmt_tl("Spell checker suggestions for \"%s\"", currentWord), subMenu);
+        }
+    }
+    else {
+        if (sugs.empty())
+            menu.Append(EDIT_MENU_SUGGESTION, _("No correction suggestions"))->Enable(false);
+
+        for (size_t i = 0; i < sugs.size(); ++i)
+            menu.Append(EDIT_MENU_SUGGESTIONS + i, to_wx(sugs[i]));
+
+        // Append "add word"
+        menu.Append(EDIT_MENU_ADD_TO_DICT, fmt_tl("Add \"%s\" to dictionary", currentWord))->Enable(spellchecker->CanAddWord(currentWord));
+    }
 }
 
-void SubsTextEditCtrl::AddThesaurusEntries(wxMenu &menu) {
-	if (currentWord.empty()) return;
-
-	auto results = thesaurus->Lookup(currentWord);
-
-	thesSugs.clear();
-
-	if (results.size()) {
-		auto thesMenu = new wxMenu;
-
-		int curThesEntry = 0;
-		for (auto const& result : results) {
-			// Single word, insert directly
-			if (result.second.empty()) {
-				thesMenu->Append(EDIT_MENU_THESAURUS_SUGS+curThesEntry, to_wx(result.first));
-				thesSugs.push_back(result.first);
-				++curThesEntry;
-			}
-			// Multiple, create submenu
-			else {
-				auto subMenu = new wxMenu;
-				for (auto const& sug : result.second) {
-					subMenu->Append(EDIT_MENU_THESAURUS_SUGS+curThesEntry, to_wx(sug));
-					thesSugs.push_back(sug);
-					++curThesEntry;
-				}
-
-				thesMenu->Append(-1, to_wx(result.first), subMenu);
-			}
-		}
-
-		menu.Append(-1, fmt_tl("Thesaurus suggestions for \"%s\"", currentWord), thesMenu);
-	}
-	else
-		menu.Append(EDIT_MENU_THESAURUS,_("No thesaurus suggestions"))->Enable(false);
-
-	// Append language list
-	menu.Append(-1,_("Thesaurus language"), GetLanguagesMenu(
-		EDIT_MENU_THES_LANGS,
-		to_wx(OPT_GET("Tool/Thesaurus/Language")->GetString()),
-		to_wx(thesaurus->GetLanguageList())));
-	menu.AppendSeparator();
+void SubsTextEditCtrl::AddThesaurusEntries(wxMenu &menu)
+{
+    if (currentWord.empty()) return;
+
+    auto results = thesaurus->Lookup(currentWord);
+
+    thesSugs.clear();
+
+    if (results.size()) {
+        auto thesMenu = new wxMenu;
+
+        int curThesEntry = 0;
+        for (auto const &result : results) {
+            // Single word, insert directly
+            if (result.second.empty()) {
+                thesMenu->Append(EDIT_MENU_THESAURUS_SUGS + curThesEntry, to_wx(result.first));
+                thesSugs.push_back(result.first);
+                ++curThesEntry;
+            }
+            // Multiple, create submenu
+            else {
+                auto subMenu = new wxMenu;
+                for (auto const &sug : result.second) {
+                    subMenu->Append(EDIT_MENU_THESAURUS_SUGS + curThesEntry, to_wx(sug));
+                    thesSugs.push_back(sug);
+                    ++curThesEntry;
+                }
+
+                thesMenu->Append(-1, to_wx(result.first), subMenu);
+            }
+        }
+
+        menu.Append(-1, fmt_tl("Thesaurus suggestions for \"%s\"", currentWord), thesMenu);
+    }
+    else
+        menu.Append(EDIT_MENU_THESAURUS, _("No thesaurus suggestions"))->Enable(false);
+
+    // Append language list
+    menu.Append(-1, _("Thesaurus language"), GetLanguagesMenu(
+                    EDIT_MENU_THES_LANGS,
+                    to_wx(OPT_GET("Tool/Thesaurus/Language")->GetString()),
+                    to_wx(thesaurus->GetLanguageList())));
+    menu.AppendSeparator();
 }
 
-wxMenu *SubsTextEditCtrl::GetLanguagesMenu(int base_id, wxString const& curLang, wxArrayString const& langs) {
-	auto languageMenu = new wxMenu;
-	languageMenu->AppendRadioItem(base_id, _("Disable"))->Check(curLang.empty());
+wxMenu *SubsTextEditCtrl::GetLanguagesMenu(int base_id, wxString const &curLang, wxArrayString const &langs)
+{
+    auto languageMenu = new wxMenu;
+    languageMenu->AppendRadioItem(base_id, _("Disable"))->Check(curLang.empty());
 
-	for (size_t i = 0; i < langs.size(); ++i)
-		languageMenu->AppendRadioItem(base_id + i + 1, LocalizedLanguageName(langs[i]))->Check(langs[i] == curLang);
+    for (size_t i = 0; i < langs.size(); ++i)
+        languageMenu->AppendRadioItem(base_id + i + 1, LocalizedLanguageName(langs[i]))->Check(langs[i] == curLang);
 
-	return languageMenu;
+    return languageMenu;
 }
 
-void SubsTextEditCtrl::OnUseSuggestion(wxCommandEvent &event) {
-	std::string suggestion;
-	int sugIdx = event.GetId() - EDIT_MENU_THESAURUS_SUGS;
-	if (sugIdx >= 0)
-		suggestion = thesSugs[sugIdx];
-	else
-		suggestion = sugs[event.GetId() - EDIT_MENU_SUGGESTIONS];
-
-	size_t pos;
-	while ((pos = suggestion.rfind('(')) != std::string::npos) {
-		// If there's only one suggestion for a word it'll be in the form "(noun) word",
-		// so we need to trim the "(noun) " part
-		if (pos == 0) {
-			pos = suggestion.find(')');
-			if (pos != std::string::npos) {
-				if (pos + 1< suggestion.size() && suggestion[pos + 1] == ' ') ++pos;
-				suggestion.erase(0, pos + 1);
-			}
-			break;
-		}
-
-		// Some replacements have notes about their usage after the word in the
-		// form "word (generic term)" that we need to remove (plus the leading space)
-		suggestion.resize(pos - 1);
-	}
-
-	// line_text needs to get cleared before SetTextRaw to ensure it gets reparsed
-	std::string new_text;
-	swap(line_text, new_text);
-	SetTextRaw(new_text.replace(currentWordPos.first, currentWordPos.second, suggestion).c_str());
-
-	SetSelection(currentWordPos.first, currentWordPos.first + suggestion.size());
-	SetFocus();
+void SubsTextEditCtrl::OnUseSuggestion(wxCommandEvent &event)
+{
+    std::string suggestion;
+    int sugIdx = event.GetId() - EDIT_MENU_THESAURUS_SUGS;
+    if (sugIdx >= 0)
+        suggestion = thesSugs[sugIdx];
+    else
+        suggestion = sugs[event.GetId() - EDIT_MENU_SUGGESTIONS];
+
+    size_t pos;
+    while ((pos = suggestion.rfind('(')) != std::string::npos) {
+        // If there's only one suggestion for a word it'll be in the form "(noun) word",
+        // so we need to trim the "(noun) " part
+        if (pos == 0) {
+            pos = suggestion.find(')');
+            if (pos != std::string::npos) {
+                if (pos + 1 < suggestion.size() && suggestion[pos + 1] == ' ') ++pos;
+                suggestion.erase(0, pos + 1);
+            }
+            break;
+        }
+
+        // Some replacements have notes about their usage after the word in the
+        // form "word (generic term)" that we need to remove (plus the leading space)
+        suggestion.resize(pos - 1);
+    }
+
+    // line_text needs to get cleared before SetTextRaw to ensure it gets reparsed
+    std::string new_text;
+    swap(line_text, new_text);
+    SetTextRaw(new_text.replace(currentWordPos.first, currentWordPos.second, suggestion).c_str());
+
+    SetSelection(currentWordPos.first, currentWordPos.first + suggestion.size());
+    SetFocus();
 }
 
-void SubsTextEditCtrl::OnSetDicLanguage(wxCommandEvent &event) {
-	std::vector<std::string> langs = spellchecker->GetLanguageList();
+void SubsTextEditCtrl::OnSetDicLanguage(wxCommandEvent &event)
+{
+    std::vector<std::string> langs = spellchecker->GetLanguageList();
 
-	int index = event.GetId() - EDIT_MENU_DIC_LANGS - 1;
-	std::string lang;
-	if (index >= 0)
-		lang = langs[index];
+    int index = event.GetId() - EDIT_MENU_DIC_LANGS - 1;
+    std::string lang;
+    if (index >= 0)
+        lang = langs[index];
 
-	OPT_SET("Tool/Spell Checker/Language")->SetString(lang);
+    OPT_SET("Tool/Spell Checker/Language")->SetString(lang);
 
-	UpdateStyle();
+    UpdateStyle();
 }
 
-void SubsTextEditCtrl::OnSetThesLanguage(wxCommandEvent &event) {
-	if (!thesaurus) return;
+void SubsTextEditCtrl::OnSetThesLanguage(wxCommandEvent &event)
+{
+    if (!thesaurus) return;
 
-	std::vector<std::string> langs = thesaurus->GetLanguageList();
+    std::vector<std::string> langs = thesaurus->GetLanguageList();
 
-	int index = event.GetId() - EDIT_MENU_THES_LANGS - 1;
-	std::string lang;
-	if (index >= 0) lang = langs[index];
-	OPT_SET("Tool/Thesaurus/Language")->SetString(lang);
+    int index = event.GetId() - EDIT_MENU_THES_LANGS - 1;
+    std::string lang;
+    if (index >= 0) lang = langs[index];
+    OPT_SET("Tool/Thesaurus/Language")->SetString(lang);
 
-	UpdateStyle();
+    UpdateStyle();
 }
 
-std::pair<int, int> SubsTextEditCtrl::GetBoundsOfWordAtPosition(int pos) {
-	int len = 0;
-	for (auto const& tok : tokenized_line) {
-		if (len + (int)tok.length > pos) {
-			if (tok.type == agi::ass::DialogueTokenType::WORD)
-				return {len, tok.length};
-			return {0, 0};
-		}
-		len += tok.length;
-	}
-
-	return {0, 0};
+std::pair<int, int> SubsTextEditCtrl::GetBoundsOfWordAtPosition(int pos)
+{
+    int len = 0;
+    for (auto const &tok : tokenized_line) {
+        if (len + (int)tok.length > pos) {
+            if (tok.type == agi::ass::DialogueTokenType::WORD)
+                return {len, tok.length};
+            return {0, 0};
+        }
+        len += tok.length;
+    }
+
+    return {0, 0};
 }
diff --git a/src/subs_edit_ctrl.h b/src/subs_edit_ctrl.h
index ccb38f4f85aa1fbc4185d9f43f5aef4030bf1ed5..69a6f5a81c802a45766d0db5bbd87e34fa14c1f5 100644
--- a/src/subs_edit_ctrl.h
+++ b/src/subs_edit_ctrl.h
@@ -34,89 +34,89 @@
 
 class Thesaurus;
 namespace agi {
-	class SpellChecker;
-	struct Context;
-	namespace ass { struct DialogueToken; }
+class SpellChecker;
+struct Context;
+namespace ass { struct DialogueToken; }
 }
 
 /// @class SubsTextEditCtrl
 /// @brief A Scintilla control with spell checking and syntax highlighting
 class SubsTextEditCtrl final : public wxStyledTextCtrl {
-	/// Backend spellchecker to use
-	std::unique_ptr<agi::SpellChecker> spellchecker;
+    /// Backend spellchecker to use
+    std::unique_ptr<agi::SpellChecker> spellchecker;
 
-	/// Backend thesaurus to use
-	std::unique_ptr<Thesaurus> thesaurus;
+    /// Backend thesaurus to use
+    std::unique_ptr<Thesaurus> thesaurus;
 
-	/// Project context, for splitting lines
-	agi::Context *context;
+    /// Project context, for splitting lines
+    agi::Context *context;
 
-	/// The word right-clicked on, used for spellchecker replacing
-	std::string currentWord;
+    /// The word right-clicked on, used for spellchecker replacing
+    std::string currentWord;
 
-	/// The beginning of the word right-clicked on, for spellchecker replacing
-	std::pair<int, int> currentWordPos;
+    /// The beginning of the word right-clicked on, for spellchecker replacing
+    std::pair<int, int> currentWordPos;
 
-	/// Spellchecker suggestions for the last right-clicked word
-	std::vector<std::string> sugs;
+    /// Spellchecker suggestions for the last right-clicked word
+    std::vector<std::string> sugs;
 
-	/// Thesaurus suggestions for the last right-clicked word
-	std::vector<std::string> thesSugs;
+    /// Thesaurus suggestions for the last right-clicked word
+    std::vector<std::string> thesSugs;
 
-	/// Text of the currently shown calltip, to avoid flickering from
-	/// pointlessly reshowing the current tip
-	std::string calltip_text;
+    /// Text of the currently shown calltip, to avoid flickering from
+    /// pointlessly reshowing the current tip
+    std::string calltip_text;
 
-	/// Position of the currently show calltip
-	size_t calltip_position = 0;
+    /// Position of the currently show calltip
+    size_t calltip_position = 0;
 
-	/// Cursor position which the current calltip is for
-	int cursor_pos;
+    /// Cursor position which the current calltip is for
+    int cursor_pos;
 
-	/// The last seen line text, used to avoid reparsing the line for syntax
-	/// highlighting when possible
-	std::string line_text;
+    /// The last seen line text, used to avoid reparsing the line for syntax
+    /// highlighting when possible
+    std::string line_text;
 
-	/// Tokenized version of line_text
-	std::vector<agi::ass::DialogueToken> tokenized_line;
+    /// Tokenized version of line_text
+    std::vector<agi::ass::DialogueToken> tokenized_line;
 
-	void OnContextMenu(wxContextMenuEvent &);
-	void OnDoubleClick(wxStyledTextEvent&);
-	void OnUseSuggestion(wxCommandEvent &event);
-	void OnSetDicLanguage(wxCommandEvent &event);
-	void OnSetThesLanguage(wxCommandEvent &event);
-	void OnLoseFocus(wxFocusEvent &event);
-	void OnKeyDown(wxKeyEvent &event);
+    void OnContextMenu(wxContextMenuEvent &);
+    void OnDoubleClick(wxStyledTextEvent &);
+    void OnUseSuggestion(wxCommandEvent &event);
+    void OnSetDicLanguage(wxCommandEvent &event);
+    void OnSetThesLanguage(wxCommandEvent &event);
+    void OnLoseFocus(wxFocusEvent &event);
+    void OnKeyDown(wxKeyEvent &event);
 
-	void SetSyntaxStyle(int id, wxFont &font, std::string const& name, wxColor const& default_background);
-	void Subscribe(std::string const& name);
+    void SetSyntaxStyle(int id, wxFont &font, std::string const &name, wxColor const &default_background);
+    void Subscribe(std::string const &name);
 
-	void StyleSpellCheck();
-	void UpdateCallTip();
-	void SetStyles();
+    void StyleSpellCheck();
+    void UpdateCallTip();
+    void SetStyles();
 
-	void UpdateStyle();
+    void UpdateStyle();
 
-	/// Add the thesaurus suggestions to a menu
-	void AddThesaurusEntries(wxMenu &menu);
+    /// Add the thesaurus suggestions to a menu
+    void AddThesaurusEntries(wxMenu &menu);
 
-	/// Add the spell checker suggestions to a menu
-	void AddSpellCheckerEntries(wxMenu &menu);
+    /// Add the spell checker suggestions to a menu
+    void AddSpellCheckerEntries(wxMenu &menu);
 
-	/// Generate a languages submenu from a list of locales and a current language
-	/// @param base_id ID to use for the first menu item
-	/// @param curLang Currently selected language
-	/// @param lang Full list of languages
-	wxMenu *GetLanguagesMenu(int base_id, wxString const& curLang, wxArrayString const& langs);
+    /// Generate a languages submenu from a list of locales and a current language
+    /// @param base_id ID to use for the first menu item
+    /// @param curLang Currently selected language
+    /// @param lang Full list of languages
+    wxMenu *GetLanguagesMenu(int base_id, wxString const &curLang, wxArrayString const &langs);
 
 public:
-	SubsTextEditCtrl(wxWindow* parent, wxSize size, long style, agi::Context *context);
-	~SubsTextEditCtrl();
+    SubsTextEditCtrl(wxWindow *parent, wxSize size, long style, agi::Context *context);
+    ~SubsTextEditCtrl();
 
-	void SetTextTo(std::string const& text);
-	void Paste() override;
+    void SetTextTo(std::string const &text);
+    void Paste() override;
 
-	std::pair<int, int> GetBoundsOfWordAtPosition(int pos);
+    std::pair<int, int> GetBoundsOfWordAtPosition(int pos);
 
-	DECLARE_EVENT_TABLE()
+    DECLARE_EVENT_TABLE()
 };
diff --git a/src/subs_preview.cpp b/src/subs_preview.cpp
index ba24ad1de8cc1890eb8b048bc000321116b995cb..1b4f12d91dcd668e713376cb5c712eac8caf8479 100644
--- a/src/subs_preview.cpp
+++ b/src/subs_preview.cpp
@@ -47,106 +47,113 @@
 #include <wx/msgdlg.h>
 
 SubtitlesPreview::SubtitlesPreview(wxWindow *parent, wxSize size, int winStyle, agi::Color col)
-: wxWindow(parent, -1, wxDefaultPosition, size, winStyle)
-, style(new AssStyle)
-, back_color(col)
-, sub_file(agi::make_unique<AssFile>())
-, line(new AssDialogue)
+    : wxWindow(parent, -1, wxDefaultPosition, size, winStyle)
+    , style(new AssStyle)
+    , back_color(col)
+    , sub_file(agi::make_unique<AssFile>())
+    , line(new AssDialogue)
 {
-	line->Text = "{\\q2}preview";
+    line->Text = "{\\q2}preview";
 
-	SetStyle(*style);
+    SetStyle(*style);
 
-	sub_file->LoadDefault();
-	sub_file->Styles.push_back(*style);
-	sub_file->Events.push_back(*line);
+    sub_file->LoadDefault();
+    sub_file->Styles.push_back(*style);
+    sub_file->Events.push_back(*line);
 
-	SetSizeHints(size.GetWidth(), size.GetHeight(), -1, -1);
-	wxSizeEvent evt(size);
-	OnSize(evt);
-	UpdateBitmap();
+    SetSizeHints(size.GetWidth(), size.GetHeight(), -1, -1);
+    wxSizeEvent evt(size);
+    OnSize(evt);
+    UpdateBitmap();
 
-	Bind(wxEVT_PAINT, &SubtitlesPreview::OnPaint, this);
-	Bind(wxEVT_SIZE, &SubtitlesPreview::OnSize, this);
+    Bind(wxEVT_PAINT, &SubtitlesPreview::OnPaint, this);
+    Bind(wxEVT_SIZE, &SubtitlesPreview::OnSize, this);
 }
 
-SubtitlesPreview::~SubtitlesPreview() {
+SubtitlesPreview::~SubtitlesPreview()
+{
 }
 
-void SubtitlesPreview::SetStyle(AssStyle const& new_style) {
-	if (provider && style->font != new_style.font)
-		provider->Reinitialize();
-
-	*style = new_style;
-	style->name = "Default";
-	style->alignment = 5;
-	std::fill(style->Margin.begin(), style->Margin.end(), 0);
-	style->UpdateData();
-	UpdateBitmap();
+void SubtitlesPreview::SetStyle(AssStyle const &new_style)
+{
+    if (provider && style->font != new_style.font)
+        provider->Reinitialize();
+
+    *style = new_style;
+    style->name = "Default";
+    style->alignment = 5;
+    std::fill(style->Margin.begin(), style->Margin.end(), 0);
+    style->UpdateData();
+    UpdateBitmap();
 }
 
-void SubtitlesPreview::SetText(std::string const& text) {
-	std::string new_text = "{\\q2}" + text;
-	if (new_text != line->Text) {
-		line->Text = new_text;
-		UpdateBitmap();
-	}
+void SubtitlesPreview::SetText(std::string const &text)
+{
+    std::string new_text = "{\\q2}" + text;
+    if (new_text != line->Text) {
+        line->Text = new_text;
+        UpdateBitmap();
+    }
 }
 
-void SubtitlesPreview::SetColour(agi::Color col) {
-	if (col != back_color) {
-		back_color = col;
-		vid = agi::make_unique<DummyVideoProvider>(0.0, 10, bmp->GetWidth(), bmp->GetHeight(), back_color, true);
-		UpdateBitmap();
-	}
+void SubtitlesPreview::SetColour(agi::Color col)
+{
+    if (col != back_color) {
+        back_color = col;
+        vid = agi::make_unique<DummyVideoProvider>(0.0, 10, bmp->GetWidth(), bmp->GetHeight(), back_color, true);
+        UpdateBitmap();
+    }
 }
 
-void SubtitlesPreview::UpdateBitmap() {
-	if (!vid) return;
-
-	VideoFrame frame;
-	vid->GetFrame(0, frame);
-
-	if (provider) {
-		try {
-			provider->LoadSubtitles(sub_file.get());
-			provider->DrawSubtitles(frame, 0.1);
-		}
-		catch (...) { }
-	}
-
-	// Convert frame to bitmap
-	*bmp = static_cast<wxBitmap>(GetImage(frame));
-	Refresh();
+void SubtitlesPreview::UpdateBitmap()
+{
+    if (!vid) return;
+
+    VideoFrame frame;
+    vid->GetFrame(0, frame);
+
+    if (provider) {
+        try {
+            provider->LoadSubtitles(sub_file.get());
+            provider->DrawSubtitles(frame, 0.1);
+        }
+        catch (...) { }
+    }
+
+    // Convert frame to bitmap
+    *bmp = static_cast<wxBitmap>(GetImage(frame));
+    Refresh();
 }
 
-void SubtitlesPreview::OnPaint(wxPaintEvent &) {
-	wxPaintDC(this).DrawBitmap(*bmp, 0, 0);
+void SubtitlesPreview::OnPaint(wxPaintEvent &)
+{
+    wxPaintDC(this).DrawBitmap(*bmp, 0, 0);
 }
 
-void SubtitlesPreview::OnSize(wxSizeEvent &evt) {
-	if (bmp.get() && evt.GetSize() == bmp->GetSize()) return;
-
-	int w = evt.GetSize().GetWidth();
-	int h = evt.GetSize().GetHeight();
-
-	bmp = agi::make_unique<wxBitmap>(w, h, -1);
-	vid = agi::make_unique<DummyVideoProvider>(0.0, 10, w, h, back_color, true);
-	try {
-		if (!progress)
-			progress = agi::make_unique<DialogProgress>(this);
-		if (!provider)
-			provider = SubtitlesProviderFactory::GetProvider(progress.get());
-	}
-	catch (...) {
-		wxMessageBox(
-			"Could not get any subtitles provider for the preview box. Make "
-			"sure that you have a provider installed.",
-			"No subtitles provider", wxOK | wxICON_ERROR | wxCENTER);
-	}
-
-	sub_file->SetScriptInfo("PlayResX", std::to_string(w));
-	sub_file->SetScriptInfo("PlayResY", std::to_string(h));
-
-	UpdateBitmap();
+void SubtitlesPreview::OnSize(wxSizeEvent &evt)
+{
+    if (bmp.get() && evt.GetSize() == bmp->GetSize()) return;
+
+    int w = evt.GetSize().GetWidth();
+    int h = evt.GetSize().GetHeight();
+
+    bmp = agi::make_unique<wxBitmap>(w, h, -1);
+    vid = agi::make_unique<DummyVideoProvider>(0.0, 10, w, h, back_color, true);
+    try {
+        if (!progress)
+            progress = agi::make_unique<DialogProgress>(this);
+        if (!provider)
+            provider = SubtitlesProviderFactory::GetProvider(progress.get());
+    }
+    catch (...) {
+        wxMessageBox(
+            "Could not get any subtitles provider for the preview box. Make "
+            "sure that you have a provider installed.",
+            "No subtitles provider", wxOK | wxICON_ERROR | wxCENTER);
+    }
+
+    sub_file->SetScriptInfo("PlayResX", std::to_string(w));
+    sub_file->SetScriptInfo("PlayResY", std::to_string(h));
+
+    UpdateBitmap();
 }
diff --git a/src/subs_preview.h b/src/subs_preview.h
index 33cbc4672eeeb76aaedd795d36cd1bc4a345201c..0f48906e24d0decccc952a82757d37c999b6a2f6 100644
--- a/src/subs_preview.h
+++ b/src/subs_preview.h
@@ -44,38 +44,38 @@ class VideoProvider;
 
 /// Preview window to show a short string with a given ass style
 class SubtitlesPreview final : public wxWindow {
-	/// The subtitle provider used to render the string
-	std::unique_ptr<SubtitlesProvider> provider;
-	/// Bitmap to render into
-	std::unique_ptr<wxBitmap> bmp;
-	/// The currently display style
-	AssStyle* style;
-	/// Video provider to render into
-	std::unique_ptr<VideoProvider> vid;
-	/// Current background color
-	agi::Color back_color;
-	/// Subtitle file containing the style and displayed line
-	std::unique_ptr<AssFile> sub_file;
-	/// Line used to render the specified text
-	AssDialogue* line;
+    /// The subtitle provider used to render the string
+    std::unique_ptr<SubtitlesProvider> provider;
+    /// Bitmap to render into
+    std::unique_ptr<wxBitmap> bmp;
+    /// The currently display style
+    AssStyle *style;
+    /// Video provider to render into
+    std::unique_ptr<VideoProvider> vid;
+    /// Current background color
+    agi::Color back_color;
+    /// Subtitle file containing the style and displayed line
+    std::unique_ptr<AssFile> sub_file;
+    /// Line used to render the specified text
+    AssDialogue *line;
 
-	std::unique_ptr<DialogProgress> progress;
+    std::unique_ptr<DialogProgress> progress;
 
-	/// Regenerate the bitmap
-	void UpdateBitmap();
-	/// Resize event handler
-	void OnSize(wxSizeEvent &event);
-	/// Paint event handler
-	void OnPaint(wxPaintEvent &);
+    /// Regenerate the bitmap
+    void UpdateBitmap();
+    /// Resize event handler
+    void OnSize(wxSizeEvent &event);
+    /// Paint event handler
+    void OnPaint(wxPaintEvent &);
 
 public:
-	/// Set the style to use
-	void SetStyle(AssStyle const& style);
-	/// Set the text to display
-	void SetText(std::string const& text);
-	/// Set the background color
-	void SetColour(agi::Color col);
+    /// Set the style to use
+    void SetStyle(AssStyle const &style);
+    /// Set the text to display
+    void SetText(std::string const &text);
+    /// Set the background color
+    void SetColour(agi::Color col);
 
-	SubtitlesPreview(wxWindow *parent, wxSize size, int style, agi::Color colour);
-	~SubtitlesPreview();
+    SubtitlesPreview(wxWindow *parent, wxSize size, int style, agi::Color colour);
+    ~SubtitlesPreview();
 };
diff --git a/src/subtitle_format.cpp b/src/subtitle_format.cpp
index 6f8f0c341b98a514c48a73ed4d02afc3370ff1a2..6e59a740a5b6693a2979f93046a1169a0c4a502a 100644
--- a/src/subtitle_format.cpp
+++ b/src/subtitle_format.cpp
@@ -59,258 +59,272 @@
 #include <wx/choicdlg.h>
 
 namespace {
-	std::vector<std::unique_ptr<SubtitleFormat>> formats;
+std::vector<std::unique_ptr<SubtitleFormat>> formats;
 }
 
 SubtitleFormat::SubtitleFormat(std::string name)
-: name(std::move(name))
+    : name(std::move(name))
 {
 }
 
-bool SubtitleFormat::CanReadFile(agi::fs::path const& filename, std::string const&) const {
-	auto wildcards = GetReadWildcards();
-	return any_of(begin(wildcards), end(wildcards),
-		[&](std::string const& ext) { return agi::fs::HasExtension(filename, ext); });
+bool SubtitleFormat::CanReadFile(agi::fs::path const &filename, std::string const &) const
+{
+    auto wildcards = GetReadWildcards();
+    return any_of(begin(wildcards), end(wildcards),
+    [&](std::string const & ext) { return agi::fs::HasExtension(filename, ext); });
 }
 
-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::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 {
-	if (!subs->Attachments.empty())
-		return false;
+bool SubtitleFormat::CanSave(const AssFile *subs) const
+{
+    if (!subs->Attachments.empty())
+        return false;
 
-	auto def = boost::flyweight<std::string>("Default");
-	for (auto const& line : subs->Events) {
-		if (line.Style != def || line.GetStrippedText() != line.Text)
-			return false;
-	}
+    auto def = boost::flyweight<std::string>("Default");
+    for (auto const &line : subs->Events) {
+        if (line.Style != def || line.GetStrippedText() != line.Text)
+            return false;
+    }
 
-	return true;
+    return true;
 }
 
-agi::vfr::Framerate SubtitleFormat::AskForFPS(bool allow_vfr, bool show_smpte, agi::vfr::Framerate const& fps) {
-	wxArrayString choices;
-
-	bool vidLoaded = false;
-	if (fps.IsLoaded()) {
-		vidLoaded = true;
-		if (!fps.IsVFR())
-			choices.Add(fmt_tl("From video (%g)", fps.FPS()));
-		else if (allow_vfr)
-			choices.Add(_("From video (VFR)"));
-		else
-			vidLoaded = false;
-	}
-
-	// Standard FPS values
-	choices.Add(_("15.000 FPS"));
-	choices.Add(_("23.976 FPS (Decimated NTSC)"));
-	choices.Add(_("24.000 FPS (FILM)"));
-	choices.Add(_("25.000 FPS (PAL)"));
-	choices.Add(_("29.970 FPS (NTSC)"));
-	if (show_smpte)
-		choices.Add(_("29.970 FPS (NTSC with SMPTE dropframe)"));
-	choices.Add(_("30.000 FPS"));
-	choices.Add(_("50.000 FPS (PAL x2)"));
-	choices.Add(_("59.940 FPS (NTSC x2)"));
-	choices.Add(_("60.000 FPS"));
-	choices.Add(_("119.880 FPS (NTSC x4)"));
-	choices.Add(_("120.000 FPS"));
-
-	bool was_busy = wxIsBusy();
-	if (was_busy) wxEndBusyCursor();
-	int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"), _("FPS"), choices);
-	if (was_busy) wxBeginBusyCursor();
-
-	using agi::vfr::Framerate;
-	if (choice == -1)
-		return Framerate();
-
-	// Get FPS from choice
-	if (vidLoaded)
-		--choice;
-	if (!show_smpte && choice > 4)
-		--choice;
-
-	switch (choice) {
-		case -1: return fps;                     break;
-		case 0:  return Framerate(15, 1);        break;
-		case 1:  return Framerate(24000, 1001);  break;
-		case 2:  return Framerate(24, 1);        break;
-		case 3:  return Framerate(25, 1);        break;
-		case 4:  return Framerate(30000, 1001);  break;
-		case 5:  return Framerate(30000, 1001, true); break;
-		case 6:  return Framerate(30, 1);        break;
-		case 7:  return Framerate(50, 1);        break;
-		case 8:  return Framerate(60000, 1001);  break;
-		case 9:  return Framerate(60, 1);        break;
-		case 10: return Framerate(120000, 1001); break;
-		case 11: return Framerate(120, 1);       break;
-	}
-	throw agi::InternalError("Out of bounds result from wxGetSingleChoiceIndex?");
+agi::vfr::Framerate SubtitleFormat::AskForFPS(bool allow_vfr, bool show_smpte, agi::vfr::Framerate const &fps)
+{
+    wxArrayString choices;
+
+    bool vidLoaded = false;
+    if (fps.IsLoaded()) {
+        vidLoaded = true;
+        if (!fps.IsVFR())
+            choices.Add(fmt_tl("From video (%g)", fps.FPS()));
+        else if (allow_vfr)
+            choices.Add(_("From video (VFR)"));
+        else
+            vidLoaded = false;
+    }
+
+    // Standard FPS values
+    choices.Add(_("15.000 FPS"));
+    choices.Add(_("23.976 FPS (Decimated NTSC)"));
+    choices.Add(_("24.000 FPS (FILM)"));
+    choices.Add(_("25.000 FPS (PAL)"));
+    choices.Add(_("29.970 FPS (NTSC)"));
+    if (show_smpte)
+        choices.Add(_("29.970 FPS (NTSC with SMPTE dropframe)"));
+    choices.Add(_("30.000 FPS"));
+    choices.Add(_("50.000 FPS (PAL x2)"));
+    choices.Add(_("59.940 FPS (NTSC x2)"));
+    choices.Add(_("60.000 FPS"));
+    choices.Add(_("119.880 FPS (NTSC x4)"));
+    choices.Add(_("120.000 FPS"));
+
+    bool was_busy = wxIsBusy();
+    if (was_busy) wxEndBusyCursor();
+    int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"), _("FPS"), choices);
+    if (was_busy) wxBeginBusyCursor();
+
+    using agi::vfr::Framerate;
+    if (choice == -1)
+        return Framerate();
+
+    // Get FPS from choice
+    if (vidLoaded)
+        --choice;
+    if (!show_smpte && choice > 4)
+        --choice;
+
+    switch (choice) {
+    case -1: return fps;                     break;
+    case 0:  return Framerate(15, 1);        break;
+    case 1:  return Framerate(24000, 1001);  break;
+    case 2:  return Framerate(24, 1);        break;
+    case 3:  return Framerate(25, 1);        break;
+    case 4:  return Framerate(30000, 1001);  break;
+    case 5:  return Framerate(30000, 1001, true); break;
+    case 6:  return Framerate(30, 1);        break;
+    case 7:  return Framerate(50, 1);        break;
+    case 8:  return Framerate(60000, 1001);  break;
+    case 9:  return Framerate(60, 1);        break;
+    case 10: return Framerate(120000, 1001); break;
+    case 11: return Framerate(120, 1);       break;
+    }
+    throw agi::InternalError("Out of bounds result from wxGetSingleChoiceIndex?");
 }
 
-void SubtitleFormat::StripTags(AssFile &file) {
-	for (auto& current : file.Events)
-		current.StripTags();
+void SubtitleFormat::StripTags(AssFile &file)
+{
+    for (auto &current : file.Events)
+        current.StripTags();
 }
 
-void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const& newline, bool mergeLineBreaks) {
-	for (auto& current : file.Events) {
-		std::string repl = current.Text;
-		boost::replace_all(repl, "\\h", " ");
-		boost::ireplace_all(repl, "\\n", newline);
-		if (mergeLineBreaks) {
-			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;
-	}
+void SubtitleFormat::ConvertNewlines(AssFile &file, std::string const &newline, bool mergeLineBreaks)
+{
+    for (auto &current : file.Events) {
+        std::string repl = current.Text;
+        boost::replace_all(repl, "\\h", " ");
+        boost::ireplace_all(repl, "\\n", newline);
+        if (mergeLineBreaks) {
+            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;
+    }
 }
 
-void SubtitleFormat::StripComments(AssFile &file) {
-	file.Events.remove_and_dispose_if([](AssDialogue const& diag) {
-		return diag.Comment || diag.Text.get().empty();
-	}, [](AssDialogue *e) { delete e; });
+void SubtitleFormat::StripComments(AssFile &file)
+{
+    file.Events.remove_and_dispose_if([](AssDialogue const & diag) {
+        return diag.Comment || diag.Text.get().empty();
+    }, [](AssDialogue * e) { delete e; });
 }
 
 /// @brief Split and merge lines so there are no overlapping lines
 ///
 /// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
-void SubtitleFormat::RecombineOverlaps(AssFile &file) {
-	auto cur = file.Events.begin();
-	for (auto next = std::next(cur); next != file.Events.end(); cur = std::prev(next)) {
-		if (next == file.Events.begin() || cur->End <= next->Start) {
-			++next;
-			continue;
-		}
-
-		std::unique_ptr<AssDialogue> prevdlg(&*cur);
-		std::unique_ptr<AssDialogue> curdlg(&*next);
-		++next;
-
-		auto insert_line = [&](AssDialogue *newdlg) {
-			file.Events.insert(std::find_if(next, file.Events.end(), [&](AssDialogue const& pos) {
-				return pos.Start >= newdlg->Start;
-			}), *newdlg);
-		};
-
-		//Is there an A part before the overlap?
-		if (curdlg->Start > prevdlg->Start) {
-			// Produce new entry with correct values
-			auto newdlg = new AssDialogue(*prevdlg);
-			newdlg->Start = prevdlg->Start;
-			newdlg->End = curdlg->Start;
-			newdlg->Text = prevdlg->Text;
-			insert_line(newdlg);
-		}
-
-		// Overlapping A+B part
-		{
-			auto newdlg = new AssDialogue(*prevdlg);
-			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.get() + "\\N" + prevdlg->Text.get();
-			insert_line(newdlg);
-		}
-
-		// Is there an A part after the overlap?
-		if (prevdlg->End > curdlg->End) {
-			// Produce new entry with correct values
-			auto newdlg = new AssDialogue(*prevdlg);
-			newdlg->Start = curdlg->End;
-			newdlg->End = prevdlg->End;
-			newdlg->Text = prevdlg->Text;
-			insert_line(newdlg);
-		}
-
-		// Is there a B part after the overlap?
-		if (curdlg->End > prevdlg->End) {
-			// Produce new entry with correct values
-			auto newdlg = new AssDialogue(*prevdlg);
-			newdlg->Start = prevdlg->End;
-			newdlg->End = curdlg->End;
-			newdlg->Text = curdlg->Text;
-			insert_line(newdlg);
-		}
-	}
+void SubtitleFormat::RecombineOverlaps(AssFile &file)
+{
+    auto cur = file.Events.begin();
+    for (auto next = std::next(cur); next != file.Events.end(); cur = std::prev(next)) {
+        if (next == file.Events.begin() || cur->End <= next->Start) {
+            ++next;
+            continue;
+        }
+
+        std::unique_ptr<AssDialogue> prevdlg(&*cur);
+        std::unique_ptr<AssDialogue> curdlg(&*next);
+        ++next;
+
+        auto insert_line = [&](AssDialogue * newdlg) {
+            file.Events.insert(std::find_if(next, file.Events.end(), [&](AssDialogue const & pos) {
+                return pos.Start >= newdlg->Start;
+            }), *newdlg);
+        };
+
+        //Is there an A part before the overlap?
+        if (curdlg->Start > prevdlg->Start) {
+            // Produce new entry with correct values
+            auto newdlg = new AssDialogue(*prevdlg);
+            newdlg->Start = prevdlg->Start;
+            newdlg->End = curdlg->Start;
+            newdlg->Text = prevdlg->Text;
+            insert_line(newdlg);
+        }
+
+        // Overlapping A+B part
+        {
+            auto newdlg = new AssDialogue(*prevdlg);
+            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.get() + "\\N" + prevdlg->Text.get();
+            insert_line(newdlg);
+        }
+
+        // Is there an A part after the overlap?
+        if (prevdlg->End > curdlg->End) {
+            // Produce new entry with correct values
+            auto newdlg = new AssDialogue(*prevdlg);
+            newdlg->Start = curdlg->End;
+            newdlg->End = prevdlg->End;
+            newdlg->Text = prevdlg->Text;
+            insert_line(newdlg);
+        }
+
+        // Is there a B part after the overlap?
+        if (curdlg->End > prevdlg->End) {
+            // Produce new entry with correct values
+            auto newdlg = new AssDialogue(*prevdlg);
+            newdlg->Start = prevdlg->End;
+            newdlg->End = curdlg->End;
+            newdlg->Text = curdlg->Text;
+            insert_line(newdlg);
+        }
+    }
 }
 
 /// @brief Merge identical lines that follow each other
-void SubtitleFormat::MergeIdentical(AssFile &file) {
-	auto next = file.Events.begin();
-	auto cur = next++;
-
-	for (; next != file.Events.end(); cur = next++) {
-		if (cur->End == next->Start && cur->Text == next->Text) {
-			// Merge timing
-			next->Start = std::min(next->Start, cur->Start);
-			next->End = std::max(next->End, cur->End);
-
-			// Remove duplicate line
-			delete &*cur;
-		}
-	}
+void SubtitleFormat::MergeIdentical(AssFile &file)
+{
+    auto next = file.Events.begin();
+    auto cur = next++;
+
+    for (; next != file.Events.end(); cur = next++) {
+        if (cur->End == next->Start && cur->Text == next->Text) {
+            // Merge timing
+            next->Start = std::min(next->Start, cur->Start);
+            next->End = std::max(next->End, cur->End);
+
+            // Remove duplicate line
+            delete &*cur;
+        }
+    }
 }
 
-void SubtitleFormat::LoadFormats() {
-	if (formats.empty()) {
-		formats.emplace_back(agi::make_unique<AssSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<Ebu3264SubtitleFormat>());
-		formats.emplace_back(agi::make_unique<EncoreSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<MKVSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<MicroDVDSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<SRTSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<SsaSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<TTXTSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<TXTSubtitleFormat>());
-		formats.emplace_back(agi::make_unique<TranStationSubtitleFormat>());
-	}
+void SubtitleFormat::LoadFormats()
+{
+    if (formats.empty()) {
+        formats.emplace_back(agi::make_unique<AssSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<Ebu3264SubtitleFormat>());
+        formats.emplace_back(agi::make_unique<EncoreSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<MKVSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<MicroDVDSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<SRTSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<SsaSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<TTXTSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<TXTSubtitleFormat>());
+        formats.emplace_back(agi::make_unique<TranStationSubtitleFormat>());
+    }
 }
 
 template<class Cont, class Pred>
-SubtitleFormat *find_or_throw(Cont &container, Pred pred) {
-	auto it = find_if(container.begin(), container.end(), pred);
-	if (it == container.end())
-		throw UnknownSubtitleFormatError("Subtitle format for extension not found");
-	return it->get();
+SubtitleFormat *find_or_throw(Cont &container, Pred pred)
+{
+    auto it = find_if(container.begin(), container.end(), pred);
+    if (it == container.end())
+        throw UnknownSubtitleFormatError("Subtitle format for extension not found");
+    return it->get();
 }
 
-const SubtitleFormat *SubtitleFormat::GetReader(agi::fs::path const& filename, std::string const& encoding) {
-	LoadFormats();
-	return find_or_throw(formats, [&](std::unique_ptr<SubtitleFormat> const& f) {
-		return f->CanReadFile(filename, encoding);
-	});
+const SubtitleFormat *SubtitleFormat::GetReader(agi::fs::path const &filename, std::string const &encoding)
+{
+    LoadFormats();
+    return find_or_throw(formats, [&](std::unique_ptr<SubtitleFormat> const & f) {
+        return f->CanReadFile(filename, encoding);
+    });
 }
 
-const SubtitleFormat *SubtitleFormat::GetWriter(agi::fs::path const& filename) {
-	LoadFormats();
-	return find_or_throw(formats, [&](std::unique_ptr<SubtitleFormat> const& f) {
-		return f->CanWriteFile(filename);
-	});
+const SubtitleFormat *SubtitleFormat::GetWriter(agi::fs::path const &filename)
+{
+    LoadFormats();
+    return find_or_throw(formats, [&](std::unique_ptr<SubtitleFormat> const & f) {
+        return f->CanWriteFile(filename);
+    });
 }
 
-std::string SubtitleFormat::GetWildcards(int mode) {
-	LoadFormats();
+std::string SubtitleFormat::GetWildcards(int mode)
+{
+    LoadFormats();
 
-	std::vector<std::string> all;
-	std::string final;
+    std::vector<std::string> all;
+    std::string final;
 
-	for (auto const& format : formats) {
-		auto cur = mode == 0 ? format->GetReadWildcards() : format->GetWriteWildcards();
-		if (cur.empty()) continue;
+    for (auto const &format : formats) {
+        auto cur = mode == 0 ? format->GetReadWildcards() : format->GetWriteWildcards();
+        if (cur.empty()) continue;
 
-		for (auto& str : cur) str.insert(0, "*.");
-		all.insert(all.end(), begin(cur), end(cur));
-		final += "|" + format->GetName() + " (" + boost::join(cur, ",") + ")|" + boost::join(cur, ";");
-	}
+        for (auto &str : cur) str.insert(0, "*.");
+        all.insert(all.end(), begin(cur), end(cur));
+        final += "|" + format->GetName() + " (" + boost::join(cur, ",") + ")|" + boost::join(cur, ";");
+    }
 
-	return from_wx(_("All Supported Formats")) + " (" + boost::join(all, ",") + ")|" + boost::join(all, ";") + final;
+    return from_wx(_("All Supported Formats")) + " (" + boost::join(all, ",") + ")|" + boost::join(all, ";") + final;
 }
diff --git a/src/subtitle_format.h b/src/subtitle_format.h
index c02e90af01efa1c65a9c14f855f34328ea6d467e..b26f94d3da12c923e902664102a955d7e81d6d9e 100644
--- a/src/subtitle_format.h
+++ b/src/subtitle_format.h
@@ -39,92 +39,92 @@ class AssFile;
 namespace agi { namespace vfr { class Framerate; } }
 
 class SubtitleFormat {
-	std::string name;
+    std::string name;
 
-	/// Get this format's wildcards for a load dialog
-	virtual std::vector<std::string> GetReadWildcards() const { return {}; }
-	/// Get this format's wildcards for a save dialog
-	virtual std::vector<std::string> GetWriteWildcards() const { return {}; }
+    /// Get this format's wildcards for a load dialog
+    virtual std::vector<std::string> GetReadWildcards() const { return {}; }
+    /// Get this format's wildcards for a save dialog
+    virtual std::vector<std::string> GetWriteWildcards() const { return {}; }
 
 public:
-	/// Strip override tags
-	static void StripTags(AssFile &file);
-	/// 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, std::string const& newline, bool mergeLineBreaks = true);
-	/// Remove All commented and empty lines
-	static void StripComments(AssFile &file);
-	/// @brief Split and merge lines so there are no overlapping lines
-	///
-	/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
-	static void RecombineOverlaps(AssFile &file);
-	/// Merge sequential identical lines
-	static void MergeIdentical(AssFile &file);
-
-	/// Prompt the user for a frame rate to use
-	/// @param allow_vfr Include video frame rate as an option even if it's vfr
-	/// @param show_smpte Show SMPTE drop frame option
-	static agi::vfr::Framerate AskForFPS(bool allow_vfr, bool show_smpte, agi::vfr::Framerate const& fps);
-
-	/// Constructor
-	/// @param Subtitle format name
-	SubtitleFormat(std::string name);
-	/// Destructor
-	virtual ~SubtitleFormat() = default;
-
-	/// Get this format's 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(agi::fs::path const& filename, std::string const& encoding) 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(agi::fs::path const& filename) const;
-
-	/// @brief Check if the given subtitles can be losslessly written by this format
-	///
-	/// Default implementation rejects files with attachments, non-default
-	/// styles, and any overrides
-	virtual bool CanSave(const AssFile *file) const;
-
-	/// Load a subtitle file
-	/// @param[out] target Destination to read lines into
-	/// @param filename File to load
-	/// @param encoding Encoding to use. May be ignored by the reader.
-	virtual void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) 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, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding="") const { }
-
-	/// Export a subtitle file
-	///
-	/// This is used when saving via Export As..., for subtitle formats which
-	/// want to distinguish between exporting a final version of a script and
-	/// saving a project.
-	virtual void ExportFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding="") const {
-		WriteFile(src, filename, fps, encoding);
-	}
-
-	/// Get the wildcards for a save or load dialog
-	/// @param mode 0: load 1: save
-	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(agi::fs::path const& filename, std::string const& encoding);
-	/// Get a subtitle format that can write the given file or nullptr if none can
-	static const SubtitleFormat *GetWriter(agi::fs::path const& filename);
-	/// Initialize subtitle formats
-	static void LoadFormats();
+    /// Strip override tags
+    static void StripTags(AssFile &file);
+    /// 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, std::string const &newline, bool mergeLineBreaks = true);
+    /// Remove All commented and empty lines
+    static void StripComments(AssFile &file);
+    /// @brief Split and merge lines so there are no overlapping lines
+    ///
+    /// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
+    static void RecombineOverlaps(AssFile &file);
+    /// Merge sequential identical lines
+    static void MergeIdentical(AssFile &file);
+
+    /// Prompt the user for a frame rate to use
+    /// @param allow_vfr Include video frame rate as an option even if it's vfr
+    /// @param show_smpte Show SMPTE drop frame option
+    static agi::vfr::Framerate AskForFPS(bool allow_vfr, bool show_smpte, agi::vfr::Framerate const &fps);
+
+    /// Constructor
+    /// @param Subtitle format name
+    SubtitleFormat(std::string name);
+    /// Destructor
+    virtual ~SubtitleFormat() = default;
+
+    /// Get this format's 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(agi::fs::path const &filename, std::string const &encoding) 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(agi::fs::path const &filename) const;
+
+    /// @brief Check if the given subtitles can be losslessly written by this format
+    ///
+    /// Default implementation rejects files with attachments, non-default
+    /// styles, and any overrides
+    virtual bool CanSave(const AssFile *file) const;
+
+    /// Load a subtitle file
+    /// @param[out] target Destination to read lines into
+    /// @param filename File to load
+    /// @param encoding Encoding to use. May be ignored by the reader.
+    virtual void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) 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, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding = "") const { }
+
+    /// Export a subtitle file
+    ///
+    /// This is used when saving via Export As..., for subtitle formats which
+    /// want to distinguish between exporting a final version of a script and
+    /// saving a project.
+    virtual void ExportFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding = "") const {
+        WriteFile(src, filename, fps, encoding);
+    }
+
+    /// Get the wildcards for a save or load dialog
+    /// @param mode 0: load 1: save
+    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(agi::fs::path const &filename, std::string const &encoding);
+    /// Get a subtitle format that can write the given file or nullptr if none can
+    static const SubtitleFormat *GetWriter(agi::fs::path const &filename);
+    /// Initialize subtitle formats
+    static void LoadFormats();
 };
 
 DEFINE_EXCEPTION(SubtitleFormatParseError, agi::InvalidInputException);
diff --git a/src/subtitle_format_ass.cpp b/src/subtitle_format_ass.cpp
index 6815c229f853d0734d0334568c2e19d216b62e85..1a29b70e68e2d1c6ce312851021930148efac32d 100644
--- a/src/subtitle_format_ass.cpp
+++ b/src/subtitle_format_ass.cpp
@@ -33,13 +33,14 @@
 
 DEFINE_EXCEPTION(AssParseError, SubtitleFormatParseError);
 
-void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	int version = !agi::fs::HasExtension(filename, "ssa");
-
-	TextFileReader file(filename, encoding);
-	AssParser parser(target, version);
-	while (file.HasMoreLines())
-		parser.AddLine(file.ReadLineFromFile());
+void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    int version = !agi::fs::HasExtension(filename, "ssa");
+
+    TextFileReader file(filename, encoding);
+    AssParser parser(target, version);
+    while (file.HasMoreLines())
+        parser.AddLine(file.ReadLineFromFile());
 }
 
 #ifdef _WIN32
@@ -49,122 +50,125 @@ void AssSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename,
 #endif
 
 namespace {
-const char *format(AssEntryGroup group) {
-	if (group == AssEntryGroup::DIALOGUE)
-		return "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
-	if (group == AssEntryGroup::STYLE)
-		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 nullptr;
+const char *format(AssEntryGroup group)
+{
+    if (group == AssEntryGroup::DIALOGUE)
+        return "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" LINEBREAK;
+    if (group == AssEntryGroup::STYLE)
+        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 nullptr;
 }
 
 struct Writer {
-	TextFileWriter file;
-	AssEntryGroup group = AssEntryGroup::INFO;
-
-	Writer(agi::fs::path const& filename, std::string const& encoding)
-	: file(filename, encoding)
-	{
-		file.WriteLineToFile("[Script Info]");
-		file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
-		file.WriteLineToFile("; http://www.aegisub.org/");
-	}
-
-	template<typename T>
-	void Write(T const& list) {
-		for (auto const& line : list) {
-			if (line.Group() != group) {
-				// Add a blank line between each group
-				file.WriteLineToFile("");
-
-				file.WriteLineToFile(line.GroupHeader());
-				if (const char *str = format(line.Group()))
-					file.WriteLineToFile(str, false);
-
-				group = line.Group();
-			}
-
-			file.WriteLineToFile(line.GetEntryData());
-		}
-	}
-
-	void Write(ProjectProperties const& properties) {
-		file.WriteLineToFile("");
-		file.WriteLineToFile("[Aegisub Project Garbage]");
-
-		WriteIfNotEmpty("Automation Scripts: ", properties.automation_scripts);
-		WriteIfNotEmpty("Export Filters: ", properties.export_filters);
-		WriteIfNotEmpty("Export Encoding: ", properties.export_encoding);
-		WriteIfNotEmpty("Last Style Storage: ", properties.style_storage);
-		WriteIfNotEmpty("Audio File: ", properties.audio_file);
-		WriteIfNotEmpty("Video File: ", properties.video_file);
-		WriteIfNotEmpty("Timecodes File: ", properties.timecodes_file);
-		WriteIfNotEmpty("Keyframes File: ", properties.keyframes_file);
-
-		WriteIfNotZero("Video AR Mode: ", properties.ar_mode);
-		WriteIfNotZero("Video AR Value: ", properties.ar_value);
-
-		if (OPT_GET("App/Save UI State")->GetBool()) {
-			WriteIfNotZero("Video Zoom Percent: ", properties.video_zoom);
-			WriteIfNotZero("Scroll Position: ", properties.scroll_position);
-			WriteIfNotZero("Active Line: ", properties.active_row);
-			WriteIfNotZero("Video Position: ", properties.video_position);
-		}
-	}
-
-	void WriteIfNotEmpty(const char *key, std::string const& value) {
-		if (!value.empty())
-			file.WriteLineToFile(key + value);
-	}
-
-	template<typename Number>
-	void WriteIfNotZero(const char *key, Number n) {
-		if (n != Number{})
-			file.WriteLineToFile(key + std::to_string(n));
-	}
-
-	void WriteExtradata(std::vector<ExtradataEntry> const& extradata) {
-		if (extradata.empty())
-			return;
-
-		group = AssEntryGroup::EXTRADATA;
-		file.WriteLineToFile("");
-		file.WriteLineToFile("[Aegisub Extradata]");
-		for (auto const& edi : extradata) {
-			std::string line = "Data: ";
-			line += std::to_string(edi.id);
-			line += ",";
-			line += inline_string_encode(edi.key);
-			line += ",";
-			std::string encoded_data = inline_string_encode(edi.value);
-			if (4*edi.value.size() < 3*encoded_data.size()) {
-				// the inline_string encoding grew the data by more than uuencoding would
-				// so base64 encode it instead
-				line += "u"; // marker for uuencoding
-				line += agi::ass::UUEncode(edi.value.c_str(), edi.value.c_str() + edi.value.size(), false);
-			} else {
-				line += "e"; // marker for inline_string encoding (escaping)
-				line += encoded_data;
-			}
-			file.WriteLineToFile(line);
-		}
-	}
+    TextFileWriter file;
+    AssEntryGroup group = AssEntryGroup::INFO;
+
+    Writer(agi::fs::path const &filename, std::string const &encoding)
+        : file(filename, encoding) {
+        file.WriteLineToFile("[Script Info]");
+        file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
+        file.WriteLineToFile("; http://www.aegisub.org/");
+    }
+
+    template<typename T>
+    void Write(T const &list) {
+        for (auto const &line : list) {
+            if (line.Group() != group) {
+                // Add a blank line between each group
+                file.WriteLineToFile("");
+
+                file.WriteLineToFile(line.GroupHeader());
+                if (const char *str = format(line.Group()))
+                    file.WriteLineToFile(str, false);
+
+                group = line.Group();
+            }
+
+            file.WriteLineToFile(line.GetEntryData());
+        }
+    }
+
+    void Write(ProjectProperties const &properties) {
+        file.WriteLineToFile("");
+        file.WriteLineToFile("[Aegisub Project Garbage]");
+
+        WriteIfNotEmpty("Automation Scripts: ", properties.automation_scripts);
+        WriteIfNotEmpty("Export Filters: ", properties.export_filters);
+        WriteIfNotEmpty("Export Encoding: ", properties.export_encoding);
+        WriteIfNotEmpty("Last Style Storage: ", properties.style_storage);
+        WriteIfNotEmpty("Audio File: ", properties.audio_file);
+        WriteIfNotEmpty("Video File: ", properties.video_file);
+        WriteIfNotEmpty("Timecodes File: ", properties.timecodes_file);
+        WriteIfNotEmpty("Keyframes File: ", properties.keyframes_file);
+
+        WriteIfNotZero("Video AR Mode: ", properties.ar_mode);
+        WriteIfNotZero("Video AR Value: ", properties.ar_value);
+
+        if (OPT_GET("App/Save UI State")->GetBool()) {
+            WriteIfNotZero("Video Zoom Percent: ", properties.video_zoom);
+            WriteIfNotZero("Scroll Position: ", properties.scroll_position);
+            WriteIfNotZero("Active Line: ", properties.active_row);
+            WriteIfNotZero("Video Position: ", properties.video_position);
+        }
+    }
+
+    void WriteIfNotEmpty(const char *key, std::string const &value) {
+        if (!value.empty())
+            file.WriteLineToFile(key + value);
+    }
+
+    template<typename Number>
+    void WriteIfNotZero(const char *key, Number n) {
+        if (n != Number{})
+            file.WriteLineToFile(key + std::to_string(n));
+    }
+
+    void WriteExtradata(std::vector<ExtradataEntry> const &extradata) {
+        if (extradata.empty())
+            return;
+
+        group = AssEntryGroup::EXTRADATA;
+        file.WriteLineToFile("");
+        file.WriteLineToFile("[Aegisub Extradata]");
+        for (auto const &edi : extradata) {
+            std::string line = "Data: ";
+            line += std::to_string(edi.id);
+            line += ",";
+            line += inline_string_encode(edi.key);
+            line += ",";
+            std::string encoded_data = inline_string_encode(edi.value);
+            if (4 * edi.value.size() < 3 * encoded_data.size()) {
+                // the inline_string encoding grew the data by more than uuencoding would
+                // so base64 encode it instead
+                line += "u"; // marker for uuencoding
+                line += agi::ass::UUEncode(edi.value.c_str(), edi.value.c_str() + edi.value.size(), false);
+            }
+            else {
+                line += "e"; // marker for inline_string encoding (escaping)
+                line += encoded_data;
+            }
+            file.WriteLineToFile(line);
+        }
+    }
 };
 }
 
-void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	Writer writer(filename, encoding);
-	writer.Write(src->Info);
-	writer.Write(src->Properties);
-	writer.Write(src->Styles);
-	writer.Write(src->Attachments);
-	writer.Write(src->Events);
-	writer.WriteExtradata(src->Extradata);
+void AssSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    Writer writer(filename, encoding);
+    writer.Write(src->Info);
+    writer.Write(src->Properties);
+    writer.Write(src->Styles);
+    writer.Write(src->Attachments);
+    writer.Write(src->Events);
+    writer.WriteExtradata(src->Extradata);
 }
 
-void AssSubtitleFormat::ExportFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	Writer writer(filename, encoding);
-	writer.Write(src->Info);
-	writer.Write(src->Styles);
-	writer.Write(src->Attachments);
-	writer.Write(src->Events);
+void AssSubtitleFormat::ExportFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    Writer writer(filename, encoding);
+    writer.Write(src->Info);
+    writer.Write(src->Styles);
+    writer.Write(src->Attachments);
+    writer.Write(src->Events);
 }
diff --git a/src/subtitle_format_ass.h b/src/subtitle_format_ass.h
index 42691e6f71732d0fc3ea8e1623ffbf08fb9e8af4..595bd363878309f14e9448edf9185083a992b4c4 100644
--- a/src/subtitle_format_ass.h
+++ b/src/subtitle_format_ass.h
@@ -18,17 +18,17 @@
 
 class AssSubtitleFormat final : public SubtitleFormat {
 public:
-	AssSubtitleFormat() : SubtitleFormat("Advanced SubStation Alpha") { }
+    AssSubtitleFormat() : SubtitleFormat("Advanced SubStation Alpha") { }
 
-	std::vector<std::string> GetReadWildcards() const override { return {"ass", "ssa"}; }
-	std::vector<std::string> GetWriteWildcards() const override { return {"ass"}; }
+    std::vector<std::string> GetReadWildcards() const override { return {"ass", "ssa"}; }
+    std::vector<std::string> GetWriteWildcards() const override { return {"ass"}; }
 
-	// Naturally the ASS subtitle format can save all ASS files
-	bool CanSave(const AssFile*) const override { return true; }
+    // Naturally the ASS subtitle format can save all ASS files
+    bool CanSave(const AssFile *) const override { return true; }
 
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 
-	// Does not write [Aegisub Project Garbage] and [Aegisub Extradata] sections when exporting
-	void ExportFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    // Does not write [Aegisub Project Garbage] and [Aegisub Extradata] sections when exporting
+    void ExportFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_ebu3264.cpp b/src/subtitle_format_ebu3264.cpp
index 37f6d139d927c6067103195f2f6b44f06ed1df26..d3ae4b6cadbc4c69ff9aa84833e5cd6e8a32c502 100644
--- a/src/subtitle_format_ebu3264.cpp
+++ b/src/subtitle_format_ebu3264.cpp
@@ -40,606 +40,566 @@
 #include <boost/algorithm/string/replace.hpp>
 #include <wx/utils.h>
 
-namespace
-{
+namespace {
 #pragma pack(push, 1)
-	/// General Subtitle Information block as it appears in the file
-	struct BlockGSI
-	{
-		char cpn[3];    ///< code page number
-		char dfc[8];    ///< disk format code
-		char dsc;       ///< display standard code
-		char cct[2];    ///< character code table number
-		char lc[2];     ///< language code
-		char opt[32];   ///< original programme title
-		char oet[32];   ///< original episode title
-		char tpt[32];   ///< translated programme title
-		char tet[32];   ///< translated episode title
-		char tn[32];    ///< translator name
-		char tcd[32];   ///< translator contact details
-		char slr[16];   ///< subtitle list reference code
-		char cd[6];     ///< creation date
-		char rd[6];     ///< revision date
-		char rn[2];     ///< revision number
-		char tnb[5];    ///< total number of TTI blocks
-		char tns[5];    ///< total number of subtitles
-		char tng[3];    ///< total number of subtitle groups
-		char mnc[2];    ///< maximum number of displayable characters in a row
-		char mnr[2];    ///< maximum number of displayable rows
-		char tcs;       ///< time code: status
-		char tcp[8];    ///< time code: start of programme
-		char tcf[8];    ///< time code: first in-cue
-		char tnd;       ///< total number of disks
-		char dsn;       ///< disk sequence number
-		char co[3];     ///< country of origin
-		char pub[32];   ///< publisher
-		char en[32];    ///< editor's name
-		char ecd[32];   ///< editor's contact details
-		char unused[75];
-		char uda[576];  ///< user defined area
-	};
-
-	/// Text and Timing Information block as it appears in the file
-	struct BlockTTI
-	{
-		uint8_t     sgn; ///< subtitle group number
-		uint16_t    sn;  ///< subtitle number
-		uint8_t     ebn; ///< extension block number
-		uint8_t     cs;  ///< cumulative status
-		EbuTimecode tci; ///< time code in
-		EbuTimecode tco; ///< time code out
-		uint8_t     vp;  ///< vertical position
-		uint8_t     jc;  ///< justification code
-		uint8_t     cf;  ///< comment flag
-		char    tf[112]; ///< text field
-	};
+/// General Subtitle Information block as it appears in the file
+struct BlockGSI {
+    char cpn[3];    ///< code page number
+    char dfc[8];    ///< disk format code
+    char dsc;       ///< display standard code
+    char cct[2];    ///< character code table number
+    char lc[2];     ///< language code
+    char opt[32];   ///< original programme title
+    char oet[32];   ///< original episode title
+    char tpt[32];   ///< translated programme title
+    char tet[32];   ///< translated episode title
+    char tn[32];    ///< translator name
+    char tcd[32];   ///< translator contact details
+    char slr[16];   ///< subtitle list reference code
+    char cd[6];     ///< creation date
+    char rd[6];     ///< revision date
+    char rn[2];     ///< revision number
+    char tnb[5];    ///< total number of TTI blocks
+    char tns[5];    ///< total number of subtitles
+    char tng[3];    ///< total number of subtitle groups
+    char mnc[2];    ///< maximum number of displayable characters in a row
+    char mnr[2];    ///< maximum number of displayable rows
+    char tcs;       ///< time code: status
+    char tcp[8];    ///< time code: start of programme
+    char tcf[8];    ///< time code: first in-cue
+    char tnd;       ///< total number of disks
+    char dsn;       ///< disk sequence number
+    char co[3];     ///< country of origin
+    char pub[32];   ///< publisher
+    char en[32];    ///< editor's name
+    char ecd[32];   ///< editor's contact details
+    char unused[75];
+    char uda[576];  ///< user defined area
+};
+
+/// Text and Timing Information block as it appears in the file
+struct BlockTTI {
+    uint8_t     sgn; ///< subtitle group number
+    uint16_t    sn;  ///< subtitle number
+    uint8_t     ebn; ///< extension block number
+    uint8_t     cs;  ///< cumulative status
+    EbuTimecode tci; ///< time code in
+    EbuTimecode tco; ///< time code out
+    uint8_t     vp;  ///< vertical position
+    uint8_t     jc;  ///< justification code
+    uint8_t     cf;  ///< comment flag
+    char    tf[112]; ///< text field
+};
 #pragma pack(pop)
 
-	/// A block of text with basic formatting information
-	struct EbuFormattedText
-	{
-		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 t, bool u = false, bool i = false, bool ws = true) : text(std::move(t)), underline(u), italic(i), word_start(ws) { }
-	};
-	typedef std::vector<EbuFormattedText> EbuTextRow;
-
-	/// Formatting character constants
-	const unsigned char EBU_FORMAT_ITALIC[]     = "\x81\x80";
-	const unsigned char EBU_FORMAT_UNDERLINE[]  = "\x83\x82";
-	const unsigned char EBU_FORMAT_LINEBREAK    = '\x8a';
-	const unsigned char EBU_FORMAT_UNUSED_SPACE = '\x8f';
-
-	/// intermediate format
-	class EbuSubtitle
-	{
-		void ProcessOverrides(AssDialogueBlockOverride *ob, bool &underline, bool &italic, int &align, bool style_underline, bool style_italic)
-		{
-			for (auto const& t : ob->Tags)
-			{
-				if (t.Name == "\\u")
-					underline = t.Params[0].Get<bool>(style_underline);
-				else if (t.Name == "\\i")
-					italic = t.Params[0].Get<bool>(style_italic);
-				else if (t.Name == "\\an")
-					align = t.Params[0].Get<int>(align);
-				else if (t.Name == "\\a" && !t.Params[0].omitted)
-					align = AssStyle::SsaToAss(t.Params[0].Get<int>());
-			}
-		}
-
-		void SetAlignment(int ass_alignment)
-		{
-			if (ass_alignment < 1 || ass_alignment > 9)
-				ass_alignment = 2;
-
-			vertical_position = static_cast<VerticalPosition>(ass_alignment / 3);
-			justification_code = static_cast<JustificationCode>((ass_alignment - 1) % 3 + 1);
-		}
-
-	public:
-		enum CumulativeStatus
-		{
-			NotCumulative    = 0,
-			CumulativeStart  = 1,
-			CulumativeMiddle = 2,
-			CumulativeEnd    = 3
-		};
-
-		enum JustificationCode
-		{
-			UnchangedPresentation = 0,
-			JustifyLeft           = 1,
-			JustifyCentre         = 2,
-			JustifyRight          = 3
-		};
-
-		// note: not set to constants from spec
-		enum VerticalPosition
-		{
-			PositionTop    = 2,
-			PositionMiddle = 1,
-			PositionBottom = 0
-		};
-
-		int group_number = 0; ///< always 0 for compat
-		/// subtitle number is assigned when generating blocks
-		CumulativeStatus cumulative_status = NotCumulative; ///< always NotCumulative for compat
-		int time_in = 0;       ///< frame number
-		int time_out = 0;      ///< frame number
-		bool comment_flag = false; ///< always false for compat
-		JustificationCode justification_code = JustifyCentre; ///< never Unchanged presentation for compat
-		VerticalPosition vertical_position = PositionBottom;   ///< translated to row on tti conversion
-		std::vector<EbuTextRow> text_rows;    ///< text split into rows, still unicode
-
-		void SplitLines(int max_width, int split_type)
-		{
-			// split_type is an SSA wrap style number
-			if (split_type == 2) return; // no wrapping here!
-			if (split_type < 0) return;
-			if (split_type > 4) return;
-
-			std::vector<EbuTextRow> new_text;
-			new_text.reserve(text_rows.size());
-
-			for (auto const& row : text_rows)
-			{
-				// Get lengths of each word
-				std::vector<size_t> word_lengths;
-				for (auto const& cur_block : row)
-				{
-					if (cur_block.word_start)
-						word_lengths.push_back(0);
-					word_lengths.back() += cur_block.text.size();
-				}
-
-				std::vector<size_t> split_points = agi::get_wrap_points(word_lengths, (size_t)max_width, (agi::WrapMode)split_type);
-
-				if (split_points.empty())
-				{
-					// Line doesn't need splitting, so copy straight over
-					new_text.push_back(row);
-					continue;
-				}
-
-				// Apply the splits
-				new_text.emplace_back();
-				size_t cur_word = 0;
-				size_t split_point = 0;
-				for (auto const& cur_block : row)
-				{
-					if (cur_block.word_start && split_point < split_points.size())
-					{
-						if (split_points[split_point] == cur_word)
-						{
-							new_text.emplace_back();
-							++split_point;
-						}
-						++cur_word;
-					}
-
-					new_text.back().push_back(cur_block);
-				}
-			}
-
-			// replace old text
-			swap(text_rows, new_text);
-		}
-
-		bool CheckLineLengths(int max_width) const
-		{
-			for (auto const& row : text_rows)
-			{
-				int line_length = 0;
-				for (auto const& block : row)
-					line_length += block.text.size();
-
-				if (line_length > max_width)
-					// early return as soon as any line is over length
-					return false;
-			}
-			// no lines failed
-			return true;
-		}
-
-		void SetTextFromAss(AssDialogue *line, bool style_underline, bool style_italic, int align, int wrap_mode)
-		{
-			text_rows.clear();
-			text_rows.emplace_back();
-
-			// current row being worked on
-			EbuTextRow *cur_row = &text_rows.back();
-
-			// create initial text part
-			cur_row->emplace_back("", style_underline, style_italic, true);
-
-			bool underline = style_underline, italic = style_italic;
-
-			for (auto& b : line->ParseTags())
-			{
-				switch (b->GetType())
-				{
-					case AssBlockType::PLAIN:
-					// find special characters and convert them
-					{
-						std::string text = b->GetText();
-
-						boost::replace_all(text, "\\t", " ");
-
-						size_t start = 0;
-						for (size_t i = 0; i < text.size(); ++i)
-						{
-							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(begin(text) + start, begin(text) + i);
-
-							// process special character
-							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);
-
-								start = i + 2;
-							}
-							else // if (substr == " " || substr == "\\n")
-							{
-								cur_row->back().text.append(" ");
-								cur_row->emplace_back("", underline, italic, true);
-
-								start = i + 1 + (text[i] == '\\');
-							}
-							++i;
-						}
-
-						// add the remaining text
-						cur_row->back().text.append(begin(text) + start, end(text));
-
-						// convert \h to regular spaces
-						// done after parsing so that words aren't split on \h
-						boost::replace_all(cur_row->back().text, "\\h", " ");
-					}
-					break;
-
-					case AssBlockType::OVERRIDE:
-					// find relevant tags and process them
-					{
-						AssDialogueBlockOverride *ob = static_cast<AssDialogueBlockOverride*>(b.get());
-						ob->ParseTags();
-						ProcessOverrides(ob, underline, italic, align, style_underline, style_italic);
-
-						// apply any changes
-						if (underline != cur_row->back().underline || italic != cur_row->back().italic)
-						{
-							if (cur_row->back().text.empty())
-							{
-								// current part is empty, we can safely change formatting on it
-								cur_row->back().underline = underline;
-								cur_row->back().italic = italic;
-							}
-							else
-							{
-								// create a new empty part with new style
-								cur_row->emplace_back("", underline, italic, false);
-							}
-						}
-					}
-					break;
-
-				default:
-					// ignore block, we don't want to output it (drawing or comment)
-					break;
-				}
-			}
-
-			SetAlignment(align);
-		}
-	};
-
-	std::vector<EbuSubtitle> convert_subtitles(AssFile &copy, EbuExportSettings const& export_settings)
-	{
-		SubtitleFormat::StripComments(copy);
-		copy.Sort();
-		SubtitleFormat::RecombineOverlaps(copy);
-		SubtitleFormat::MergeIdentical(copy);
-
-		int line_wrap_type = copy.GetScriptInfoAsInt("WrapStyle");
-
-		agi::vfr::Framerate fps = export_settings.GetFramerate();
-		EbuTimecode tcofs = export_settings.timecode_offset;
-		int timecode_bias = fps.FrameAtSmpte(tcofs.h, tcofs.m, tcofs.s, tcofs.s);
-
-		AssStyle default_style;
-		std::vector<EbuSubtitle> subs_list;
-		subs_list.reserve(copy.Events.size());
-
-		// convert to intermediate format
-		for (auto& line : copy.Events)
-		{
-			// add a new subtitle and work on it
-			subs_list.emplace_back();
-			EbuSubtitle &imline = subs_list.back();
-
-			// some defaults for compatibility
-			imline.group_number = 0;
-			imline.comment_flag = false;
-			imline.cumulative_status = EbuSubtitle::NotCumulative;
-
-			// convert times
-			imline.time_in = fps.FrameAtTime(line.Start) + timecode_bias;
-			imline.time_out = fps.FrameAtTime(line.End) + timecode_bias;
-			if (export_settings.inclusive_end_times)
-				// cheap and possibly wrong way to ensure exclusive times, subtract one frame from end time
-				imline.time_out -= 1;
-
-			// convert alignment from style
-			AssStyle *style = copy.GetStyle(line.Style);
-			if (!style)
-				style = &default_style;
-
-			// add text, translate formatting
-			imline.SetTextFromAss(&line, style->underline, style->italic, style->alignment, line_wrap_type);
-
-			// line breaking handling
-			if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrap)
-				imline.SplitLines(export_settings.max_line_length, line_wrap_type);
-			else if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrapBalance)
-				imline.SplitLines(export_settings.max_line_length, agi::Wrap_Balanced);
-			else if (!imline.CheckLineLengths(export_settings.max_line_length))
-			{
-				if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
-					throw Ebu3264SubtitleFormat::ConversionFailed(agi::format(_("Line over maximum length: %s"), line.Text));
-				else // skip over-long lines
-					subs_list.pop_back();
-			}
-		}
-
-		// produce an empty line if there are none
-		// (it still has to contain a space to not get ignored)
-		if (subs_list.empty())
-		{
-			subs_list.emplace_back();
-			subs_list.back().text_rows.emplace_back();
-			subs_list.back().text_rows.back().emplace_back(" ");
-		}
-
-		return subs_list;
-	}
-
-	std::string convert_subtitle_line(EbuSubtitle const& sub, agi::charset::IconvWrapper *encoder, bool enable_formatting)
-	{
-		std::string fullstring;
-		for (auto const& row : sub.text_rows)
-		{
-			if (!fullstring.empty())
-				fullstring += EBU_FORMAT_LINEBREAK;
-
-			// formatting is reset at the start of every row, so keep track per row
-			bool underline = false, italic = false;
-			for (auto const& block : row)
-			{
-				if (enable_formatting)
-				{
-					// insert codes for changed formatting
-					if (underline != block.underline)
-						fullstring += EBU_FORMAT_UNDERLINE[block.underline];
-					if (italic != block.italic)
-						fullstring += EBU_FORMAT_ITALIC[block.italic];
-
-					underline = block.underline;
-					italic = block.italic;
-				}
-
-				// convert text to specified encoding
-				fullstring += encoder->Convert(block.text);
-			}
-		}
-		return fullstring;
-	}
-
-	void smpte_at_frame(agi::vfr::Framerate const& fps, int frame, EbuTimecode &tc)
-	{
-		int h=0, m=0, s=0, f=0;
-		fps.SmpteAtFrame(frame, &h, &m, &s, &f);
-		tc.h = h;
-		tc.m = m;
-		tc.s = s;
-		tc.f = f;
-	}
-
-	std::vector<BlockTTI> create_blocks(std::vector<EbuSubtitle> const& subs_list, EbuExportSettings const& export_settings)
-	{
-		auto encoder = export_settings.GetTextEncoder();
-		auto fps = export_settings.GetFramerate();
-
-		// Teletext captions are 1-23; Open subtitles are 0-99
-		uint8_t min_row = 0;
-		uint8_t max_row = 100;
-		if (export_settings.display_standard != EbuExportSettings::DSC_Open) {
-			min_row = 1;
-			max_row = 24;
-		}
-
-		uint16_t subtitle_number = 0;
-
-		std::vector<BlockTTI> tti;
-		tti.reserve(subs_list.size());
-		for (auto const& sub : subs_list)
-		{
-			std::string fullstring = convert_subtitle_line(sub, encoder.get(),
-				export_settings.display_standard == EbuExportSettings::DSC_Open);
-
-			// construct a base block that can be copied and filled
-			BlockTTI base;
-			base.sgn = sub.group_number;
-			base.sn = subtitle_number++;
-			base.ebn = 255;
-			base.cf = sub.comment_flag;
-			memset(base.tf, EBU_FORMAT_UNUSED_SPACE, sizeof(base.tf));
-			smpte_at_frame(fps, sub.time_in, base.tci);
-			smpte_at_frame(fps, sub.time_out, base.tco);
-			base.cs = sub.cumulative_status;
-
-			if (export_settings.translate_alignments)
-			{
-				// vertical position
-				if (sub.vertical_position == EbuSubtitle::PositionTop)
-					base.vp = min_row;
-				else if (sub.vertical_position == EbuSubtitle::PositionMiddle)
-					base.vp = std::min<uint8_t>(min_row, max_row / 2 - (max_row / 5 * sub.text_rows.size()));
-				else //if (sub.vertical_position == EbuSubtitle::PositionBottom)
-					base.vp = max_row - 1;
-
-				base.jc = sub.justification_code;
-			}
-			else
-			{
-				base.vp = max_row - 1;
-				base.jc = EbuSubtitle::JustifyCentre;
-			}
-
-			// produce blocks from string
-			static const size_t block_size = sizeof(((BlockTTI*)nullptr)->tf);
-			uint8_t num_blocks = 0;
-			for (size_t pos = 0; pos < fullstring.size(); pos += block_size)
-			{
-				size_t bytes_remaining = fullstring.size() - pos;
-
-				tti.push_back(base);
-				// write an extension block number if the remaining text doesn't fit in the block
-				tti.back().ebn = bytes_remaining >= block_size ? num_blocks++ : 255;
-
-				std::copy(&fullstring[pos], &fullstring[pos + std::min(block_size, bytes_remaining)], tti.back().tf);
-
-				// Write another block for the terminator if we exactly used up
-				// the last block
-				if (bytes_remaining == block_size)
-					tti.push_back(base);
-			}
-		}
-
-		return tti;
-	}
-
-	void fieldprintf(char *field, size_t fieldlen, const char *format, ...)
-	{
-		char buf[16];
-		va_list ap;
-		va_start(ap, format);
-		vsnprintf(buf, sizeof(buf), format, ap);
-		va_end(ap);
-		memcpy(field, buf, fieldlen);
-	}
-
-	BlockGSI create_header(AssFile const& copy, EbuExportSettings const& export_settings)
-	{
-		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("UTF-8", "CP850");
-
-		BlockGSI gsi;
-		memset(&gsi, 0x20, sizeof(gsi)); // fill with spaces
-		memcpy(gsi.cpn, "850", 3);
-		switch (export_settings.tv_standard)
-		{
-			case EbuExportSettings::STL23:
-			case EbuExportSettings::STL24:
-				memcpy(gsi.dfc, "STL24.01", 8);
-				break;
-			case EbuExportSettings::STL29:
-			case EbuExportSettings::STL29drop:
-			case EbuExportSettings::STL30:
-				memcpy(gsi.dfc, "STL30.01", 8);
-				break;
-			case EbuExportSettings::STL25:
-			default:
-				memcpy(gsi.dfc, "STL25.01", 8);
-				break;
-		}
-		gsi.dsc = '0' + (int)export_settings.display_standard;
-		gsi.cct[0] = '0';
-		gsi.cct[1] = '0' + (int)export_settings.text_encoding;
-		if (export_settings.text_encoding == EbuExportSettings::utf8)
-			memcpy(gsi.cct, "U8", 2);
-		memcpy(gsi.lc, "00", 2);
-		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;
-			time(&now);
-			tm *thetime = localtime(&now);
-			strftime(buf, 20, "AGI-%y%m%d%H%M%S", thetime);
-			memcpy(gsi.slr, buf, 16);
-			strftime(buf, 20, "%y%m%d", thetime);
-			memcpy(gsi.cd, buf, 6);
-			memcpy(gsi.rd, buf, 6);
-			memcpy(gsi.rn, "00", 2);
-			memcpy(gsi.tng, "001", 3);
-			fieldprintf(gsi.mnc, 2, "%02u", (unsigned int)export_settings.max_line_length);
-			memcpy(gsi.mnr, "99", 2);
-			gsi.tcs = '1';
-			EbuTimecode tcofs = export_settings.timecode_offset;
-			fieldprintf(gsi.tcp, 8, "%02u%02u%02u%02u", (unsigned int)tcofs.h, (unsigned int)tcofs.m, (unsigned int)tcofs.s, (unsigned int)tcofs.s);
-		}
-		gsi.tnd = '1';
-		gsi.dsn = '1';
-		memcpy(gsi.co, "XXX", 3);
-		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));
-
-		return gsi;
-	}
-
-	EbuExportSettings get_export_config(wxWindow *parent)
-	{
-		EbuExportSettings s("Subtitle Format/EBU STL");
-
-		// Disable the busy cursor set by the exporter while the dialog is visible
-		wxEndBusyCursor();
-		int res = ShowEbuExportConfigurationDialog(parent, s);
-		wxBeginBusyCursor();
-
-		if (res != wxID_OK)
-			throw agi::UserCancelException("EBU/STL export");
-
-		s.Save();
-		return s;
-	}
+/// A block of text with basic formatting information
+struct EbuFormattedText {
+    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 t, bool u = false, bool i = false, bool ws = true) : text(std::move(t)), underline(u), italic(i), word_start(ws) { }
+};
+typedef std::vector<EbuFormattedText> EbuTextRow;
+
+/// Formatting character constants
+const unsigned char EBU_FORMAT_ITALIC[]     = "\x81\x80";
+const unsigned char EBU_FORMAT_UNDERLINE[]  = "\x83\x82";
+const unsigned char EBU_FORMAT_LINEBREAK    = '\x8a';
+const unsigned char EBU_FORMAT_UNUSED_SPACE = '\x8f';
+
+/// intermediate format
+class EbuSubtitle {
+    void ProcessOverrides(AssDialogueBlockOverride *ob, bool &underline, bool &italic, int &align, bool style_underline, bool style_italic) {
+        for (auto const &t : ob->Tags) {
+            if (t.Name == "\\u")
+                underline = t.Params[0].Get<bool>(style_underline);
+            else if (t.Name == "\\i")
+                italic = t.Params[0].Get<bool>(style_italic);
+            else if (t.Name == "\\an")
+                align = t.Params[0].Get<int>(align);
+            else if (t.Name == "\\a" && !t.Params[0].omitted)
+                align = AssStyle::SsaToAss(t.Params[0].Get<int>());
+        }
+    }
+
+    void SetAlignment(int ass_alignment) {
+        if (ass_alignment < 1 || ass_alignment > 9)
+            ass_alignment = 2;
+
+        vertical_position = static_cast<VerticalPosition>(ass_alignment / 3);
+        justification_code = static_cast<JustificationCode>((ass_alignment - 1) % 3 + 1);
+    }
+
+public:
+    enum CumulativeStatus {
+        NotCumulative    = 0,
+        CumulativeStart  = 1,
+        CulumativeMiddle = 2,
+        CumulativeEnd    = 3
+    };
+
+    enum JustificationCode {
+        UnchangedPresentation = 0,
+        JustifyLeft           = 1,
+        JustifyCentre         = 2,
+        JustifyRight          = 3
+    };
+
+    // note: not set to constants from spec
+    enum VerticalPosition {
+        PositionTop    = 2,
+        PositionMiddle = 1,
+        PositionBottom = 0
+    };
+
+    int group_number = 0; ///< always 0 for compat
+    /// subtitle number is assigned when generating blocks
+    CumulativeStatus cumulative_status = NotCumulative; ///< always NotCumulative for compat
+    int time_in = 0;       ///< frame number
+    int time_out = 0;      ///< frame number
+    bool comment_flag = false; ///< always false for compat
+    JustificationCode justification_code = JustifyCentre; ///< never Unchanged presentation for compat
+    VerticalPosition vertical_position = PositionBottom;   ///< translated to row on tti conversion
+    std::vector<EbuTextRow> text_rows;    ///< text split into rows, still unicode
+
+    void SplitLines(int max_width, int split_type) {
+        // split_type is an SSA wrap style number
+        if (split_type == 2) return; // no wrapping here!
+        if (split_type < 0) return;
+        if (split_type > 4) return;
+
+        std::vector<EbuTextRow> new_text;
+        new_text.reserve(text_rows.size());
+
+        for (auto const &row : text_rows) {
+            // Get lengths of each word
+            std::vector<size_t> word_lengths;
+            for (auto const &cur_block : row) {
+                if (cur_block.word_start)
+                    word_lengths.push_back(0);
+                word_lengths.back() += cur_block.text.size();
+            }
+
+            std::vector<size_t> split_points = agi::get_wrap_points(word_lengths, (size_t)max_width, (agi::WrapMode)split_type);
+
+            if (split_points.empty()) {
+                // Line doesn't need splitting, so copy straight over
+                new_text.push_back(row);
+                continue;
+            }
+
+            // Apply the splits
+            new_text.emplace_back();
+            size_t cur_word = 0;
+            size_t split_point = 0;
+            for (auto const &cur_block : row) {
+                if (cur_block.word_start && split_point < split_points.size()) {
+                    if (split_points[split_point] == cur_word) {
+                        new_text.emplace_back();
+                        ++split_point;
+                    }
+                    ++cur_word;
+                }
+
+                new_text.back().push_back(cur_block);
+            }
+        }
+
+        // replace old text
+        swap(text_rows, new_text);
+    }
+
+    bool CheckLineLengths(int max_width) const {
+        for (auto const &row : text_rows) {
+            int line_length = 0;
+            for (auto const &block : row)
+                line_length += block.text.size();
+
+            if (line_length > max_width)
+                // early return as soon as any line is over length
+                return false;
+        }
+        // no lines failed
+        return true;
+    }
+
+    void SetTextFromAss(AssDialogue *line, bool style_underline, bool style_italic, int align, int wrap_mode) {
+        text_rows.clear();
+        text_rows.emplace_back();
+
+        // current row being worked on
+        EbuTextRow *cur_row = &text_rows.back();
+
+        // create initial text part
+        cur_row->emplace_back("", style_underline, style_italic, true);
+
+        bool underline = style_underline, italic = style_italic;
+
+        for (auto &b : line->ParseTags()) {
+            switch (b->GetType()) {
+            case AssBlockType::PLAIN:
+                // find special characters and convert them
+            {
+                std::string text = b->GetText();
+
+                boost::replace_all(text, "\\t", " ");
+
+                size_t start = 0;
+                for (size_t i = 0; i < text.size(); ++i) {
+                    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(begin(text) + start, begin(text) + i);
+
+                    // process special character
+                    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);
+
+                        start = i + 2;
+                    }
+                    else { // if (substr == " " || substr == "\\n")
+                        cur_row->back().text.append(" ");
+                        cur_row->emplace_back("", underline, italic, true);
+
+                        start = i + 1 + (text[i] == '\\');
+                    }
+                    ++i;
+                }
+
+                // add the remaining text
+                cur_row->back().text.append(begin(text) + start, end(text));
+
+                // convert \h to regular spaces
+                // done after parsing so that words aren't split on \h
+                boost::replace_all(cur_row->back().text, "\\h", " ");
+            }
+            break;
+
+            case AssBlockType::OVERRIDE:
+                // find relevant tags and process them
+            {
+                AssDialogueBlockOverride *ob = static_cast<AssDialogueBlockOverride *>(b.get());
+                ob->ParseTags();
+                ProcessOverrides(ob, underline, italic, align, style_underline, style_italic);
+
+                // apply any changes
+                if (underline != cur_row->back().underline || italic != cur_row->back().italic) {
+                    if (cur_row->back().text.empty()) {
+                        // current part is empty, we can safely change formatting on it
+                        cur_row->back().underline = underline;
+                        cur_row->back().italic = italic;
+                    }
+                    else {
+                        // create a new empty part with new style
+                        cur_row->emplace_back("", underline, italic, false);
+                    }
+                }
+            }
+            break;
+
+            default:
+                // ignore block, we don't want to output it (drawing or comment)
+                break;
+            }
+        }
+
+        SetAlignment(align);
+    }
+};
+
+std::vector<EbuSubtitle> convert_subtitles(AssFile &copy, EbuExportSettings const &export_settings)
+{
+    SubtitleFormat::StripComments(copy);
+    copy.Sort();
+    SubtitleFormat::RecombineOverlaps(copy);
+    SubtitleFormat::MergeIdentical(copy);
+
+    int line_wrap_type = copy.GetScriptInfoAsInt("WrapStyle");
+
+    agi::vfr::Framerate fps = export_settings.GetFramerate();
+    EbuTimecode tcofs = export_settings.timecode_offset;
+    int timecode_bias = fps.FrameAtSmpte(tcofs.h, tcofs.m, tcofs.s, tcofs.s);
+
+    AssStyle default_style;
+    std::vector<EbuSubtitle> subs_list;
+    subs_list.reserve(copy.Events.size());
+
+    // convert to intermediate format
+    for (auto &line : copy.Events) {
+        // add a new subtitle and work on it
+        subs_list.emplace_back();
+        EbuSubtitle &imline = subs_list.back();
+
+        // some defaults for compatibility
+        imline.group_number = 0;
+        imline.comment_flag = false;
+        imline.cumulative_status = EbuSubtitle::NotCumulative;
+
+        // convert times
+        imline.time_in = fps.FrameAtTime(line.Start) + timecode_bias;
+        imline.time_out = fps.FrameAtTime(line.End) + timecode_bias;
+        if (export_settings.inclusive_end_times)
+            // cheap and possibly wrong way to ensure exclusive times, subtract one frame from end time
+            imline.time_out -= 1;
+
+        // convert alignment from style
+        AssStyle *style = copy.GetStyle(line.Style);
+        if (!style)
+            style = &default_style;
+
+        // add text, translate formatting
+        imline.SetTextFromAss(&line, style->underline, style->italic, style->alignment, line_wrap_type);
+
+        // line breaking handling
+        if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrap)
+            imline.SplitLines(export_settings.max_line_length, line_wrap_type);
+        else if (export_settings.line_wrapping_mode == EbuExportSettings::AutoWrapBalance)
+            imline.SplitLines(export_settings.max_line_length, agi::Wrap_Balanced);
+        else if (!imline.CheckLineLengths(export_settings.max_line_length)) {
+            if (export_settings.line_wrapping_mode == EbuExportSettings::AbortOverLength)
+                throw Ebu3264SubtitleFormat::ConversionFailed(agi::format(_("Line over maximum length: %s"), line.Text));
+            else // skip over-long lines
+                subs_list.pop_back();
+        }
+    }
+
+    // produce an empty line if there are none
+    // (it still has to contain a space to not get ignored)
+    if (subs_list.empty()) {
+        subs_list.emplace_back();
+        subs_list.back().text_rows.emplace_back();
+        subs_list.back().text_rows.back().emplace_back(" ");
+    }
+
+    return subs_list;
+}
+
+std::string convert_subtitle_line(EbuSubtitle const &sub, agi::charset::IconvWrapper *encoder, bool enable_formatting)
+{
+    std::string fullstring;
+    for (auto const &row : sub.text_rows) {
+        if (!fullstring.empty())
+            fullstring += EBU_FORMAT_LINEBREAK;
+
+        // formatting is reset at the start of every row, so keep track per row
+        bool underline = false, italic = false;
+        for (auto const &block : row) {
+            if (enable_formatting) {
+                // insert codes for changed formatting
+                if (underline != block.underline)
+                    fullstring += EBU_FORMAT_UNDERLINE[block.underline];
+                if (italic != block.italic)
+                    fullstring += EBU_FORMAT_ITALIC[block.italic];
+
+                underline = block.underline;
+                italic = block.italic;
+            }
+
+            // convert text to specified encoding
+            fullstring += encoder->Convert(block.text);
+        }
+    }
+    return fullstring;
+}
+
+void smpte_at_frame(agi::vfr::Framerate const &fps, int frame, EbuTimecode &tc)
+{
+    int h = 0, m = 0, s = 0, f = 0;
+    fps.SmpteAtFrame(frame, &h, &m, &s, &f);
+    tc.h = h;
+    tc.m = m;
+    tc.s = s;
+    tc.f = f;
+}
+
+std::vector<BlockTTI> create_blocks(std::vector<EbuSubtitle> const &subs_list, EbuExportSettings const &export_settings)
+{
+    auto encoder = export_settings.GetTextEncoder();
+    auto fps = export_settings.GetFramerate();
+
+    // Teletext captions are 1-23; Open subtitles are 0-99
+    uint8_t min_row = 0;
+    uint8_t max_row = 100;
+    if (export_settings.display_standard != EbuExportSettings::DSC_Open) {
+        min_row = 1;
+        max_row = 24;
+    }
+
+    uint16_t subtitle_number = 0;
+
+    std::vector<BlockTTI> tti;
+    tti.reserve(subs_list.size());
+    for (auto const &sub : subs_list) {
+        std::string fullstring = convert_subtitle_line(sub, encoder.get(),
+                                 export_settings.display_standard == EbuExportSettings::DSC_Open);
+
+        // construct a base block that can be copied and filled
+        BlockTTI base;
+        base.sgn = sub.group_number;
+        base.sn = subtitle_number++;
+        base.ebn = 255;
+        base.cf = sub.comment_flag;
+        memset(base.tf, EBU_FORMAT_UNUSED_SPACE, sizeof(base.tf));
+        smpte_at_frame(fps, sub.time_in, base.tci);
+        smpte_at_frame(fps, sub.time_out, base.tco);
+        base.cs = sub.cumulative_status;
+
+        if (export_settings.translate_alignments) {
+            // vertical position
+            if (sub.vertical_position == EbuSubtitle::PositionTop)
+                base.vp = min_row;
+            else if (sub.vertical_position == EbuSubtitle::PositionMiddle)
+                base.vp = std::min<uint8_t>(min_row, max_row / 2 - (max_row / 5 * sub.text_rows.size()));
+            else //if (sub.vertical_position == EbuSubtitle::PositionBottom)
+                base.vp = max_row - 1;
+
+            base.jc = sub.justification_code;
+        }
+        else {
+            base.vp = max_row - 1;
+            base.jc = EbuSubtitle::JustifyCentre;
+        }
+
+        // produce blocks from string
+        static const size_t block_size = sizeof(((BlockTTI *)nullptr)->tf);
+        uint8_t num_blocks = 0;
+        for (size_t pos = 0; pos < fullstring.size(); pos += block_size) {
+            size_t bytes_remaining = fullstring.size() - pos;
+
+            tti.push_back(base);
+            // write an extension block number if the remaining text doesn't fit in the block
+            tti.back().ebn = bytes_remaining >= block_size ? num_blocks++ : 255;
+
+            std::copy(&fullstring[pos], &fullstring[pos + std::min(block_size, bytes_remaining)], tti.back().tf);
+
+            // Write another block for the terminator if we exactly used up
+            // the last block
+            if (bytes_remaining == block_size)
+                tti.push_back(base);
+        }
+    }
+
+    return tti;
+}
+
+void fieldprintf(char *field, size_t fieldlen, const char *format, ...)
+{
+    char buf[16];
+    va_list ap;
+    va_start(ap, format);
+    vsnprintf(buf, sizeof(buf), format, ap);
+    va_end(ap);
+    memcpy(field, buf, fieldlen);
+}
+
+BlockGSI create_header(AssFile const &copy, EbuExportSettings const &export_settings)
+{
+    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("UTF-8", "CP850");
+
+    BlockGSI gsi;
+    memset(&gsi, 0x20, sizeof(gsi)); // fill with spaces
+    memcpy(gsi.cpn, "850", 3);
+    switch (export_settings.tv_standard) {
+    case EbuExportSettings::STL23:
+    case EbuExportSettings::STL24:
+        memcpy(gsi.dfc, "STL24.01", 8);
+        break;
+    case EbuExportSettings::STL29:
+    case EbuExportSettings::STL29drop:
+    case EbuExportSettings::STL30:
+        memcpy(gsi.dfc, "STL30.01", 8);
+        break;
+    case EbuExportSettings::STL25:
+    default:
+        memcpy(gsi.dfc, "STL25.01", 8);
+        break;
+    }
+    gsi.dsc = '0' + (int)export_settings.display_standard;
+    gsi.cct[0] = '0';
+    gsi.cct[1] = '0' + (int)export_settings.text_encoding;
+    if (export_settings.text_encoding == EbuExportSettings::utf8)
+        memcpy(gsi.cct, "U8", 2);
+    memcpy(gsi.lc, "00", 2);
+    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;
+        time(&now);
+        tm *thetime = localtime(&now);
+        strftime(buf, 20, "AGI-%y%m%d%H%M%S", thetime);
+        memcpy(gsi.slr, buf, 16);
+        strftime(buf, 20, "%y%m%d", thetime);
+        memcpy(gsi.cd, buf, 6);
+        memcpy(gsi.rd, buf, 6);
+        memcpy(gsi.rn, "00", 2);
+        memcpy(gsi.tng, "001", 3);
+        fieldprintf(gsi.mnc, 2, "%02u", (unsigned int)export_settings.max_line_length);
+        memcpy(gsi.mnr, "99", 2);
+        gsi.tcs = '1';
+        EbuTimecode tcofs = export_settings.timecode_offset;
+        fieldprintf(gsi.tcp, 8, "%02u%02u%02u%02u", (unsigned int)tcofs.h, (unsigned int)tcofs.m, (unsigned int)tcofs.s, (unsigned int)tcofs.s);
+    }
+    gsi.tnd = '1';
+    gsi.dsn = '1';
+    memcpy(gsi.co, "XXX", 3);
+    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));
+
+    return gsi;
+}
+
+EbuExportSettings get_export_config(wxWindow *parent)
+{
+    EbuExportSettings s("Subtitle Format/EBU STL");
+
+    // Disable the busy cursor set by the exporter while the dialog is visible
+    wxEndBusyCursor();
+    int res = ShowEbuExportConfigurationDialog(parent, s);
+    wxBeginBusyCursor();
+
+    if (res != wxID_OK)
+        throw agi::UserCancelException("EBU/STL export");
+
+    s.Save();
+    return s;
+}
 
 } // namespace {
 
 Ebu3264SubtitleFormat::Ebu3264SubtitleFormat()
-: SubtitleFormat("EBU subtitling data exchange format (EBU tech 3264, 1991)")
+    : SubtitleFormat("EBU subtitling data exchange format (EBU tech 3264, 1991)")
 {
 }
 
-void Ebu3264SubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const&) const
+void Ebu3264SubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &) const
 {
-	// collect data from user
-	EbuExportSettings export_settings = get_export_config(nullptr);
-	AssFile copy(*src);
-
-	std::vector<EbuSubtitle> subs_list = convert_subtitles(copy, export_settings);
-	std::vector<BlockTTI> tti = create_blocks(subs_list, export_settings);
-	BlockGSI gsi = create_header(copy, export_settings);
-
-	BlockTTI &block0 = tti.front();
-	fieldprintf(gsi.tcf, 8, "%02u%02u%02u%02u", (unsigned int)block0.tci.h, (unsigned int)block0.tci.m, (unsigned int)block0.tci.s, (unsigned int)block0.tci.f);
-	fieldprintf(gsi.tnb, 5, "%5u", (unsigned int)tti.size());
-	fieldprintf(gsi.tns, 5, "%5u", (unsigned int)subs_list.size());
-
-	// write file
-	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));
+    // collect data from user
+    EbuExportSettings export_settings = get_export_config(nullptr);
+    AssFile copy(*src);
+
+    std::vector<EbuSubtitle> subs_list = convert_subtitles(copy, export_settings);
+    std::vector<BlockTTI> tti = create_blocks(subs_list, export_settings);
+    BlockGSI gsi = create_header(copy, export_settings);
+
+    BlockTTI &block0 = tti.front();
+    fieldprintf(gsi.tcf, 8, "%02u%02u%02u%02u", (unsigned int)block0.tci.h, (unsigned int)block0.tci.m, (unsigned int)block0.tci.s, (unsigned int)block0.tci.f);
+    fieldprintf(gsi.tnb, 5, "%5u", (unsigned int)tti.size());
+    fieldprintf(gsi.tns, 5, "%5u", (unsigned int)subs_list.size());
+
+    // write file
+    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/src/subtitle_format_ebu3264.h b/src/subtitle_format_ebu3264.h
index e7e5cc452c06b7a6da2c8632634719279623cd31..8ae9cb16bb357414c59167c8a5e7b4f8854de4d0 100644
--- a/src/subtitle_format_ebu3264.h
+++ b/src/subtitle_format_ebu3264.h
@@ -26,9 +26,9 @@
 /// Work on support for this format was sponsored by Bandai.
 class Ebu3264SubtitleFormat final : public SubtitleFormat {
 public:
-	Ebu3264SubtitleFormat();
-	std::vector<std::string> GetWriteWildcards() const override { return {"stl"}; }
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    Ebu3264SubtitleFormat();
+    std::vector<std::string> GetWriteWildcards() const override { return {"stl"}; }
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 
-	DEFINE_EXCEPTION(ConversionFailed, agi::InvalidInputException);
+    DEFINE_EXCEPTION(ConversionFailed, agi::InvalidInputException);
 };
diff --git a/src/subtitle_format_encore.cpp b/src/subtitle_format_encore.cpp
index cf54b17219f14b384eaa0c4715f804fade270dbe..00afb50c61f83f7fd74fc5915194652539d1b0bd 100644
--- a/src/subtitle_format_encore.cpp
+++ b/src/subtitle_format_encore.cpp
@@ -42,34 +42,36 @@
 #include <libaegisub/format.h>
 
 EncoreSubtitleFormat::EncoreSubtitleFormat()
-: SubtitleFormat("Adobe Encore")
+    : SubtitleFormat("Adobe Encore")
 {
 }
 
-std::vector<std::string> EncoreSubtitleFormat::GetWriteWildcards() const {
-	return {"encore.txt"};
+std::vector<std::string> EncoreSubtitleFormat::GetWriteWildcards() const
+{
+    return {"encore.txt"};
 }
 
-void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& video_fps, std::string const&) const {
-	agi::vfr::Framerate fps = AskForFPS(false, true, video_fps);
-	if (!fps.IsLoaded()) return;
+void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &video_fps, std::string const &) const
+{
+    agi::vfr::Framerate fps = AskForFPS(false, true, video_fps);
+    if (!fps.IsLoaded()) return;
 
-	// Convert to encore
-	AssFile copy(*src);
-	copy.Sort();
-	StripComments(copy);
-	RecombineOverlaps(copy);
-	MergeIdentical(copy);
-	StripTags(copy);
-	ConvertNewlines(copy, "\r\n");
+    // Convert to encore
+    AssFile copy(*src);
+    copy.Sort();
+    StripComments(copy);
+    RecombineOverlaps(copy);
+    MergeIdentical(copy);
+    StripTags(copy);
+    ConvertNewlines(copy, "\r\n");
 
-	// Encore wants ; for NTSC and : for PAL
-	// The manual suggests no other frame rates are supported
-	agi::SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ';' : ':');
+    // Encore wants ; for NTSC and : for PAL
+    // The manual suggests no other frame rates are supported
+    agi::SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ';' : ':');
 
-	// Write lines
-	int i = 0;
-	TextFileWriter file(filename, "UTF-8");
-	for (auto const& current : copy.Events)
-		file.WriteLineToFile(agi::format("%i %s %s %s", ++i, ft.ToSMPTE(current.Start), ft.ToSMPTE(current.End), current.Text));
+    // Write lines
+    int i = 0;
+    TextFileWriter file(filename, "UTF-8");
+    for (auto const &current : copy.Events)
+        file.WriteLineToFile(agi::format("%i %s %s %s", ++i, ft.ToSMPTE(current.Start), ft.ToSMPTE(current.End), current.Text));
 }
diff --git a/src/subtitle_format_encore.h b/src/subtitle_format_encore.h
index 77827e8f6fa41946866dbd85985361014ddf646d..84ab565e878f5ccb429a81945fe2bf32f074819e 100644
--- a/src/subtitle_format_encore.h
+++ b/src/subtitle_format_encore.h
@@ -36,7 +36,7 @@
 
 class EncoreSubtitleFormat final : public SubtitleFormat {
 public:
-	EncoreSubtitleFormat();
-	std::vector<std::string> GetWriteWildcards() const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const&) const override;
+    EncoreSubtitleFormat();
+    std::vector<std::string> GetWriteWildcards() const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &) const override;
 };
diff --git a/src/subtitle_format_microdvd.cpp b/src/subtitle_format_microdvd.cpp
index 6c3991718c0ef387d392bc12554e191ab5137e78..169b6f02c2401162697cef5ff3419c7b6ece663d 100644
--- a/src/subtitle_format_microdvd.cpp
+++ b/src/subtitle_format_microdvd.cpp
@@ -51,98 +51,103 @@
 #include <boost/regex.hpp>
 
 MicroDVDSubtitleFormat::MicroDVDSubtitleFormat()
-: SubtitleFormat("MicroDVD")
+    : SubtitleFormat("MicroDVD")
 {
 }
 
-std::vector<std::string> MicroDVDSubtitleFormat::GetReadWildcards() const {
-	return {"sub"};
+std::vector<std::string> MicroDVDSubtitleFormat::GetReadWildcards() const
+{
+    return {"sub"};
 }
 
-std::vector<std::string> MicroDVDSubtitleFormat::GetWriteWildcards() const {
-	return GetReadWildcards();
+std::vector<std::string> MicroDVDSubtitleFormat::GetWriteWildcards() const
+{
+    return GetReadWildcards();
 }
 
 static const boost::regex line_regex(R"(^[\{\[]([0-9]+)[\}\]][\{\[]([0-9]+)[\}\]](.*)$)");
 
-bool MicroDVDSubtitleFormat::CanReadFile(agi::fs::path const& filename, std::string const& encoding) const {
-	// Return false immediately if extension is wrong
-	if (!agi::fs::HasExtension(filename, "sub")) return false;
+bool MicroDVDSubtitleFormat::CanReadFile(agi::fs::path const &filename, std::string const &encoding) const
+{
+    // Return false immediately if extension is wrong
+    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, encoding);
-	if (file.HasMoreLines())
-		return regex_match(file.ReadLineFromFile(), line_regex);
+    // Since there is an infinity of .sub formats, load first line and check if it's valid
+    TextFileReader file(filename, encoding);
+    if (file.HasMoreLines())
+        return regex_match(file.ReadLineFromFile(), line_regex);
 
-	return false;
+    return false;
 }
 
-void MicroDVDSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& vfps, std::string const& encoding) const {
-	TextFileReader file(filename, encoding);
+void MicroDVDSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &vfps, std::string const &encoding) const
+{
+    TextFileReader file(filename, encoding);
 
-	target->LoadDefault(false, OPT_GET("Subtitle Format/MicroDVD/Default Style Catalog")->GetString());
+    target->LoadDefault(false, OPT_GET("Subtitle Format/MicroDVD/Default Style Catalog")->GetString());
 
-	agi::vfr::Framerate fps;
+    agi::vfr::Framerate fps;
 
-	bool isFirst = true;
-	while (file.HasMoreLines()) {
-		boost::smatch match;
-		std::string line = file.ReadLineFromFile();
-		if (!regex_match(line, match, line_regex)) continue;
+    bool isFirst = true;
+    while (file.HasMoreLines()) {
+        boost::smatch match;
+        std::string line = file.ReadLineFromFile();
+        if (!regex_match(line, match, line_regex)) continue;
 
-		std::string text = match[3].str();
+        std::string text = match[3].str();
 
-		// If it's the first, check if it contains fps information
-		if (isFirst) {
-			isFirst = false;
+        // If it's the first, check if it contains fps information
+        if (isFirst) {
+            isFirst = false;
 
-			double cfr;
-			if (agi::util::try_parse(text, &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
-			fps = AskForFPS(true, false, vfps);
-			if (!fps.IsLoaded()) return;
-		}
+            // If it wasn't an fps line, ask the user for it
+            fps = AskForFPS(true, false, vfps);
+            if (!fps.IsLoaded()) return;
+        }
 
-		int f1 = boost::lexical_cast<int>(match[1]);
-		int f2 = boost::lexical_cast<int>(match[2]);
+        int f1 = boost::lexical_cast<int>(match[1]);
+        int f2 = boost::lexical_cast<int>(match[2]);
 
-		boost::replace_all(text, "|", "\\N");
+        boost::replace_all(text, "|", "\\N");
 
-		auto diag = new AssDialogue;
-		diag->Start = fps.TimeAtFrame(f1, agi::vfr::START);
-		diag->End = fps.TimeAtFrame(f2, agi::vfr::END);
-		diag->Text = text;
-		target->Events.push_back(*diag);
-	}
+        auto diag = new AssDialogue;
+        diag->Start = fps.TimeAtFrame(f1, agi::vfr::START);
+        diag->End = fps.TimeAtFrame(f2, agi::vfr::END);
+        diag->Text = text;
+        target->Events.push_back(*diag);
+    }
 }
 
-void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& vfps, std::string const& encoding) const {
-	agi::vfr::Framerate fps = AskForFPS(true, false, vfps);
-	if (!fps.IsLoaded()) return;
-
-	AssFile copy(*src);
-	copy.Sort();
-	StripComments(copy);
-	RecombineOverlaps(copy);
-	MergeIdentical(copy);
-	StripTags(copy);
-	ConvertNewlines(copy, "|");
-
-	TextFileWriter file(filename, encoding);
-
-	// Write FPS line
-	if (!fps.IsVFR())
-		file.WriteLineToFile(agi::format("{1}{1}%.6f", fps.FPS()));
-
-	// Write lines
-	for (auto const& current : copy.Events) {
-		int start = fps.FrameAtTime(current.Start, agi::vfr::START);
-		int end = fps.FrameAtTime(current.End, agi::vfr::END);
-
-		file.WriteLineToFile(agi::format("{%i}{%i}%s", start, end, boost::replace_all_copy(current.Text.get(), "\\N", "|")));
-	}
+void MicroDVDSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &vfps, std::string const &encoding) const
+{
+    agi::vfr::Framerate fps = AskForFPS(true, false, vfps);
+    if (!fps.IsLoaded()) return;
+
+    AssFile copy(*src);
+    copy.Sort();
+    StripComments(copy);
+    RecombineOverlaps(copy);
+    MergeIdentical(copy);
+    StripTags(copy);
+    ConvertNewlines(copy, "|");
+
+    TextFileWriter file(filename, encoding);
+
+    // Write FPS line
+    if (!fps.IsVFR())
+        file.WriteLineToFile(agi::format("{1}{1}%.6f", fps.FPS()));
+
+    // Write lines
+    for (auto const &current : copy.Events) {
+        int start = fps.FrameAtTime(current.Start, agi::vfr::START);
+        int end = fps.FrameAtTime(current.End, agi::vfr::END);
+
+        file.WriteLineToFile(agi::format("{%i}{%i}%s", start, end, boost::replace_all_copy(current.Text.get(), "\\N", "|")));
+    }
 }
diff --git a/src/subtitle_format_microdvd.h b/src/subtitle_format_microdvd.h
index 2ae7013d27f70e63bc44d06128686b7c8c74a1a5..501b5206ef394e58b90e4e5d0910069024f87fb1 100644
--- a/src/subtitle_format_microdvd.h
+++ b/src/subtitle_format_microdvd.h
@@ -36,13 +36,13 @@
 
 class MicroDVDSubtitleFormat final : public SubtitleFormat {
 public:
-	MicroDVDSubtitleFormat();
+    MicroDVDSubtitleFormat();
 
-	std::vector<std::string> GetReadWildcards() const override;
-	std::vector<std::string> GetWriteWildcards() const override;
+    std::vector<std::string> GetReadWildcards() const override;
+    std::vector<std::string> GetWriteWildcards() const override;
 
-	bool CanReadFile(agi::fs::path const& filename, std::string const& encoding) const override;
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
+    bool CanReadFile(agi::fs::path const &filename, std::string const &encoding) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
 
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_mkv.cpp b/src/subtitle_format_mkv.cpp
index ae4ac2893ed03813e7207f6e1bcb14395c22c342..f21ace247df75d8071080bd28c120c83fde7a477 100644
--- a/src/subtitle_format_mkv.cpp
+++ b/src/subtitle_format_mkv.cpp
@@ -37,18 +37,20 @@
 #include "mkv_wrap.h"
 
 MKVSubtitleFormat::MKVSubtitleFormat()
-: SubtitleFormat("Matroska")
+    : SubtitleFormat("Matroska")
 {
 }
 
-std::vector<std::string> MKVSubtitleFormat::GetReadWildcards() const {
-	std::vector<std::string> formats;
-	formats.push_back("mkv");
-	formats.push_back("mka");
-	formats.push_back("mks");
-	return formats;
+std::vector<std::string> MKVSubtitleFormat::GetReadWildcards() const
+{
+    std::vector<std::string> formats;
+    formats.push_back("mkv");
+    formats.push_back("mka");
+    formats.push_back("mks");
+    return formats;
 }
 
-void MKVSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const&) const {
-	MatroskaWrapper::GetSubtitles(filename, target);
+void MKVSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &) const
+{
+    MatroskaWrapper::GetSubtitles(filename, target);
 }
diff --git a/src/subtitle_format_mkv.h b/src/subtitle_format_mkv.h
index 207bd04f8599cdf09171b0f978e3a429d2dfefe9..779409e933553a327cf58a46068b70340c1dfd18 100644
--- a/src/subtitle_format_mkv.h
+++ b/src/subtitle_format_mkv.h
@@ -36,8 +36,8 @@
 
 class MKVSubtitleFormat final : public SubtitleFormat {
 public:
-	MKVSubtitleFormat();
-	std::vector<std::string> GetReadWildcards() const override;
+    MKVSubtitleFormat();
+    std::vector<std::string> GetReadWildcards() const override;
 
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
 };
diff --git a/src/subtitle_format_srt.cpp b/src/subtitle_format_srt.cpp
index 981137a6da9f68d46533a08a1559ecc6c1913e51..2767d858898e9d7d9b9c6c00d04069dd87135a6a 100644
--- a/src/subtitle_format_srt.cpp
+++ b/src/subtitle_format_srt.cpp
@@ -54,469 +54,465 @@ DEFINE_EXCEPTION(SRTParseError, SubtitleFormatParseError);
 
 namespace {
 enum class TagType {
-	UNKNOWN,
-	BOLD_OPEN,
-	BOLD_CLOSE,
-	ITALICS_OPEN,
-	ITALICS_CLOSE,
-	UNDERLINE_OPEN,
-	UNDERLINE_CLOSE,
-	STRIKEOUT_OPEN,
-	STRIKEOUT_CLOSE,
-	FONT_OPEN,
-	FONT_CLOSE
+    UNKNOWN,
+    BOLD_OPEN,
+    BOLD_CLOSE,
+    ITALICS_OPEN,
+    ITALICS_CLOSE,
+    UNDERLINE_OPEN,
+    UNDERLINE_CLOSE,
+    STRIKEOUT_OPEN,
+    STRIKEOUT_CLOSE,
+    FONT_OPEN,
+    FONT_CLOSE
 };
 
-TagType type_from_name(std::string const& tag) {
-	switch (tag.size()) {
-		case 1:
-			switch (tag[0]) {
-				case 'b': return TagType::BOLD_OPEN;
-				case 'i': return TagType::ITALICS_OPEN;
-				case 'u': return TagType::UNDERLINE_OPEN;
-				case 's': return TagType::STRIKEOUT_OPEN;
-				default: return TagType::UNKNOWN;
-			}
-		case 2:
-			if (tag[0] != '/') return TagType::UNKNOWN;
-			switch (tag[1]) {
-				case 'b': return TagType::BOLD_CLOSE;
-				case 'i': return TagType::ITALICS_CLOSE;
-				case 'u': return TagType::UNDERLINE_CLOSE;
-				case 's': return TagType::STRIKEOUT_CLOSE;
-				default: return TagType::UNKNOWN;
-			}
-		default:
-			if (tag == "font") return TagType::FONT_OPEN;
-			if (tag == "/font") return TagType::FONT_CLOSE;
-			return TagType::UNKNOWN;
-	}
+TagType type_from_name(std::string const &tag)
+{
+    switch (tag.size()) {
+    case 1:
+        switch (tag[0]) {
+        case 'b': return TagType::BOLD_OPEN;
+        case 'i': return TagType::ITALICS_OPEN;
+        case 'u': return TagType::UNDERLINE_OPEN;
+        case 's': return TagType::STRIKEOUT_OPEN;
+        default: return TagType::UNKNOWN;
+        }
+    case 2:
+        if (tag[0] != '/') return TagType::UNKNOWN;
+        switch (tag[1]) {
+        case 'b': return TagType::BOLD_CLOSE;
+        case 'i': return TagType::ITALICS_CLOSE;
+        case 'u': return TagType::UNDERLINE_CLOSE;
+        case 's': return TagType::STRIKEOUT_CLOSE;
+        default: return TagType::UNKNOWN;
+        }
+    default:
+        if (tag == "font") return TagType::FONT_OPEN;
+        if (tag == "/font") return TagType::FONT_CLOSE;
+        return TagType::UNKNOWN;
+    }
 }
 
 struct ToggleTag {
-	char tag;
-	int level = 0;
-
-	ToggleTag(char tag) : tag(tag) { }
-
-	void Open(std::string& out) {
-		if (level == 0) {
-			out += "{\\";
-			out += tag;
-			out += "1}";
-		}
-		++level;
-	}
-
-	void Close(std::string& out) {
-		if (level == 1) {
-			out += "{\\";
-			out += tag;
-			out += '}';
-		}
-		if (level > 0)
-			--level;
-	}
+    char tag;
+    int level = 0;
+
+    ToggleTag(char tag) : tag(tag) { }
+
+    void Open(std::string &out) {
+        if (level == 0) {
+            out += "{\\";
+            out += tag;
+            out += "1}";
+        }
+        ++level;
+    }
+
+    void Close(std::string &out) {
+        if (level == 1) {
+            out += "{\\";
+            out += tag;
+            out += '}';
+        }
+        if (level > 0)
+            --level;
+    }
 };
 
 class SrtTagParser {
-	struct FontAttribs {
-		std::string face;
-		std::string size;
-		std::string color;
-	};
+    struct FontAttribs {
+        std::string face;
+        std::string size;
+        std::string color;
+    };
 
-	const boost::regex tag_matcher;
-	const boost::regex attrib_matcher;
-	const boost::regex is_quoted;
+    const boost::regex tag_matcher;
+    const boost::regex attrib_matcher;
+    const boost::regex is_quoted;
 
 public:
-	SrtTagParser()
-	: tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", boost::regex::icase)
-	, attrib_matcher(R"(^[[:space:]]+(face|size|color)=('[^']*'|"[^"]*"|[^[:space:]]+))", boost::regex::icase)
-	, is_quoted(R"(^(['"]).*\1$)")
-	{
-	}
-
-	std::string ToAss(std::string srt)
-	{
-		ToggleTag bold('b');
-		ToggleTag italic('i');
-		ToggleTag underline('u');
-		ToggleTag strikeout('s');
-		std::vector<FontAttribs> font_stack;
-
-		std::string ass; // result to be built
-
-		while (!srt.empty())
-		{
-			boost::smatch result;
-			if (!regex_match(srt, result, tag_matcher))
-			{
-				// no more tags could be matched, end of string
-				ass.append(srt);
-				break;
-			}
-
-			// we found a tag, translate it
-			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;
-
-			boost::to_lower(tag_name);
-			switch (type_from_name(tag_name))
-			{
-			case TagType::BOLD_OPEN:       bold.Open(ass);       break;
-			case TagType::BOLD_CLOSE:      bold.Close(ass);      break;
-			case TagType::ITALICS_OPEN:    italic.Open(ass);     break;
-			case TagType::ITALICS_CLOSE:   italic.Close(ass);    break;
-			case TagType::UNDERLINE_OPEN:  underline.Open(ass);  break;
-			case TagType::UNDERLINE_CLOSE: underline.Close(ass); break;
-			case TagType::STRIKEOUT_OPEN:  strikeout.Open(ass);  break;
-			case TagType::STRIKEOUT_CLOSE: strikeout.Close(ass); break;
-			case TagType::FONT_OPEN:
-				{
-					// new attributes to fill in
-					FontAttribs new_attribs;
-					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
-					boost::smatch result;
-					while (regex_search(tag_attrs, result, attrib_matcher))
-					{
-						// get attribute name and values
-						std::string attr_name = result.str(1);
-						std::string attr_value = result.str(2);
-
-						// clean them
-						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 = agi::format("{\\fn%s}", attr_value);
-						else if (attr_name == "size")
-							new_attribs.size = agi::format("{\\fs%s}", attr_value);
-						else if (attr_name == "color")
-							new_attribs.color = agi::format("{\\c%s}", agi::Color(attr_value).GetAssOverrideFormatted());
-
-						// remove this attribute to prepare for the next
-						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);
-					if (new_attribs.size != old_attribs.size)
-						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);
-				}
-				break;
-			case TagType::FONT_CLOSE:
-				{
-					// this requires a font stack entry
-					if (font_stack.empty())
-						break;
-					// get the current attribs
-					FontAttribs cur_attribs = font_stack.back();
-					// remove them from the stack
-					font_stack.pop_back();
-					// grab the old attributes if there are any
-					FontAttribs old_attribs;
-					if (font_stack.size() > 0)
-						old_attribs = font_stack.back();
-					// then restore the attributes to previous settings
-					if (cur_attribs.face != old_attribs.face)
-					{
-						if (old_attribs.face.empty())
-							ass.append("{\\fn}");
-						else
-							ass.append(old_attribs.face);
-					}
-					if (cur_attribs.size != old_attribs.size)
-					{
-						if (old_attribs.size.empty())
-							ass.append("{\\fs}");
-						else
-							ass.append(old_attribs.size);
-					}
-					if (cur_attribs.color != old_attribs.color)
-					{
-						if (old_attribs.color.empty())
-							ass.append("{\\c}");
-						else
-							ass.append(old_attribs.color);
-					}
-				}
-				break;
-			default:
-				// unknown tag, replicate it in the output
-				ass.append("<").append(tag_name).append(tag_attrs).append(">");
-				break;
-			}
-		}
-
-		// make it a little prettier, join tag groups
-		boost::replace_all(ass, "}{", "");
-
-		return ass;
-	}
+    SrtTagParser()
+        : tag_matcher("^(.*?)<(/?b|/?i|/?u|/?s|/?font)([^>]*)>(.*)$", boost::regex::icase)
+        , attrib_matcher(R"(^[[:space:]]+(face|size|color)=('[^']*'|"[^"]*"|[^[:space:]]+))", boost::regex::icase)
+        , is_quoted(R"(^(['"]).*\1$)") {
+    }
+
+    std::string ToAss(std::string srt) {
+        ToggleTag bold('b');
+        ToggleTag italic('i');
+        ToggleTag underline('u');
+        ToggleTag strikeout('s');
+        std::vector<FontAttribs> font_stack;
+
+        std::string ass; // result to be built
+
+        while (!srt.empty()) {
+            boost::smatch result;
+            if (!regex_match(srt, result, tag_matcher)) {
+                // no more tags could be matched, end of string
+                ass.append(srt);
+                break;
+            }
+
+            // we found a tag, translate it
+            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;
+
+            boost::to_lower(tag_name);
+            switch (type_from_name(tag_name)) {
+            case TagType::BOLD_OPEN:       bold.Open(ass);       break;
+            case TagType::BOLD_CLOSE:      bold.Close(ass);      break;
+            case TagType::ITALICS_OPEN:    italic.Open(ass);     break;
+            case TagType::ITALICS_CLOSE:   italic.Close(ass);    break;
+            case TagType::UNDERLINE_OPEN:  underline.Open(ass);  break;
+            case TagType::UNDERLINE_CLOSE: underline.Close(ass); break;
+            case TagType::STRIKEOUT_OPEN:  strikeout.Open(ass);  break;
+            case TagType::STRIKEOUT_CLOSE: strikeout.Close(ass); break;
+            case TagType::FONT_OPEN: {
+                    // new attributes to fill in
+                    FontAttribs new_attribs;
+                    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
+                    boost::smatch result;
+                    while (regex_search(tag_attrs, result, attrib_matcher)) {
+                        // get attribute name and values
+                        std::string attr_name = result.str(1);
+                        std::string attr_value = result.str(2);
+
+                        // clean them
+                        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 = agi::format("{\\fn%s}", attr_value);
+                        else if (attr_name == "size")
+                            new_attribs.size = agi::format("{\\fs%s}", attr_value);
+                        else if (attr_name == "color")
+                            new_attribs.color = agi::format("{\\c%s}", agi::Color(attr_value).GetAssOverrideFormatted());
+
+                        // remove this attribute to prepare for the next
+                        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);
+                    if (new_attribs.size != old_attribs.size)
+                        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);
+                }
+                break;
+            case TagType::FONT_CLOSE: {
+                    // this requires a font stack entry
+                    if (font_stack.empty())
+                        break;
+                    // get the current attribs
+                    FontAttribs cur_attribs = font_stack.back();
+                    // remove them from the stack
+                    font_stack.pop_back();
+                    // grab the old attributes if there are any
+                    FontAttribs old_attribs;
+                    if (font_stack.size() > 0)
+                        old_attribs = font_stack.back();
+                    // then restore the attributes to previous settings
+                    if (cur_attribs.face != old_attribs.face) {
+                        if (old_attribs.face.empty())
+                            ass.append("{\\fn}");
+                        else
+                            ass.append(old_attribs.face);
+                    }
+                    if (cur_attribs.size != old_attribs.size) {
+                        if (old_attribs.size.empty())
+                            ass.append("{\\fs}");
+                        else
+                            ass.append(old_attribs.size);
+                    }
+                    if (cur_attribs.color != old_attribs.color) {
+                        if (old_attribs.color.empty())
+                            ass.append("{\\c}");
+                        else
+                            ass.append(old_attribs.color);
+                    }
+                }
+                break;
+            default:
+                // unknown tag, replicate it in the output
+                ass.append("<").append(tag_name).append(tag_attrs).append(">");
+                break;
+            }
+        }
+
+        // make it a little prettier, join tag groups
+        boost::replace_all(ass, "}{", "");
+
+        return ass;
+    }
 };
 
-std::string WriteSRTTime(agi::Time const& ts)
+std::string WriteSRTTime(agi::Time const &ts)
 {
-	return agi::format("%02d:%02d:%02d,%03d", ts.GetTimeHours(), ts.GetTimeMinutes(), ts.GetTimeSeconds(), ts.GetTimeMiliseconds());
+    return agi::format("%02d:%02d:%02d,%03d", ts.GetTimeHours(), ts.GetTimeMinutes(), ts.GetTimeSeconds(), ts.GetTimeMiliseconds());
 }
 
 }
 
 SRTSubtitleFormat::SRTSubtitleFormat()
-: SubtitleFormat("SubRip")
+    : SubtitleFormat("SubRip")
 {
 }
 
-std::vector<std::string> SRTSubtitleFormat::GetReadWildcards() const {
-	return {"srt"};
+std::vector<std::string> SRTSubtitleFormat::GetReadWildcards() const
+{
+    return {"srt"};
 }
 
-std::vector<std::string> SRTSubtitleFormat::GetWriteWildcards() const {
-	return GetReadWildcards();
+std::vector<std::string> SRTSubtitleFormat::GetWriteWildcards() const
+{
+    return GetReadWildcards();
 }
 
 enum class ParseState {
-	INITIAL,
-	TIMESTAMP,
-	FIRST_LINE_OF_BODY,
-	REST_OF_BODY,
-	LAST_WAS_BLANK
+    INITIAL,
+    TIMESTAMP,
+    FIRST_LINE_OF_BODY,
+    REST_OF_BODY,
+    LAST_WAS_BLANK
 };
 
-void SRTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	using namespace std;
-
-	TextFileReader file(filename, encoding);
-	target->LoadDefault(false, OPT_GET("Subtitle Format/SRT/Default Style Catalog")->GetString());
-
-	// See parsing algorithm at <http://devel.aegisub.org/wiki/SubtitleFormats/SRT>
-
-	// "hh:mm:ss,fff --> hh:mm:ss,fff" (e.g. "00:00:04,070 --> 00:00:10,04")
-	const boost::regex timestamp_regex("^([0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2},[0-9]{1,}) --> ([0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2},[0-9]{1,})");
-
-	SrtTagParser tag_parser;
-
-	ParseState state = ParseState::INITIAL;
-	int line_num = 0;
-	int linebreak_debt = 0;
-	AssDialogue *line = nullptr;
-	std::string text;
-	while (file.HasMoreLines()) {
-		std::string text_line = file.ReadLineFromFile();
-		++line_num;
-		boost::trim(text_line);
-
-		boost::smatch timestamp_match;
-		bool found_timestamps = false;
-		switch (state) {
-			case ParseState::INITIAL:
-				// ignore leading blank lines
-				if (text_line.empty()) break;
-				if (all(text_line, boost::is_digit())) {
-					// found the line number, throw it away and hope for timestamps
-					state = ParseState::TIMESTAMP;
-					break;
-				}
-				if (regex_search(text_line, timestamp_match, timestamp_regex)) {
-					found_timestamps = true;
-					break;
-				}
-
-				throw SRTParseError(agi::format("Parsing SRT: Expected subtitle index at line %d", line_num));
-
-			case ParseState::TIMESTAMP:
-				if (!regex_search(text_line, timestamp_match, timestamp_regex))
-					throw SRTParseError(agi::format("Parsing SRT: Expected timestamp pair at line %d", line_num));
-
-				found_timestamps = true;
-				break;
-
-			case ParseState::FIRST_LINE_OF_BODY:
-				if (text_line.empty()) {
-					// that's not very interesting... blank subtitle?
-					state = ParseState::LAST_WAS_BLANK;
-					// no previous line that needs a line break after
-					linebreak_debt = 0;
-					break;
-				}
-				text.append(text_line);
-				state = ParseState::REST_OF_BODY;
-				break;
-
-			case ParseState::REST_OF_BODY:
-				if (text_line.empty()) {
-					// Might be either the gap between two subtitles or just a
-					// blank line in the middle of a subtitle, so defer adding
-					// the line break until we check what's on the next line
-					state = ParseState::LAST_WAS_BLANK;
-					linebreak_debt = 1;
-					break;
-				}
-				text.append("\\N");
-				text.append(text_line);
-				break;
-
-			case ParseState::LAST_WAS_BLANK:
-				++linebreak_debt;
-				if (text_line.empty()) break;
-				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 = ParseState::TIMESTAMP;
-					break;
-				}
-				if (regex_search(text_line, timestamp_match, timestamp_regex)) {
-					found_timestamps = true;
-					break;
-				}
-
-				// 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);
-				state = ParseState::REST_OF_BODY;
-				break;
-		}
-		if (found_timestamps) {
-			if (line) {
-				// finalize active line
-				line->Text = tag_parser.ToAss(text);
-				text.clear();
-			}
-
-			// create new subtitle
-			line = new AssDialogue;
-			line->Start = timestamp_match.str(1);
-			line->End = timestamp_match.str(2);
-			// store pointer to subtitle, we'll continue working on it
-			target->Events.push_back(*line);
-			// next we're reading the text
-			state = ParseState::FIRST_LINE_OF_BODY;
-		}
-	}
-
-	if (state == ParseState::TIMESTAMP || state == ParseState::FIRST_LINE_OF_BODY)
-		throw SRTParseError("Parsing SRT: Incomplete file");
-
-	if (line) // an unfinalized line
-		line->Text = tag_parser.ToAss(text);
+void SRTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    using namespace std;
+
+    TextFileReader file(filename, encoding);
+    target->LoadDefault(false, OPT_GET("Subtitle Format/SRT/Default Style Catalog")->GetString());
+
+    // See parsing algorithm at <http://devel.aegisub.org/wiki/SubtitleFormats/SRT>
+
+    // "hh:mm:ss,fff --> hh:mm:ss,fff" (e.g. "00:00:04,070 --> 00:00:10,04")
+    const boost::regex timestamp_regex("^([0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2},[0-9]{1,}) --> ([0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2},[0-9]{1,})");
+
+    SrtTagParser tag_parser;
+
+    ParseState state = ParseState::INITIAL;
+    int line_num = 0;
+    int linebreak_debt = 0;
+    AssDialogue *line = nullptr;
+    std::string text;
+    while (file.HasMoreLines()) {
+        std::string text_line = file.ReadLineFromFile();
+        ++line_num;
+        boost::trim(text_line);
+
+        boost::smatch timestamp_match;
+        bool found_timestamps = false;
+        switch (state) {
+        case ParseState::INITIAL:
+            // ignore leading blank lines
+            if (text_line.empty()) break;
+            if (all(text_line, boost::is_digit())) {
+                // found the line number, throw it away and hope for timestamps
+                state = ParseState::TIMESTAMP;
+                break;
+            }
+            if (regex_search(text_line, timestamp_match, timestamp_regex)) {
+                found_timestamps = true;
+                break;
+            }
+
+            throw SRTParseError(agi::format("Parsing SRT: Expected subtitle index at line %d", line_num));
+
+        case ParseState::TIMESTAMP:
+            if (!regex_search(text_line, timestamp_match, timestamp_regex))
+                throw SRTParseError(agi::format("Parsing SRT: Expected timestamp pair at line %d", line_num));
+
+            found_timestamps = true;
+            break;
+
+        case ParseState::FIRST_LINE_OF_BODY:
+            if (text_line.empty()) {
+                // that's not very interesting... blank subtitle?
+                state = ParseState::LAST_WAS_BLANK;
+                // no previous line that needs a line break after
+                linebreak_debt = 0;
+                break;
+            }
+            text.append(text_line);
+            state = ParseState::REST_OF_BODY;
+            break;
+
+        case ParseState::REST_OF_BODY:
+            if (text_line.empty()) {
+                // Might be either the gap between two subtitles or just a
+                // blank line in the middle of a subtitle, so defer adding
+                // the line break until we check what's on the next line
+                state = ParseState::LAST_WAS_BLANK;
+                linebreak_debt = 1;
+                break;
+            }
+            text.append("\\N");
+            text.append(text_line);
+            break;
+
+        case ParseState::LAST_WAS_BLANK:
+            ++linebreak_debt;
+            if (text_line.empty()) break;
+            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 = ParseState::TIMESTAMP;
+                break;
+            }
+            if (regex_search(text_line, timestamp_match, timestamp_regex)) {
+                found_timestamps = true;
+                break;
+            }
+
+            // 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);
+            state = ParseState::REST_OF_BODY;
+            break;
+        }
+        if (found_timestamps) {
+            if (line) {
+                // finalize active line
+                line->Text = tag_parser.ToAss(text);
+                text.clear();
+            }
+
+            // create new subtitle
+            line = new AssDialogue;
+            line->Start = timestamp_match.str(1);
+            line->End = timestamp_match.str(2);
+            // store pointer to subtitle, we'll continue working on it
+            target->Events.push_back(*line);
+            // next we're reading the text
+            state = ParseState::FIRST_LINE_OF_BODY;
+        }
+    }
+
+    if (state == ParseState::TIMESTAMP || state == ParseState::FIRST_LINE_OF_BODY)
+        throw SRTParseError("Parsing SRT: Incomplete file");
+
+    if (line) // an unfinalized line
+        line->Text = tag_parser.ToAss(text);
 }
 
-void SRTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	TextFileWriter file(filename, encoding);
-
-	// Convert to SRT
-	AssFile copy(*src);
-	copy.Sort();
-	StripComments(copy);
-	RecombineOverlaps(copy);
-	MergeIdentical(copy);
+void SRTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    TextFileWriter file(filename, encoding);
+
+    // Convert to SRT
+    AssFile copy(*src);
+    copy.Sort();
+    StripComments(copy);
+    RecombineOverlaps(copy);
+    MergeIdentical(copy);
 #ifdef _WIN32
-	ConvertNewlines(copy, "\r\n", false);
+    ConvertNewlines(copy, "\r\n", false);
 #else
-	ConvertNewlines(copy, "\n", false);
+    ConvertNewlines(copy, "\n", false);
 #endif
 
-	// Write lines
-	int i=0;
-	for (auto const& current : copy.Events) {
-		file.WriteLineToFile(std::to_string(++i));
-		file.WriteLineToFile(WriteSRTTime(current.Start) + " --> " + WriteSRTTime(current.End));
-		file.WriteLineToFile(ConvertTags(&current));
-		file.WriteLineToFile("");
-	}
+    // Write lines
+    int i = 0;
+    for (auto const &current : copy.Events) {
+        file.WriteLineToFile(std::to_string(++i));
+        file.WriteLineToFile(WriteSRTTime(current.Start) + " --> " + WriteSRTTime(current.End));
+        file.WriteLineToFile(ConvertTags(&current));
+        file.WriteLineToFile("");
+    }
 }
 
-bool SRTSubtitleFormat::CanSave(const AssFile *file) const {
-	if (!file->Attachments.empty())
-		return false;
-
-	auto def = boost::flyweight<std::string>("Default");
-	for (auto const& line : file->Events) {
-		if (line.Style != def)
-			return false;
-
-		auto blocks = line.ParseTags();
-		for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
-			// Verify that all overrides used are supported
-			for (auto const& tag : ovr->Tags) {
-				if (tag.Name.size() != 2)
-					return false;
-				if (!strchr("bisu", tag.Name[1]))
-					return false;
-			}
-		}
-	}
-
-	return true;
+bool SRTSubtitleFormat::CanSave(const AssFile *file) const
+{
+    if (!file->Attachments.empty())
+        return false;
+
+    auto def = boost::flyweight<std::string>("Default");
+    for (auto const &line : file->Events) {
+        if (line.Style != def)
+            return false;
+
+        auto blocks = line.ParseTags();
+        for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
+            // Verify that all overrides used are supported
+            for (auto const &tag : ovr->Tags) {
+                if (tag.Name.size() != 2)
+                    return false;
+                if (!strchr("bisu", tag.Name[1]))
+                    return false;
+            }
+        }
+    }
+
+    return true;
 }
 
-std::string SRTSubtitleFormat::ConvertTags(const AssDialogue *diag) const {
-	struct tag_state { char tag; bool value; };
-	tag_state tag_states[] = {
-		{'b', false},
-		{'i', false},
-		{'s', false},
-		{'u', false}
-	};
-
-	std::string final;
-	for (auto& block : diag->ParseTags()) {
-		switch (block->GetType()) {
-		case AssBlockType::OVERRIDE:
-			for (auto const& tag : static_cast<AssDialogueBlockOverride&>(*block).Tags) {
-				if (!tag.IsValid() || tag.Name.size() != 2)
-					continue;
-				for (auto& state : tag_states) {
-					if (state.tag != tag.Name[1]) continue;
-
-					bool temp = tag.Params[0].Get(false);
-					if (temp && !state.value)
-						final += agi::format("<%c>", state.tag);
-					if (!temp && state.value)
-						final += agi::format("</%c>", state.tag);
-					state.value = temp;
-				}
-			}
-			break;
-		case AssBlockType::PLAIN:
-			final += block->GetText();
-			break;
-		case AssBlockType::DRAWING:
-		case AssBlockType::COMMENT:
-			break;
-		}
-	}
-
-	// Ensure all tags are closed
-	// Otherwise unclosed overrides might affect lines they shouldn't, see bug #809 for example
-	for (auto state : tag_states) {
-		if (state.value)
-			final += agi::format("</%c>", state.tag);
-	}
-
-	return final;
+std::string SRTSubtitleFormat::ConvertTags(const AssDialogue *diag) const
+{
+    struct tag_state { char tag; bool value; };
+    tag_state tag_states[] = {
+        {'b', false},
+        {'i', false},
+        {'s', false},
+        {'u', false}
+    };
+
+    std::string final;
+    for (auto &block : diag->ParseTags()) {
+        switch (block->GetType()) {
+        case AssBlockType::OVERRIDE:
+            for (auto const &tag : static_cast<AssDialogueBlockOverride &>(*block).Tags) {
+                if (!tag.IsValid() || tag.Name.size() != 2)
+                    continue;
+                for (auto &state : tag_states) {
+                    if (state.tag != tag.Name[1]) continue;
+
+                    bool temp = tag.Params[0].Get(false);
+                    if (temp && !state.value)
+                        final += agi::format("<%c>", state.tag);
+                    if (!temp && state.value)
+                        final += agi::format("</%c>", state.tag);
+                    state.value = temp;
+                }
+            }
+            break;
+        case AssBlockType::PLAIN:
+            final += block->GetText();
+            break;
+        case AssBlockType::DRAWING:
+        case AssBlockType::COMMENT:
+            break;
+        }
+    }
+
+    // Ensure all tags are closed
+    // Otherwise unclosed overrides might affect lines they shouldn't, see bug #809 for example
+    for (auto state : tag_states) {
+        if (state.value)
+            final += agi::format("</%c>", state.tag);
+    }
+
+    return final;
 }
diff --git a/src/subtitle_format_srt.h b/src/subtitle_format_srt.h
index 1947ed2ea9e223b624a679aae1026354d6797caa..7ee1c65cccfbda25d5be2a5e331e8cc568c7311d 100644
--- a/src/subtitle_format_srt.h
+++ b/src/subtitle_format_srt.h
@@ -37,14 +37,14 @@
 class AssDialogue;
 
 class SRTSubtitleFormat final : public SubtitleFormat {
-	std::string ConvertTags(const AssDialogue *diag) const;
+    std::string ConvertTags(const AssDialogue *diag) const;
 public:
-	SRTSubtitleFormat();
-	std::vector<std::string> GetReadWildcards() const override;
-	std::vector<std::string> GetWriteWildcards() const override;
+    SRTSubtitleFormat();
+    std::vector<std::string> GetReadWildcards() const override;
+    std::vector<std::string> GetWriteWildcards() const override;
 
-	bool CanSave(const AssFile *file) const override;
+    bool CanSave(const AssFile *file) const override;
 
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_ssa.cpp b/src/subtitle_format_ssa.cpp
index 623fbbc67ccfb8397d47f477f4119343a1d20bd8..5d76a33d812a8ae92169829f8cd2e741753bd183 100644
--- a/src/subtitle_format_ssa.cpp
+++ b/src/subtitle_format_ssa.cpp
@@ -30,63 +30,66 @@
 #include <boost/algorithm/string/replace.hpp>
 
 namespace {
-std::string replace_commas(std::string str) {
-	boost::replace_all(str, ",", ";");
-	return str;
+std::string replace_commas(std::string str)
+{
+    boost::replace_all(str, ",", ";");
+    return str;
 }
 
-std::string strip_newlines(std::string str) {
-	boost::replace_all(str, "\n", "");
-	boost::replace_all(str, "\r", "");
-	return str;
+std::string strip_newlines(std::string str)
+{
+    boost::replace_all(str, "\n", "");
+    boost::replace_all(str, "\r", "");
+    return str;
 }
 }
 
-void SsaSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const&, std::string const& encoding) const {
-	TextFileWriter file(filename, encoding);
+void SsaSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &, std::string const &encoding) const
+{
+    TextFileWriter file(filename, encoding);
 
-	file.WriteLineToFile("[Script Info]");
-	file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
-	file.WriteLineToFile("; http://www.aegisub.org/");
-	for (auto const& line : src->Info)
-		file.WriteLineToFile(boost::iequals(line.Key(), "scripttype") ? "ScriptType: v4.00" : line.GetEntryData());
+    file.WriteLineToFile("[Script Info]");
+    file.WriteLineToFile(std::string("; Script generated by Aegisub ") + GetAegisubLongVersionString());
+    file.WriteLineToFile("; http://www.aegisub.org/");
+    for (auto const &line : src->Info)
+        file.WriteLineToFile(boost::iequals(line.Key(), "scripttype") ? "ScriptType: v4.00" : line.GetEntryData());
 
-	file.WriteLineToFile("");
-	file.WriteLineToFile("[V4 Styles]");
-	file.WriteLineToFile("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
-	for (auto const& line : src->Styles)
-		file.WriteLineToFile(agi::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i"
-			, line.name, line.font, line.fontsize
-			, line.primary.GetSsaFormatted()
-			, line.secondary.GetSsaFormatted()
-			, line.shadow.GetSsaFormatted()
-			, (line.bold? -1 : 0), (line.italic ? -1 : 0)
-			, line.borderstyle, line.outline_w, line.shadow_w, AssStyle::AssToSsa(line.alignment)
-			, line.Margin[0], line.Margin[1], line.Margin[2], line.encoding));
+    file.WriteLineToFile("");
+    file.WriteLineToFile("[V4 Styles]");
+    file.WriteLineToFile("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
+    for (auto const &line : src->Styles)
+        file.WriteLineToFile(agi::format("Style: %s,%s,%g,%s,%s,0,%s,%d,%d,%d,%g,%g,%d,%d,%d,%d,0,%i"
+                                         , line.name, line.font, line.fontsize
+                                         , line.primary.GetSsaFormatted()
+                                         , line.secondary.GetSsaFormatted()
+                                         , line.shadow.GetSsaFormatted()
+                                         , (line.bold ? -1 : 0), (line.italic ? -1 : 0)
+                                         , line.borderstyle, line.outline_w, line.shadow_w, AssStyle::AssToSsa(line.alignment)
+                                         , line.Margin[0], line.Margin[1], line.Margin[2], line.encoding));
 
-	file.WriteLineToFile("");
-	file.WriteLineToFile("[Fonts]");
-	for (auto const& line : src->Attachments) {
-		if (line.Group() == AssEntryGroup::FONT)
-			file.WriteLineToFile(line.GetEntryData());
-	}
+    file.WriteLineToFile("");
+    file.WriteLineToFile("[Fonts]");
+    for (auto const &line : src->Attachments) {
+        if (line.Group() == AssEntryGroup::FONT)
+            file.WriteLineToFile(line.GetEntryData());
+    }
 
-	file.WriteLineToFile("");
-	file.WriteLineToFile("[Graphics]");
-	for (auto const& line : src->Attachments) {
-		if (line.Group() == AssEntryGroup::GRAPHIC)
-			file.WriteLineToFile(line.GetEntryData());
-	}
+    file.WriteLineToFile("");
+    file.WriteLineToFile("[Graphics]");
+    for (auto const &line : src->Attachments) {
+        if (line.Group() == AssEntryGroup::GRAPHIC)
+            file.WriteLineToFile(line.GetEntryData());
+    }
 
-	file.WriteLineToFile("");
-	file.WriteLineToFile("[Events]");
-	file.WriteLineToFile("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
-	for (auto const& line : src->Events)
-		file.WriteLineToFile(agi::format("%s: Marked=0,%s,%s,%s,%s,%d,%d,%d,%s,%s"
-			, (line.Comment ? "Comment" : "Dialogue")
-			, line.Start.GetAssFormatted(), line.End.GetAssFormatted()
-			, replace_commas(line.Style), replace_commas(line.Actor)
-			, line.Margin[0], line.Margin[1], line.Margin[2]
-			, replace_commas(line.Effect)
-			, strip_newlines(line.Text)));
+    file.WriteLineToFile("");
+    file.WriteLineToFile("[Events]");
+    file.WriteLineToFile("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text");
+    for (auto const &line : src->Events)
+        file.WriteLineToFile(agi::format("%s: Marked=0,%s,%s,%s,%s,%d,%d,%d,%s,%s"
+                                         , (line.Comment ? "Comment" : "Dialogue")
+                                         , line.Start.GetAssFormatted(), line.End.GetAssFormatted()
+                                         , replace_commas(line.Style), replace_commas(line.Actor)
+                                         , line.Margin[0], line.Margin[1], line.Margin[2]
+                                         , replace_commas(line.Effect)
+                                         , strip_newlines(line.Text)));
 }
diff --git a/src/subtitle_format_ssa.h b/src/subtitle_format_ssa.h
index 5083834d3774746c48a17ef403f95f345e9b794b..51687bd8323fc779c4c39d210cbdc09bc4668ebd 100644
--- a/src/subtitle_format_ssa.h
+++ b/src/subtitle_format_ssa.h
@@ -18,9 +18,9 @@
 
 class SsaSubtitleFormat final : public SubtitleFormat {
 public:
-	SsaSubtitleFormat() : SubtitleFormat("SubStation Alpha") { }
-	std::vector<std::string> GetWriteWildcards() const override { return {"ssa"}; }
-	/// @todo Not actually true
-	bool CanSave(const AssFile*) const override { return true; }
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    SsaSubtitleFormat() : SubtitleFormat("SubStation Alpha") { }
+    std::vector<std::string> GetWriteWildcards() const override { return {"ssa"}; }
+    /// @todo Not actually true
+    bool CanSave(const AssFile *) const override { return true; }
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_transtation.cpp b/src/subtitle_format_transtation.cpp
index 66196764a3ed975cc62b4be06448d543025f95b3..43a8cfb8db62da0f5caac541a728b4bb7133f96a 100644
--- a/src/subtitle_format_transtation.cpp
+++ b/src/subtitle_format_transtation.cpp
@@ -39,72 +39,75 @@
 #include <libaegisub/format.h>
 
 TranStationSubtitleFormat::TranStationSubtitleFormat()
-: SubtitleFormat("TranStation")
+    : SubtitleFormat("TranStation")
 {
 }
 
-std::vector<std::string> TranStationSubtitleFormat::GetWriteWildcards() const {
-	return {"transtation.txt"};
+std::vector<std::string> TranStationSubtitleFormat::GetWriteWildcards() const
+{
+    return {"transtation.txt"};
 }
 
-void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& vfps, std::string const& encoding) const {
-	auto fps = AskForFPS(false, true, vfps);
-	if (!fps.IsLoaded()) return;
-
-	// Convert to TranStation
-	AssFile copy(*src);
-	copy.Sort();
-	StripComments(copy);
-	RecombineOverlaps(copy);
-	MergeIdentical(copy);
-	StripTags(copy);
-	ConvertNewlines(copy, "\r\n");
-
-	agi::SmpteFormatter ft(fps);
-	TextFileWriter file(filename, encoding);
-	const AssDialogue *prev = nullptr;
-	for (auto const& cur : copy.Events) {
-		if (prev) {
-			file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, cur.Start));
-			file.WriteLineToFile("");
-		}
-
-		prev = &cur;
-	}
-
-	// flush last line
-	if (prev)
-		file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, -1));
-
-	// Every file must end with this line
-	file.WriteLineToFile("SUB[");
+void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &vfps, std::string const &encoding) const
+{
+    auto fps = AskForFPS(false, true, vfps);
+    if (!fps.IsLoaded()) return;
+
+    // Convert to TranStation
+    AssFile copy(*src);
+    copy.Sort();
+    StripComments(copy);
+    RecombineOverlaps(copy);
+    MergeIdentical(copy);
+    StripTags(copy);
+    ConvertNewlines(copy, "\r\n");
+
+    agi::SmpteFormatter ft(fps);
+    TextFileWriter file(filename, encoding);
+    const AssDialogue *prev = nullptr;
+    for (auto const &cur : copy.Events) {
+        if (prev) {
+            file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, cur.Start));
+            file.WriteLineToFile("");
+        }
+
+        prev = &cur;
+    }
+
+    // flush last line
+    if (prev)
+        file.WriteLineToFile(ConvertLine(&copy, prev, fps, ft, -1));
+
+    // Every file must end with this line
+    file.WriteLineToFile("SUB[");
 }
 
-std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialogue *current, agi::vfr::Framerate const& fps, agi::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(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";
-		if (style->alignment == 3 || style->alignment == 6 || style->alignment == 9) halign = "R";
-		if (style->italic) type = "I";
-	}
-
-	// Hack: If an italics-tag (\i1) appears anywhere in the line,
-	// make it all italics
-	if (current->Text.get().find("\\i1") != std::string::npos) type = "I";
-
-	// Write header
-	agi::Time end = current->End;
-
-	// Subtract one frame if the end time of the current line is equal to the
-	// start of next one, since the end timestamp is inclusive and the lines
-	// would overlap if left as is.
-	if (nextl_start > 0 && end == nextl_start)
-		end = fps.TimeAtFrame(fps.FrameAtTime(end, agi::vfr::END) - 1, agi::vfr::END);
-
-	std::string header = agi::format("SUB[%i%s%s %s>%s]\r\n", valign, halign, type, ft.ToSMPTE(current->Start), ft.ToSMPTE(end));
-	return header + current->Text.get();
+std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialogue *current, agi::vfr::Framerate const &fps, agi::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(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";
+        if (style->alignment == 3 || style->alignment == 6 || style->alignment == 9) halign = "R";
+        if (style->italic) type = "I";
+    }
+
+    // Hack: If an italics-tag (\i1) appears anywhere in the line,
+    // make it all italics
+    if (current->Text.get().find("\\i1") != std::string::npos) type = "I";
+
+    // Write header
+    agi::Time end = current->End;
+
+    // Subtract one frame if the end time of the current line is equal to the
+    // start of next one, since the end timestamp is inclusive and the lines
+    // would overlap if left as is.
+    if (nextl_start > 0 && end == nextl_start)
+        end = fps.TimeAtFrame(fps.FrameAtTime(end, agi::vfr::END) - 1, agi::vfr::END);
+
+    std::string header = agi::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/src/subtitle_format_transtation.h b/src/subtitle_format_transtation.h
index da4390b2937f51ada4b6e6d2953473a6a0cea361..b18492204c2633170eeff2c274bf46249da04c0c 100644
--- a/src/subtitle_format_transtation.h
+++ b/src/subtitle_format_transtation.h
@@ -33,10 +33,10 @@ class AssDialogue;
 namespace agi { class SmpteFormatter; }
 
 class TranStationSubtitleFormat final : public SubtitleFormat {
-	std::string ConvertLine(AssFile *file, const AssDialogue *line, agi::vfr::Framerate const& fps, agi::SmpteFormatter const& ft, int nextl_start) const;
+    std::string ConvertLine(AssFile *file, const AssDialogue *line, agi::vfr::Framerate const &fps, agi::SmpteFormatter const &ft, int nextl_start) const;
 
 public:
-	TranStationSubtitleFormat();
-	std::vector<std::string> GetWriteWildcards() const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    TranStationSubtitleFormat();
+    std::vector<std::string> GetWriteWildcards() const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_ttxt.cpp b/src/subtitle_format_ttxt.cpp
index 609f51bba8e8c9e184cf00db4c501b3138564d50..3f55ca68800157b759d58c37b12219dcd754f583 100644
--- a/src/subtitle_format_ttxt.cpp
+++ b/src/subtitle_format_ttxt.cpp
@@ -46,224 +46,233 @@
 DEFINE_EXCEPTION(TTXTParseError, SubtitleFormatParseError);
 
 TTXTSubtitleFormat::TTXTSubtitleFormat()
-: SubtitleFormat("MPEG-4 Streaming Text")
+    : SubtitleFormat("MPEG-4 Streaming Text")
 {
 }
 
-std::vector<std::string> TTXTSubtitleFormat::GetReadWildcards() const {
-	return {"ttxt"};
+std::vector<std::string> TTXTSubtitleFormat::GetReadWildcards() const
+{
+    return {"ttxt"};
 }
 
-std::vector<std::string> TTXTSubtitleFormat::GetWriteWildcards() const {
-	return GetReadWildcards();
+std::vector<std::string> TTXTSubtitleFormat::GetWriteWildcards() const
+{
+    return GetReadWildcards();
 }
 
-void TTXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	target->LoadDefault(false, OPT_GET("Subtitle Format/TTXT/Default Style Catalog")->GetString());
-
-	// Load XML document
-	wxXmlDocument doc;
-	if (!doc.Load(filename.wstring())) throw TTXTParseError("Failed loading TTXT XML file.");
-
-	// Check root node name
-	if (doc.GetRoot()->GetName() != "TextStream") throw TTXTParseError("Invalid TTXT file.");
-
-	// Check version
-	wxString verStr = doc.GetRoot()->GetAttribute("version", "");
-	int version = -1;
-	if (verStr == "1.0")
-		version = 0;
-	else if (verStr == "1.1")
-		version = 1;
-	else
-		throw TTXTParseError("Unknown TTXT version: " + from_wx(verStr));
-
-	// Get children
-	AssDialogue *diag = nullptr;
-	int lines = 0;
-	for (wxXmlNode *child = doc.GetRoot()->GetChildren(); child; child = child->GetNext()) {
-		// Line
-		if (child->GetName() == "TextSample") {
-			if ((diag = ProcessLine(child, diag, version))) {
-				lines++;
-				target->Events.push_back(*diag);
-			}
-		}
-		// Header
-		else if (child->GetName() == "TextStreamHeader") {
-			ProcessHeader(child);
-		}
-	}
-
-	// No lines?
-	if (lines == 0)
-		target->Events.push_back(*new AssDialogue);
+void TTXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    target->LoadDefault(false, OPT_GET("Subtitle Format/TTXT/Default Style Catalog")->GetString());
+
+    // Load XML document
+    wxXmlDocument doc;
+    if (!doc.Load(filename.wstring())) throw TTXTParseError("Failed loading TTXT XML file.");
+
+    // Check root node name
+    if (doc.GetRoot()->GetName() != "TextStream") throw TTXTParseError("Invalid TTXT file.");
+
+    // Check version
+    wxString verStr = doc.GetRoot()->GetAttribute("version", "");
+    int version = -1;
+    if (verStr == "1.0")
+        version = 0;
+    else if (verStr == "1.1")
+        version = 1;
+    else
+        throw TTXTParseError("Unknown TTXT version: " + from_wx(verStr));
+
+    // Get children
+    AssDialogue *diag = nullptr;
+    int lines = 0;
+    for (wxXmlNode *child = doc.GetRoot()->GetChildren(); child; child = child->GetNext()) {
+        // Line
+        if (child->GetName() == "TextSample") {
+            if ((diag = ProcessLine(child, diag, version))) {
+                lines++;
+                target->Events.push_back(*diag);
+            }
+        }
+        // Header
+        else if (child->GetName() == "TextStreamHeader") {
+            ProcessHeader(child);
+        }
+    }
+
+    // No lines?
+    if (lines == 0)
+        target->Events.push_back(*new AssDialogue);
 }
 
-AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const {
-	// Get time
-	wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000");
-	agi::Time time(from_wx(sampleTime));
-
-	// Set end time of last line
-	if (prev)
-		prev->End = time;
-
-	// Get text
-	wxString text;
-	if (version == 0)
-		text = node->GetAttribute("text", "");
-	else
-		text = node->GetNodeContent();
-
-	// Create line
-	if (text.empty()) return nullptr;
-
-	// Create dialogue
-	auto diag = new AssDialogue;
-	diag->Start = time;
-	diag->End = 36000000-10;
-
-	// Process text for 1.0
-	if (version == 0) {
-		wxString finalText;
-		finalText.reserve(text.size());
-		bool in = false;
-		bool first = true;
-		for (auto chr : text) {
-			if (chr == '\'') {
-				if (!in && !first) finalText += "\\N";
-				first = false;
-				in = !in;
-			}
-			else if (in) finalText += chr;
-		}
-		diag->Text = from_wx(finalText);
-	}
-
-	// Process text for 1.1
-	else {
-		text.Replace("\r", "");
-		text.Replace("\n", "\\N");
-		diag->Text = from_wx(text);
-	}
-
-	return diag;
+AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const
+{
+    // Get time
+    wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000");
+    agi::Time time(from_wx(sampleTime));
+
+    // Set end time of last line
+    if (prev)
+        prev->End = time;
+
+    // Get text
+    wxString text;
+    if (version == 0)
+        text = node->GetAttribute("text", "");
+    else
+        text = node->GetNodeContent();
+
+    // Create line
+    if (text.empty()) return nullptr;
+
+    // Create dialogue
+    auto diag = new AssDialogue;
+    diag->Start = time;
+    diag->End = 36000000 - 10;
+
+    // Process text for 1.0
+    if (version == 0) {
+        wxString finalText;
+        finalText.reserve(text.size());
+        bool in = false;
+        bool first = true;
+        for (auto chr : text) {
+            if (chr == '\'') {
+                if (!in && !first) finalText += "\\N";
+                first = false;
+                in = !in;
+            }
+            else if (in) finalText += chr;
+        }
+        diag->Text = from_wx(finalText);
+    }
+
+    // Process text for 1.1
+    else {
+        text.Replace("\r", "");
+        text.Replace("\n", "\\N");
+        diag->Text = from_wx(text);
+    }
+
+    return diag;
 }
 
-void TTXTSubtitleFormat::ProcessHeader(wxXmlNode *node) const {
-	// TODO
+void TTXTSubtitleFormat::ProcessHeader(wxXmlNode *node) const
+{
+    // TODO
 }
 
-void TTXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	// Convert to TTXT
-	AssFile copy(*src);
-	ConvertToTTXT(copy);
-
-	// Create XML structure
-	wxXmlDocument doc;
-	wxXmlNode *root = new wxXmlNode(nullptr, wxXML_ELEMENT_NODE, "TextStream");
-	root->AddAttribute("version", "1.1");
-	doc.SetRoot(root);
-
-	// Create header
-	WriteHeader(root);
-
-	// Create lines
-	const AssDialogue *prev = nullptr;
-	for (auto const& current : copy.Events) {
-		WriteLine(root, prev, &current);
-		prev = &current;
-	}
-
-	// Save XML
-	doc.Save(filename.wstring());
+void TTXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    // Convert to TTXT
+    AssFile copy(*src);
+    ConvertToTTXT(copy);
+
+    // Create XML structure
+    wxXmlDocument doc;
+    wxXmlNode *root = new wxXmlNode(nullptr, wxXML_ELEMENT_NODE, "TextStream");
+    root->AddAttribute("version", "1.1");
+    doc.SetRoot(root);
+
+    // Create header
+    WriteHeader(root);
+
+    // Create lines
+    const AssDialogue *prev = nullptr;
+    for (auto const &current : copy.Events) {
+        WriteLine(root, prev, &current);
+        prev = &current;
+    }
+
+    // Save XML
+    doc.Save(filename.wstring());
 }
 
-void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) const {
-	// Write stream header
-	wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextStreamHeader");
-	node->AddAttribute("width", "400");
-	node->AddAttribute("height", "60");
-	node->AddAttribute("layer", "0");
-	node->AddAttribute("translation_x", "0");
-	node->AddAttribute("translation_y", "0");
-	root->AddChild(node);
-	root = node;
-
-	// Write sample description
-	node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSampleDescription");
-	node->AddAttribute("horizontalJustification", "center");
-	node->AddAttribute("verticalJustification", "bottom");
-	node->AddAttribute("backColor", "0 0 0 0");
-	node->AddAttribute("verticalText", "no");
-	node->AddAttribute("fillTextRegion", "no");
-	node->AddAttribute("continuousKaraoke", "no");
-	node->AddAttribute("scroll", "None");
-	root->AddChild(node);
-	root = node;
-
-	// Write font table
-
-	node = new wxXmlNode(wxXML_ELEMENT_NODE, "FontTable");
-	root->AddChild(node);
-
-	wxXmlNode *subNode = new wxXmlNode(wxXML_ELEMENT_NODE, "FontTableEntry");
-	subNode->AddAttribute("fontName", "Sans");
-	subNode->AddAttribute("fontID", "1");
-	node->AddChild(subNode);
-
-	// Write text box
-	node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextBox");
-	node->AddAttribute("top", "0");
-	node->AddAttribute("left", "0");
-	node->AddAttribute("bottom", "60");
-	node->AddAttribute("right", "400");
-	root->AddChild(node);
-
-	// Write style
-	node = new wxXmlNode(wxXML_ELEMENT_NODE, "Style");
-	node->AddAttribute("styles", "Normal");
-	node->AddAttribute("fontID", "1");
-	node->AddAttribute("fontSize", "18");
-	node->AddAttribute("color", "ff ff ff ff");
-	root->AddChild(node);
+void TTXTSubtitleFormat::WriteHeader(wxXmlNode *root) const
+{
+    // Write stream header
+    wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextStreamHeader");
+    node->AddAttribute("width", "400");
+    node->AddAttribute("height", "60");
+    node->AddAttribute("layer", "0");
+    node->AddAttribute("translation_x", "0");
+    node->AddAttribute("translation_y", "0");
+    root->AddChild(node);
+    root = node;
+
+    // Write sample description
+    node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSampleDescription");
+    node->AddAttribute("horizontalJustification", "center");
+    node->AddAttribute("verticalJustification", "bottom");
+    node->AddAttribute("backColor", "0 0 0 0");
+    node->AddAttribute("verticalText", "no");
+    node->AddAttribute("fillTextRegion", "no");
+    node->AddAttribute("continuousKaraoke", "no");
+    node->AddAttribute("scroll", "None");
+    root->AddChild(node);
+    root = node;
+
+    // Write font table
+
+    node = new wxXmlNode(wxXML_ELEMENT_NODE, "FontTable");
+    root->AddChild(node);
+
+    wxXmlNode *subNode = new wxXmlNode(wxXML_ELEMENT_NODE, "FontTableEntry");
+    subNode->AddAttribute("fontName", "Sans");
+    subNode->AddAttribute("fontID", "1");
+    node->AddChild(subNode);
+
+    // Write text box
+    node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextBox");
+    node->AddAttribute("top", "0");
+    node->AddAttribute("left", "0");
+    node->AddAttribute("bottom", "60");
+    node->AddAttribute("right", "400");
+    root->AddChild(node);
+
+    // Write style
+    node = new wxXmlNode(wxXML_ELEMENT_NODE, "Style");
+    node->AddAttribute("styles", "Normal");
+    node->AddAttribute("fontID", "1");
+    node->AddAttribute("fontSize", "18");
+    node->AddAttribute("color", "ff ff ff ff");
+    root->AddChild(node);
 }
 
-void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, const AssDialogue *line) const {
-	// 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", to_wx("0" + prev->End.GetAssFormatted(true)));
-		node->AddAttribute("xml:space", "preserve");
-		root->AddChild(node);
-		node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", ""));
-	}
-
-	// Generate and insert node
-	wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample");
-	node->AddAttribute("sampleTime", to_wx("0" + line->Start.GetAssFormatted(true)));
-	node->AddAttribute("xml:space", "preserve");
-	root->AddChild(node);
-	node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", to_wx(line->Text)));
+void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, const AssDialogue *line) const
+{
+    // 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", to_wx("0" + prev->End.GetAssFormatted(true)));
+        node->AddAttribute("xml:space", "preserve");
+        root->AddChild(node);
+        node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", ""));
+    }
+
+    // Generate and insert node
+    wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample");
+    node->AddAttribute("sampleTime", to_wx("0" + line->Start.GetAssFormatted(true)));
+    node->AddAttribute("xml:space", "preserve");
+    root->AddChild(node);
+    node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", to_wx(line->Text)));
 }
 
-void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const {
-	file.Sort();
-	StripComments(file);
-	RecombineOverlaps(file);
-	MergeIdentical(file);
-	StripTags(file);
-	ConvertNewlines(file, "\r\n");
-
-	// Find last line
-	agi::Time lastTime;
-	if (!file.Events.empty())
-		lastTime = file.Events.back().End;
-
-	// Insert blank line at the end
-	auto diag = new AssDialogue;
-	diag->Start = lastTime;
-	diag->End = lastTime+OPT_GET("Timing/Default Duration")->GetInt();
-	file.Events.push_back(*diag);
+void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const
+{
+    file.Sort();
+    StripComments(file);
+    RecombineOverlaps(file);
+    MergeIdentical(file);
+    StripTags(file);
+    ConvertNewlines(file, "\r\n");
+
+    // Find last line
+    agi::Time lastTime;
+    if (!file.Events.empty())
+        lastTime = file.Events.back().End;
+
+    // Insert blank line at the end
+    auto diag = new AssDialogue;
+    diag->Start = lastTime;
+    diag->End = lastTime + OPT_GET("Timing/Default Duration")->GetInt();
+    file.Events.push_back(*diag);
 }
diff --git a/src/subtitle_format_ttxt.h b/src/subtitle_format_ttxt.h
index cde1aaef90e7b9f15eda4836d882bd0d97cd513e..28d16eebd51fdd97f47507b040f07d13bcdd9db9 100644
--- a/src/subtitle_format_ttxt.h
+++ b/src/subtitle_format_ttxt.h
@@ -38,19 +38,19 @@ class AssDialogue;
 class wxXmlNode;
 
 class TTXTSubtitleFormat final : public SubtitleFormat {
-	AssDialogue *ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const;
-	void ProcessHeader(wxXmlNode *node) const;
+    AssDialogue *ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const;
+    void ProcessHeader(wxXmlNode *node) const;
 
-	void WriteHeader(wxXmlNode *root) const;
-	void WriteLine(wxXmlNode *root, const AssDialogue *prev, const AssDialogue *line) const;
+    void WriteHeader(wxXmlNode *root) const;
+    void WriteLine(wxXmlNode *root, const AssDialogue *prev, const AssDialogue *line) const;
 
-	void ConvertToTTXT(AssFile &file) const;
+    void ConvertToTTXT(AssFile &file) const;
 
 public:
-	TTXTSubtitleFormat();
-	std::vector<std::string> GetReadWildcards() const override;
-	std::vector<std::string> GetWriteWildcards() const override;
+    TTXTSubtitleFormat();
+    std::vector<std::string> GetReadWildcards() const override;
+    std::vector<std::string> GetWriteWildcards() const override;
 
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitle_format_txt.cpp b/src/subtitle_format_txt.cpp
index 707dd76f819f640f52280010efff5a737c459424..694cf2f4cd3a56f158ff9ef61f8b7caeb3a580ee 100644
--- a/src/subtitle_format_txt.cpp
+++ b/src/subtitle_format_txt.cpp
@@ -46,112 +46,117 @@
 #include <boost/algorithm/string/trim.hpp>
 
 TXTSubtitleFormat::TXTSubtitleFormat()
-: SubtitleFormat("Plain-Text")
+    : SubtitleFormat("Plain-Text")
 {
 }
 
-std::vector<std::string> TXTSubtitleFormat::GetReadWildcards() const {
-	return {"txt"};
+std::vector<std::string> TXTSubtitleFormat::GetReadWildcards() const
+{
+    return {"txt"};
 }
 
-std::vector<std::string> TXTSubtitleFormat::GetWriteWildcards() const {
-	return GetReadWildcards();
+std::vector<std::string> TXTSubtitleFormat::GetWriteWildcards() const
+{
+    return GetReadWildcards();
 }
 
-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"));
+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, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	if (!ShowPlainTextImportDialog()) return;
-
-	TextFileReader file(filename, encoding, false);
-
-	target->LoadDefault(false, OPT_GET("Subtitle Format/TXT/Default Style Catalog")->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()) {
-		std::string value = file.ReadLineFromFile();
-		if (value.empty() && !OPT_GET("Tool/Import/Text/Include Blank")->GetBool()) continue;
-
-		// Check if this isn't a timecodes file
-		if (boost::starts_with(value, "# timecode"))
-			throw SubtitleFormatParseError("File is a timecode file, cannot load as subtitles.");
-
-		// Read comment data
-		bool isComment = false;
-		if (!comment.empty() && boost::starts_with(value, comment)) {
-			isComment = true;
-			value.erase(0, comment.size());
-		}
-
-		// Read actor data
-		if (!isComment && !separator.empty() && !value.empty()) {
-			if (value[0] != ' ' && value[0] != '\t') {
-				size_t pos = value.find(separator);
-				if (pos != std::string::npos) {
-					actor = value.substr(0, pos);
-					boost::trim(actor);
-					value.erase(0, pos + 1);
-				}
-			}
-		}
-
-		// Trim spaces at start
-		boost::trim_left(value);
-
-		if (value.empty())
-			isComment = true;
-
-		// Sets line up
-		auto line = new AssDialogue;
-		line->Actor = isComment ? std::string() : actor;
-		line->Comment = isComment;
-		line->Text = value;
-		line->End = 0;
-
-		target->Events.push_back(*line);
-	}
+void TXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    if (!ShowPlainTextImportDialog()) return;
+
+    TextFileReader file(filename, encoding, false);
+
+    target->LoadDefault(false, OPT_GET("Subtitle Format/TXT/Default Style Catalog")->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()) {
+        std::string value = file.ReadLineFromFile();
+        if (value.empty() && !OPT_GET("Tool/Import/Text/Include Blank")->GetBool()) continue;
+
+        // Check if this isn't a timecodes file
+        if (boost::starts_with(value, "# timecode"))
+            throw SubtitleFormatParseError("File is a timecode file, cannot load as subtitles.");
+
+        // Read comment data
+        bool isComment = false;
+        if (!comment.empty() && boost::starts_with(value, comment)) {
+            isComment = true;
+            value.erase(0, comment.size());
+        }
+
+        // Read actor data
+        if (!isComment && !separator.empty() && !value.empty()) {
+            if (value[0] != ' ' && value[0] != '\t') {
+                size_t pos = value.find(separator);
+                if (pos != std::string::npos) {
+                    actor = value.substr(0, pos);
+                    boost::trim(actor);
+                    value.erase(0, pos + 1);
+                }
+            }
+        }
+
+        // Trim spaces at start
+        boost::trim_left(value);
+
+        if (value.empty())
+            isComment = true;
+
+        // Sets line up
+        auto line = new AssDialogue;
+        line->Actor = isComment ? std::string() : actor;
+        line->Comment = isComment;
+        line->Text = value;
+        line->End = 0;
+
+        target->Events.push_back(*line);
+    }
 }
 
-void TXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const {
-	size_t num_actor_names = 0, num_dialogue_lines = 0;
+void TXTSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const
+{
+    size_t num_actor_names = 0, num_dialogue_lines = 0;
 
-	// Detect number of lines with Actor field filled out
-	for (auto const& dia : src->Events) {
-		if (!dia.Comment) {
-			num_dialogue_lines++;
-			if (!dia.Actor.get().empty())
-				num_actor_names++;
-		}
-	}
+    // Detect number of lines with Actor field filled out
+    for (auto const &dia : src->Events) {
+        if (!dia.Comment) {
+            num_dialogue_lines++;
+            if (!dia.Actor.get().empty())
+                num_actor_names++;
+        }
+    }
 
-	// If too few lines have Actor filled out, don't write it
-	bool write_actors = num_actor_names > num_dialogue_lines/2;
-	bool strip_formatting = true;
+    // If too few lines have Actor filled out, don't write it
+    bool write_actors = num_actor_names > num_dialogue_lines / 2;
+    bool strip_formatting = true;
 
-	TextFileWriter file(filename, encoding);
-	file.WriteLineToFile(std::string("# Exported by Aegisub ") + GetAegisubShortVersionString());
+    TextFileWriter file(filename, encoding);
+    file.WriteLineToFile(std::string("# Exported by Aegisub ") + GetAegisubShortVersionString());
 
-	// Write the file
-	for (auto const& dia : src->Events) {
-		std::string out_line;
+    // Write the file
+    for (auto const &dia : src->Events) {
+        std::string out_line;
 
-		if (dia.Comment)
-			out_line = "# ";
+        if (dia.Comment)
+            out_line = "# ";
 
-		if (write_actors)
-			out_line += dia.Actor.get() + ": ";
+        if (write_actors)
+            out_line += dia.Actor.get() + ": ";
 
-		std::string out_text = strip_formatting ? dia.GetStrippedText() : dia.Text;
-		out_line += out_text;
+        std::string out_text = strip_formatting ? dia.GetStrippedText() : dia.Text;
+        out_line += out_text;
 
-		if (!out_text.empty())
-			file.WriteLineToFile(out_line);
-	}
+        if (!out_text.empty())
+            file.WriteLineToFile(out_line);
+    }
 }
diff --git a/src/subtitle_format_txt.h b/src/subtitle_format_txt.h
index 838fd9367ff61c8461094534032493fd2b771dba..14709adeaf2ed09fb1c8668e04008e28afcb01b1 100644
--- a/src/subtitle_format_txt.h
+++ b/src/subtitle_format_txt.h
@@ -36,14 +36,14 @@
 
 class TXTSubtitleFormat final : public SubtitleFormat {
 public:
-	TXTSubtitleFormat();
-	std::vector<std::string> GetReadWildcards() const override;
-	std::vector<std::string> GetWriteWildcards() const override;
+    TXTSubtitleFormat();
+    std::vector<std::string> GetReadWildcards() const override;
+    std::vector<std::string> GetWriteWildcards() const override;
 
-	// TXT format supports so little that it should always require an export
-	bool CanSave(const AssFile*) const override { return false; }
+    // TXT format supports so little that it should always require an export
+    bool CanSave(const AssFile *) const override { return false; }
 
-	bool CanWriteFile(agi::fs::path const& filename) const override;
-	void ReadFile(AssFile *target, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& forceEncoding) const override;
-	void WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& fps, std::string const& encoding) const override;
+    bool CanWriteFile(agi::fs::path const &filename) const override;
+    void ReadFile(AssFile *target, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &forceEncoding) const override;
+    void WriteFile(const AssFile *src, agi::fs::path const &filename, agi::vfr::Framerate const &fps, std::string const &encoding) const override;
 };
diff --git a/src/subtitles_provider.cpp b/src/subtitles_provider.cpp
index 057bbbdbde398426bffc77aac044327bfa73570c..667dd218e20b6c8dc97d3b7808a42ab322f7b571 100644
--- a/src/subtitles_provider.cpp
+++ b/src/subtitles_provider.cpp
@@ -27,82 +27,86 @@
 #include "subtitles_provider_libass.h"
 
 namespace {
-	struct factory {
-		std::string name;
-		std::string subtype;
-		std::unique_ptr<SubtitlesProvider> (*create)(std::string const& subtype, agi::BackgroundRunner *br);
-		bool hidden;
-	};
+struct factory {
+    std::string name;
+    std::string subtype;
+    std::unique_ptr<SubtitlesProvider> (*create)(std::string const &subtype, agi::BackgroundRunner *br);
+    bool hidden;
+};
 
-	std::vector<factory> const& factories() {
-		static std::vector<factory> factories;
-		if (factories.size()) return factories;
+std::vector<factory> const &factories()
+{
+    static std::vector<factory> factories;
+    if (factories.size()) return factories;
 #ifdef WITH_CSRI
-		for (auto const& subtype : csri::List())
-			factories.push_back(factory{"CSRI/" + subtype, subtype, csri::Create, false});
+    for (auto const &subtype : csri::List())
+        factories.push_back(factory{"CSRI/" + subtype, subtype, csri::Create, false});
 #endif
-		factories.push_back(factory{"libass", "", libass::Create, false});
-		return factories;
-	}
+    factories.push_back(factory {"libass", "", libass::Create, false});
+    return factories;
+}
 }
 
-std::vector<std::string> SubtitlesProviderFactory::GetClasses() {
-	return ::GetClasses(factories());
+std::vector<std::string> SubtitlesProviderFactory::GetClasses()
+{
+    return ::GetClasses(factories());
 }
 
-std::unique_ptr<SubtitlesProvider> SubtitlesProviderFactory::GetProvider(agi::BackgroundRunner *br) {
-	auto preferred = OPT_GET("Subtitle/Provider")->GetString();
-	auto sorted = GetSorted(factories(), preferred);
+std::unique_ptr<SubtitlesProvider> SubtitlesProviderFactory::GetProvider(agi::BackgroundRunner *br)
+{
+    auto preferred = OPT_GET("Subtitle/Provider")->GetString();
+    auto sorted = GetSorted(factories(), preferred);
 
-	std::string error;
-	for (auto factory : sorted) {
-		try {
-			auto provider = factory->create(factory->subtype, br);
-			if (provider) return provider;
-		}
-		catch (agi::UserCancelException const&) { throw; }
-		catch (agi::Exception const& err) { error += factory->name + ": " + err.GetMessage() + "\n"; }
-		catch (...) { error += factory->name + ": Unknown error\n"; }
-	}
+    std::string error;
+    for (auto factory : sorted) {
+        try {
+            auto provider = factory->create(factory->subtype, br);
+            if (provider) return provider;
+        }
+        catch (agi::UserCancelException const &) { throw; }
+        catch (agi::Exception const &err) { error += factory->name + ": " + err.GetMessage() + "\n"; }
+        catch (...) { error += factory->name + ": Unknown error\n"; }
+    }
 
-	throw error;
+    throw error;
 }
 
-void SubtitlesProvider::LoadSubtitles(AssFile *subs, int time) {
-	buffer.clear();
+void SubtitlesProvider::LoadSubtitles(AssFile *subs, int time)
+{
+    buffer.clear();
 
-	auto push_header = [&](const char *str) {
-		buffer.insert(buffer.end(), str, str + strlen(str));
-	};
-	auto push_line = [&](std::string const& str) {
-		buffer.insert(buffer.end(), &str[0], &str[0] + str.size());
-		buffer.push_back('\n');
-	};
+    auto push_header = [&](const char *str) {
+        buffer.insert(buffer.end(), str, str + strlen(str));
+    };
+    auto push_line = [&](std::string const & str) {
+        buffer.insert(buffer.end(), &str[0], &str[0] + str.size());
+        buffer.push_back('\n');
+    };
 
-	push_header("\xEF\xBB\xBF[Script Info]\n");
-	for (auto const& line : subs->Info)
-		push_line(line.GetEntryData());
+    push_header("\xEF\xBB\xBF[Script Info]\n");
+    for (auto const &line : subs->Info)
+        push_line(line.GetEntryData());
 
-	push_header("[V4+ Styles]\n");
-	for (auto const& line : subs->Styles)
-		push_line(line.GetEntryData());
+    push_header("[V4+ Styles]\n");
+    for (auto const &line : subs->Styles)
+        push_line(line.GetEntryData());
 
-	if (!subs->Attachments.empty()) {
-		// TODO: some scripts may have a lot of attachments, 
-		// so ideally we'd want to write only those actually used on the requested video frame,
-		// but this would require some pre-parsing of the attached font files with FreeType,
-		// which isn't probably trivial.
-		push_header("[Fonts]\n");
-		for (auto const& attachment : subs->Attachments)
-			if (attachment.Group() == AssEntryGroup::FONT)
-				push_line(attachment.GetEntryData());
-	}
+    if (!subs->Attachments.empty()) {
+        // TODO: some scripts may have a lot of attachments,
+        // so ideally we'd want to write only those actually used on the requested video frame,
+        // but this would require some pre-parsing of the attached font files with FreeType,
+        // which isn't probably trivial.
+        push_header("[Fonts]\n");
+        for (auto const &attachment : subs->Attachments)
+            if (attachment.Group() == AssEntryGroup::FONT)
+                push_line(attachment.GetEntryData());
+    }
 
-	push_header("[Events]\n");
-	for (auto const& line : subs->Events) {
-		if (!line.Comment && (time < 0 || !(line.Start > time || line.End <= time)))
-			push_line(line.GetEntryData());
-	}
+    push_header("[Events]\n");
+    for (auto const &line : subs->Events) {
+        if (!line.Comment && (time < 0 || !(line.Start > time || line.End <= time)))
+            push_line(line.GetEntryData());
+    }
 
-	LoadSubtitles(&buffer[0], buffer.size());
+    LoadSubtitles(&buffer[0], buffer.size());
 }
diff --git a/src/subtitles_provider_csri.cpp b/src/subtitles_provider_csri.cpp
index 87429e213c34a202a22002e712ac8958d9009980..f61cf92d4748b8db143508cfd4f5cc17ddfef1ce 100644
--- a/src/subtitles_provider_csri.cpp
+++ b/src/subtitles_provider_csri.cpp
@@ -55,74 +55,78 @@ namespace {
 std::mutex csri_mutex;
 
 struct closer {
-	void operator()(csri_inst *inst) { if (inst) csri_close(inst); }
+    void operator()(csri_inst *inst) { if (inst) csri_close(inst); }
 };
 
 class CSRISubtitlesProvider final : public SubtitlesProvider {
-	std::unique_ptr<csri_inst, closer> instance;
-	csri_rend *renderer = nullptr;
+    std::unique_ptr<csri_inst, closer> instance;
+    csri_rend *renderer = nullptr;
 
-	void LoadSubtitles(const char *data, size_t len) override {
-		std::lock_guard<std::mutex> lock(csri_mutex);
-		instance.reset(csri_open_mem(renderer, data, len, nullptr));
-	}
+    void LoadSubtitles(const char *data, size_t len) override {
+        std::lock_guard<std::mutex> lock(csri_mutex);
+        instance.reset(csri_open_mem(renderer, data, len, nullptr));
+    }
 
 public:
-	CSRISubtitlesProvider(std::string subType);
+    CSRISubtitlesProvider(std::string subType);
 
-	void DrawSubtitles(VideoFrame &dst, double time) override;
+    void DrawSubtitles(VideoFrame &dst, double time) override;
 };
 
-CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type) {
-	std::lock_guard<std::mutex> lock(csri_mutex);
-	for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
-		if (type == csri_renderer_info(cur)->name) {
-			renderer = cur;
-			break;
-		}
-	}
-
-	if (!renderer)
-		throw agi::InternalError("CSRI renderer vanished between initial list and creation?");
+CSRISubtitlesProvider::CSRISubtitlesProvider(std::string type)
+{
+    std::lock_guard<std::mutex> lock(csri_mutex);
+    for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
+        if (type == csri_renderer_info(cur)->name) {
+            renderer = cur;
+            break;
+        }
+    }
+
+    if (!renderer)
+        throw agi::InternalError("CSRI renderer vanished between initial list and creation?");
 }
 
-void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst, double time) {
-	if (!instance) return;
-
-	csri_frame frame;
-	if (dst.flipped) {
-		frame.planes[0] = dst.data.data() + (dst.height-1) * dst.width * 4;
-		frame.strides[0] = -(signed)dst.width * 4;
-	}
-	else {
-		frame.planes[0] = dst.data.data();
-		frame.strides[0] = dst.width * 4;
-	}
-	frame.pixfmt = CSRI_F_BGR_;
-
-	csri_fmt format = { frame.pixfmt, dst.width, dst.height };
-
-	std::lock_guard<std::mutex> lock(csri_mutex);
-	if (!csri_request_fmt(instance.get(), &format))
-		csri_render(instance.get(), &frame, time);
+void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst, double time)
+{
+    if (!instance) return;
+
+    csri_frame frame;
+    if (dst.flipped) {
+        frame.planes[0] = dst.data.data() + (dst.height - 1) * dst.width * 4;
+        frame.strides[0] = -(signed)dst.width * 4;
+    }
+    else {
+        frame.planes[0] = dst.data.data();
+        frame.strides[0] = dst.width * 4;
+    }
+    frame.pixfmt = CSRI_F_BGR_;
+
+    csri_fmt format = { frame.pixfmt, dst.width, dst.height };
+
+    std::lock_guard<std::mutex> lock(csri_mutex);
+    if (!csri_request_fmt(instance.get(), &format))
+        csri_render(instance.get(), &frame, time);
 }
 }
 
 namespace csri {
-std::vector<std::string> List() {
-	std::vector<std::string> final;
-	for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
-		std::string name(csri_renderer_info(cur)->name);
-		if (name.find("aegisub") != name.npos)
-			final.insert(final.begin(), name);
-		else
-			final.push_back(name);
-	}
-	return final;
+std::vector<std::string> List()
+{
+    std::vector<std::string> final;
+    for (csri_rend *cur = csri_renderer_default(); cur; cur = csri_renderer_next(cur)) {
+        std::string name(csri_renderer_info(cur)->name);
+        if (name.find("aegisub") != name.npos)
+            final.insert(final.begin(), name);
+        else
+            final.push_back(name);
+    }
+    return final;
 }
 
-std::unique_ptr<SubtitlesProvider> Create(std::string const& name, agi::BackgroundRunner *) {
-	return agi::make_unique<CSRISubtitlesProvider>(name);
+std::unique_ptr<SubtitlesProvider> Create(std::string const &name, agi::BackgroundRunner *)
+{
+    return agi::make_unique<CSRISubtitlesProvider>(name);
 }
 }
 #endif // WITH_CSRI
diff --git a/src/subtitles_provider_csri.h b/src/subtitles_provider_csri.h
index ef5026f044fe6d1ffbc1117d101a140b4b0f1adb..e9eabb15355cf720735a24e995b105c678cd1785 100644
--- a/src/subtitles_provider_csri.h
+++ b/src/subtitles_provider_csri.h
@@ -22,6 +22,6 @@ class SubtitlesProvider;
 namespace agi { class BackgroundRunner; }
 
 namespace csri {
-	std::vector<std::string> List();
-	std::unique_ptr<SubtitlesProvider> Create(std::string const& subtype, agi::BackgroundRunner *br);
+std::vector<std::string> List();
+std::unique_ptr<SubtitlesProvider> Create(std::string const &subtype, agi::BackgroundRunner *br);
 }
diff --git a/src/subtitles_provider_libass.cpp b/src/subtitles_provider_libass.cpp
index f424c017760a6a1d999f771624c46c5751ce0f16..d4c5598d090e296d69cd5817b09096f68acee78e 100644
--- a/src/subtitles_provider_libass.cpp
+++ b/src/subtitles_provider_libass.cpp
@@ -65,102 +65,105 @@ namespace {
 std::unique_ptr<agi::dispatch::Queue> cache_queue;
 ASS_Library *library;
 
-void msg_callback(int level, const char *fmt, va_list args, void *) {
-	if (level >= 7) return;
-	char buf[1024];
+void msg_callback(int level, const char *fmt, va_list args, void *)
+{
+    if (level >= 7) return;
+    char buf[1024];
 #ifdef _WIN32
-	vsprintf_s(buf, sizeof(buf), fmt, args);
+    vsprintf_s(buf, sizeof(buf), fmt, args);
 #else
-	vsnprintf(buf, sizeof(buf), fmt, args);
+    vsnprintf(buf, sizeof(buf), fmt, args);
 #endif
 
-	if (level < 2) // warning/error
-		LOG_I("subtitle/provider/libass") << buf;
-	else // verbose
-		LOG_D("subtitle/provider/libass") << buf;
+    if (level < 2) // warning/error
+        LOG_I("subtitle/provider/libass") << buf;
+    else // verbose
+        LOG_D("subtitle/provider/libass") << buf;
 }
 
 // Stuff used on the cache thread, owned by a shared_ptr in case the provider
 // gets deleted before the cache finishing updating
 struct cache_thread_shared {
-	ASS_Renderer *renderer = nullptr;
-	std::atomic<bool> ready{false};
-	~cache_thread_shared() { if (renderer) ass_renderer_done(renderer); }
+    ASS_Renderer *renderer = nullptr;
+    std::atomic<bool> ready{false};
+    ~cache_thread_shared() { if (renderer) ass_renderer_done(renderer); }
 };
 
 class LibassSubtitlesProvider final : public SubtitlesProvider {
-	agi::BackgroundRunner *br;
-	std::shared_ptr<cache_thread_shared> shared;
-	ASS_Track* ass_track = nullptr;
-
-	ASS_Renderer *renderer() {
-		if (shared->ready)
-			return shared->renderer;
-
-		auto block = [&] {
-			if (shared->ready)
-				return;
-			agi::util::sleep_for(250);
-			if (shared->ready)
-				return;
-			br->Run([=](agi::ProgressSink *ps) {
-				ps->SetTitle(from_wx(_("Updating font index")));
-				ps->SetMessage(from_wx(_("This may take several minutes")));
-				ps->SetIndeterminate();
-				while (!shared->ready && !ps->IsCancelled())
-					agi::util::sleep_for(250);
-			});
-		};
-
-		if (wxThread::IsMain())
-			block();
-		else
-			agi::dispatch::Main().Sync(block);
-		return shared->renderer;
-	}
+    agi::BackgroundRunner *br;
+    std::shared_ptr<cache_thread_shared> shared;
+    ASS_Track *ass_track = nullptr;
+
+    ASS_Renderer *renderer() {
+        if (shared->ready)
+            return shared->renderer;
+
+        auto block = [&] {
+            if (shared->ready)
+                return;
+            agi::util::sleep_for(250);
+            if (shared->ready)
+                return;
+            br->Run([ = ](agi::ProgressSink * ps) {
+                ps->SetTitle(from_wx(_("Updating font index")));
+                ps->SetMessage(from_wx(_("This may take several minutes")));
+                ps->SetIndeterminate();
+                while (!shared->ready && !ps->IsCancelled())
+                    agi::util::sleep_for(250);
+            });
+        };
+
+        if (wxThread::IsMain())
+            block();
+        else
+            agi::dispatch::Main().Sync(block);
+        return shared->renderer;
+    }
 
 public:
-	LibassSubtitlesProvider(agi::BackgroundRunner *br);
-	~LibassSubtitlesProvider();
-
-	void LoadSubtitles(const char *data, size_t len) override {
-		if (ass_track) ass_free_track(ass_track);
-		ass_track = ass_read_memory(library, const_cast<char *>(data), len, nullptr);
-		if (!ass_track) throw agi::InternalError("libass failed to load subtitles.");
-	}
-
-	void DrawSubtitles(VideoFrame &dst, double time) override;
-
-	void Reinitialize() override {
-		// No need to reinit if we're not even done with the initial init
-		if (!shared->ready)
-			return;
-
-		ass_renderer_done(shared->renderer);
-		shared->renderer = ass_renderer_init(library);
-		ass_set_font_scale(shared->renderer, 1.);
-		ass_set_fonts(shared->renderer, nullptr, "Sans", 1, nullptr, true);
-	}
+    LibassSubtitlesProvider(agi::BackgroundRunner *br);
+    ~LibassSubtitlesProvider();
+
+    void LoadSubtitles(const char *data, size_t len) override {
+        if (ass_track) ass_free_track(ass_track);
+        ass_track = ass_read_memory(library, const_cast<char *>(data), len, nullptr);
+        if (!ass_track) throw agi::InternalError("libass failed to load subtitles.");
+    }
+
+    void DrawSubtitles(VideoFrame &dst, double time) override;
+
+    void Reinitialize() override {
+        // No need to reinit if we're not even done with the initial init
+        if (!shared->ready)
+            return;
+
+        ass_renderer_done(shared->renderer);
+        shared->renderer = ass_renderer_init(library);
+        ass_set_font_scale(shared->renderer, 1.);
+        ass_set_fonts(shared->renderer, nullptr, "Sans", 1, nullptr, true);
+    }
 };
 
 LibassSubtitlesProvider::LibassSubtitlesProvider(agi::BackgroundRunner *br)
-: br(br)
-, shared(std::make_shared<cache_thread_shared>())
+    : br(br)
+    , shared(std::make_shared<cache_thread_shared>())
 {
-	auto state = shared;
-	cache_queue->Async([state] {
-		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, nullptr, true);
-		}
-		state->renderer = ass_renderer;
-		state->ready = true;
-	});
+    auto state = shared;
+    cache_queue->Async([state] {
+        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, nullptr, true);
+        }
+        state->renderer = ass_renderer;
+        state->ready = true;
+    });
 }
 
-LibassSubtitlesProvider::~LibassSubtitlesProvider() {
-	if (ass_track) ass_free_track(ass_track);
+LibassSubtitlesProvider::~LibassSubtitlesProvider()
+{
+    if (ass_track) ass_free_track(ass_track);
 }
 
 #define _r(c) ((c)>>24)
@@ -168,62 +171,65 @@ LibassSubtitlesProvider::~LibassSubtitlesProvider() {
 #define _b(c) (((c)>>8)&0xFF)
 #define _a(c) ((c)&0xFF)
 
-void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) {
-	ass_set_frame_size(renderer(), frame.width, frame.height);
-
-	ASS_Image* img = ass_render_frame(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.
-
-	using namespace boost::gil;
-	auto dst = interleaved_view(frame.width, frame.height, (bgra8_pixel_t*)frame.data.data(), frame.width * 4);
-	if (frame.flipped)
-		dst = flipped_up_down_view(dst);
-
-	for (; img; img = img->next) {
-		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);
-
-		auto srcview = interleaved_view(img->w, img->h, (gray8_pixel_t*)img->bitmap, img->stride);
-		auto dstview = subimage_view(dst, img->dst_x, img->dst_y, img->w, img->h);
-
-		transform_pixels(dstview, srcview, dstview, [=](const bgra8_pixel_t frame, const gray8_pixel_t src) -> bgra8_pixel_t {
-			unsigned int k = ((unsigned)src) * opacity / 255;
-			unsigned int ck = 255 - k;
-
-			bgra8_pixel_t ret;
-			ret[0] = (k * b + ck * frame[0]) / 255;
-			ret[1] = (k * g + ck * frame[1]) / 255;
-			ret[2] = (k * r + ck * frame[2]) / 255;
-			ret[3] = 0;
-			return ret;
-		});
-	}
+void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame, double time)
+{
+    ass_set_frame_size(renderer(), frame.width, frame.height);
+
+    ASS_Image *img = ass_render_frame(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.
+
+    using namespace boost::gil;
+    auto dst = interleaved_view(frame.width, frame.height, (bgra8_pixel_t *)frame.data.data(), frame.width * 4);
+    if (frame.flipped)
+        dst = flipped_up_down_view(dst);
+
+    for (; img; img = img->next) {
+        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);
+
+        auto srcview = interleaved_view(img->w, img->h, (gray8_pixel_t *)img->bitmap, img->stride);
+        auto dstview = subimage_view(dst, img->dst_x, img->dst_y, img->w, img->h);
+
+        transform_pixels(dstview, srcview, dstview, [ = ](const bgra8_pixel_t frame, const gray8_pixel_t src) -> bgra8_pixel_t {
+            unsigned int k = ((unsigned)src) * opacity / 255;
+            unsigned int ck = 255 - k;
+
+            bgra8_pixel_t ret;
+            ret[0] = (k * b + ck *frame[0]) / 255;
+            ret[1] = (k * g + ck *frame[1]) / 255;
+            ret[2] = (k * r + ck *frame[2]) / 255;
+            ret[3] = 0;
+            return ret;
+        });
+    }
 }
 }
 
 namespace libass {
-std::unique_ptr<SubtitlesProvider> Create(std::string const&, agi::BackgroundRunner *br) {
-	return agi::make_unique<LibassSubtitlesProvider>(br);
+std::unique_ptr<SubtitlesProvider> Create(std::string const &, agi::BackgroundRunner *br)
+{
+    return agi::make_unique<LibassSubtitlesProvider>(br);
 }
 
-void CacheFonts() {
-	// 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, nullptr, true);
-		ass_renderer_done(ass_renderer);
-	});
+void CacheFonts()
+{
+    // 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, nullptr, true);
+        ass_renderer_done(ass_renderer);
+    });
 }
 }
diff --git a/src/subtitles_provider_libass.h b/src/subtitles_provider_libass.h
index 8df3335954fba2326e2dc1a1d83cc3850ca60754..f92559f98eebcd0987457283e64e0d59392a09b0 100644
--- a/src/subtitles_provider_libass.h
+++ b/src/subtitles_provider_libass.h
@@ -21,6 +21,6 @@ class SubtitlesProvider;
 namespace agi { class BackgroundRunner; }
 
 namespace libass {
-	std::unique_ptr<SubtitlesProvider> Create(std::string const&, agi::BackgroundRunner *br);
-	void CacheFonts();
+std::unique_ptr<SubtitlesProvider> Create(std::string const &, agi::BackgroundRunner *br);
+void CacheFonts();
 }
diff --git a/src/text_file_reader.cpp b/src/text_file_reader.cpp
index 972526a36abac2faae5a55af8f7da9c959a73196..8f8a3f9cc22242311c37476e2796f7cc8cd19231 100644
--- a/src/text_file_reader.cpp
+++ b/src/text_file_reader.cpp
@@ -23,23 +23,25 @@
 #include <boost/algorithm/string/trim.hpp>
 #include <boost/interprocess/streams/bufferstream.hpp>
 
-TextFileReader::TextFileReader(agi::fs::path const& filename, std::string encoding, bool trim)
-: file(agi::make_unique<agi::read_file_mapping>(filename))
-, stream(agi::make_unique<boost::interprocess::ibufferstream>(file->read(), file->size()))
-, trim(trim)
-, iter(agi::line_iterator<std::string>(*stream, encoding))
+TextFileReader::TextFileReader(agi::fs::path const &filename, std::string encoding, bool trim)
+    : file(agi::make_unique<agi::read_file_mapping>(filename))
+    , stream(agi::make_unique<boost::interprocess::ibufferstream>(file->read(), file->size()))
+    , trim(trim)
+    , iter(agi::line_iterator<std::string>(*stream, encoding))
 {
 }
 
-TextFileReader::~TextFileReader() {
+TextFileReader::~TextFileReader()
+{
 }
 
-std::string TextFileReader::ReadLineFromFile() {
-	std::string str = *iter;
-	++iter;
-	if (trim)
-		boost::trim(str);
-	if (boost::starts_with(str, "\xEF\xBB\xBF"))
-		str.erase(0, 3);
-	return str;
+std::string TextFileReader::ReadLineFromFile()
+{
+    std::string str = *iter;
+    ++iter;
+    if (trim)
+        boost::trim(str);
+    if (boost::starts_with(str, "\xEF\xBB\xBF"))
+        str.erase(0, 3);
+    return str;
 }
diff --git a/src/text_file_reader.h b/src/text_file_reader.h
index 4f948a233a7a6cb6f1a0412e298e8312862224fc..cc557417e180380c343b07d48edac2b90b0b7bcd 100644
--- a/src/text_file_reader.h
+++ b/src/text_file_reader.h
@@ -26,23 +26,23 @@ namespace agi { class read_file_mapping; }
 /// @class TextFileReader
 /// @brief A line-based text file reader
 class TextFileReader {
-	std::unique_ptr<agi::read_file_mapping> file;
-	std::unique_ptr<std::istream> stream;
-	bool trim;
-	agi::line_iterator<std::string> iter;
+    std::unique_ptr<agi::read_file_mapping> file;
+    std::unique_ptr<std::istream> stream;
+    bool trim;
+    agi::line_iterator<std::string> iter;
 
 public:
-	/// @brief Constructor
-	/// @param filename File to open
-	/// @param enc      Encoding to use, or empty to autodetect
-	/// @param trim     Whether to trim whitespace from lines read
-	TextFileReader(agi::fs::path const& filename, std::string encoding, bool trim=true);
-	/// @brief Destructor
-	~TextFileReader();
+    /// @brief Constructor
+    /// @param filename File to open
+    /// @param enc      Encoding to use, or empty to autodetect
+    /// @param trim     Whether to trim whitespace from lines read
+    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
-	std::string ReadLineFromFile();
-	/// @brief Check if there are any more lines to read
-	bool HasMoreLines() const { return iter != agi::line_iterator<std::string>(); }
+    /// @brief Read a line from the file
+    /// @return The line, possibly trimmed
+    std::string ReadLineFromFile();
+    /// @brief Check if there are any more lines to read
+    bool HasMoreLines() const { return iter != agi::line_iterator<std::string>(); }
 };
diff --git a/src/text_file_writer.cpp b/src/text_file_writer.cpp
index c4de4c0776dae3518fe93155810c412a1c0f281e..614b8208018ba4b2871bc6547120a6ce783f67b6 100644
--- a/src/text_file_writer.cpp
+++ b/src/text_file_writer.cpp
@@ -29,37 +29,39 @@
 
 #include <boost/algorithm/string/case_conv.hpp>
 
-TextFileWriter::TextFileWriter(agi::fs::path const& filename, std::string encoding)
-: file(new agi::io::Save(filename, true))
+TextFileWriter::TextFileWriter(agi::fs::path const &filename, std::string encoding)
+    : file(new agi::io::Save(filename, true))
 {
-	if (encoding.empty())
-		encoding = OPT_GET("App/Save Charset")->GetString();
-	if (encoding != "utf-8" && encoding != "UTF-8") {
-		conv = agi::make_unique<agi::charset::IconvWrapper>("utf-8", encoding.c_str(), true);
-		newline = conv->Convert(newline);
-	}
+    if (encoding.empty())
+        encoding = OPT_GET("App/Save Charset")->GetString();
+    if (encoding != "utf-8" && encoding != "UTF-8") {
+        conv = agi::make_unique<agi::charset::IconvWrapper>("utf-8", encoding.c_str(), true);
+        newline = conv->Convert(newline);
+    }
 
-	try {
-		// 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
-	}
+    try {
+        // 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
+    }
 }
 
-TextFileWriter::~TextFileWriter() {
-	// Explicit empty destructor required with a unique_ptr to an incomplete class
+TextFileWriter::~TextFileWriter()
+{
+    // Explicit empty destructor required with a unique_ptr to an incomplete class
 }
 
-void TextFileWriter::WriteLineToFile(std::string const& line, bool addLineBreak) {
-	if (conv) {
-		auto converted = conv->Convert(line);
-		file->Get().write(converted.data(), converted.size());
-	}
-	else
-		file->Get().write(line.data(), line.size());
+void TextFileWriter::WriteLineToFile(std::string const &line, bool addLineBreak)
+{
+    if (conv) {
+        auto converted = conv->Convert(line);
+        file->Get().write(converted.data(), converted.size());
+    }
+    else
+        file->Get().write(line.data(), line.size());
 
-	if (addLineBreak)
-		file->Get().write(newline.data(), newline.size());
+    if (addLineBreak)
+        file->Get().write(newline.data(), newline.size());
 }
diff --git a/src/text_file_writer.h b/src/text_file_writer.h
index 8f98b79488b1f0aed694dcd12993d9794a787a41..d9580a10ac5b543a38f3224c451d3a2b9093181f 100644
--- a/src/text_file_writer.h
+++ b/src/text_file_writer.h
@@ -25,22 +25,22 @@
 #include <libaegisub/fs_fwd.h>
 
 namespace agi {
-	namespace charset { class IconvWrapper; }
-	namespace io { class Save; }
+namespace charset { class IconvWrapper; }
+namespace io { class Save; }
 }
 
 class TextFileWriter {
-	std::unique_ptr<agi::io::Save> file;
-	std::unique_ptr<agi::charset::IconvWrapper> conv;
+    std::unique_ptr<agi::io::Save> file;
+    std::unique_ptr<agi::charset::IconvWrapper> conv;
 #ifdef _WIN32
-	std::string newline = "\r\n";
+    std::string newline = "\r\n";
 #else
-	std::string newline = "\n";
+    std::string newline = "\n";
 #endif
 
 public:
-	TextFileWriter(agi::fs::path const& filename, std::string encoding="");
-	~TextFileWriter();
+    TextFileWriter(agi::fs::path const &filename, std::string encoding = "");
+    ~TextFileWriter();
 
-	void WriteLineToFile(std::string const& line, bool addLineBreak=true);
+    void WriteLineToFile(std::string const &line, bool addLineBreak = true);
 };
diff --git a/src/text_selection_controller.cpp b/src/text_selection_controller.cpp
index deefa65734ce6da2f16ebfd3eab2142c46cc39d6..76799ecd0c24df4c5bd2117091b23ef9e2648bc4 100644
--- a/src/text_selection_controller.cpp
+++ b/src/text_selection_controller.cpp
@@ -18,14 +18,16 @@
 
 #include <wx/stc/stc.h>
 
-void TextSelectionController::SetControl(wxStyledTextCtrl *ctrl) {
-	this->ctrl = ctrl;
-	if (ctrl)
-		ctrl->Bind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this);
+void TextSelectionController::SetControl(wxStyledTextCtrl *ctrl)
+{
+    this->ctrl = ctrl;
+    if (ctrl)
+        ctrl->Bind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this);
 }
 
-TextSelectionController::~TextSelectionController() {
-	if (ctrl) ctrl->Unbind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this);
+TextSelectionController::~TextSelectionController()
+{
+    if (ctrl) ctrl->Unbind(wxEVT_STC_UPDATEUI, &TextSelectionController::UpdateUI, this);
 }
 
 #define GET(var, new_value) do { \
@@ -43,33 +45,36 @@ TextSelectionController::~TextSelectionController() {
 	}                                    \
 } while (false)
 
-void TextSelectionController::UpdateUI(wxStyledTextEvent &evt) {
-	if (changing) return;
+void TextSelectionController::UpdateUI(wxStyledTextEvent &evt)
+{
+    if (changing) return;
 
-	bool changed = false;
-	GET(insertion_point, ctrl->GetInsertionPoint());
-	if (evt.GetUpdated() & wxSTC_UPDATE_SELECTION) {
-		GET(selection_start, ctrl->GetSelectionStart());
-		GET(selection_end, ctrl->GetSelectionEnd());
-	}
-	else {
-		GET(selection_start, insertion_point);
-		GET(selection_end, insertion_point);
-	}
-	if (changed) AnnounceSelectionChanged();
+    bool changed = false;
+    GET(insertion_point, ctrl->GetInsertionPoint());
+    if (evt.GetUpdated() & wxSTC_UPDATE_SELECTION) {
+        GET(selection_start, ctrl->GetSelectionStart());
+        GET(selection_end, ctrl->GetSelectionEnd());
+    }
+    else {
+        GET(selection_start, insertion_point);
+        GET(selection_end, insertion_point);
+    }
+    if (changed) AnnounceSelectionChanged();
 }
 
-void TextSelectionController::SetInsertionPoint(int position) {
-	changing = true;
-	SET(insertion_point, position, SetInsertionPoint);
-	changing = false;
-	AnnounceSelectionChanged();
+void TextSelectionController::SetInsertionPoint(int position)
+{
+    changing = true;
+    SET(insertion_point, position, SetInsertionPoint);
+    changing = false;
+    AnnounceSelectionChanged();
 }
 
-void TextSelectionController::SetSelection(int start, int end) {
-	changing = true;
-	SET(selection_start, start, SetSelectionStart);
-	SET(selection_end, end, SetSelectionEnd);
-	changing = false;
-	AnnounceSelectionChanged();
+void TextSelectionController::SetSelection(int start, int end)
+{
+    changing = true;
+    SET(selection_start, start, SetSelectionStart);
+    SET(selection_end, end, SetSelectionEnd);
+    changing = false;
+    AnnounceSelectionChanged();
 }
diff --git a/src/text_selection_controller.h b/src/text_selection_controller.h
index be6949ef46144ab64daf8d503988d06133977e4a..1ef36aee24e17d0e42621946abf7e4e65d2f268b 100644
--- a/src/text_selection_controller.h
+++ b/src/text_selection_controller.h
@@ -20,27 +20,27 @@ class wxStyledTextCtrl;
 class wxStyledTextEvent;
 
 class TextSelectionController {
-	int selection_start = 0;
-	int selection_end = 0;
-	int insertion_point = 0;
-	bool changing = false;
+    int selection_start = 0;
+    int selection_end = 0;
+    int insertion_point = 0;
+    bool changing = false;
 
-	wxStyledTextCtrl *ctrl = nullptr;
+    wxStyledTextCtrl *ctrl = nullptr;
 
-	void UpdateUI(wxStyledTextEvent &evt);
+    void UpdateUI(wxStyledTextEvent &evt);
 
-	agi::signal::Signal<> AnnounceSelectionChanged;
+    agi::signal::Signal<> AnnounceSelectionChanged;
 
 public:
-	void SetSelection(int start, int end);
-	void SetInsertionPoint(int point);
+    void SetSelection(int start, int end);
+    void SetInsertionPoint(int point);
 
-	int GetSelectionStart() const { return selection_start; }
-	int GetSelectionEnd() const { return selection_end; }
-	int GetInsertionPoint() const { return insertion_point; }
+    int GetSelectionStart() const { return selection_start; }
+    int GetSelectionEnd() const { return selection_end; }
+    int GetInsertionPoint() const { return insertion_point; }
 
-	void SetControl(wxStyledTextCtrl *ctrl);
-	~TextSelectionController();
+    void SetControl(wxStyledTextCtrl *ctrl);
+    ~TextSelectionController();
 
-	DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionListener)
+    DEFINE_SIGNAL_ADDERS(AnnounceSelectionChanged, AddSelectionListener)
 };
diff --git a/src/thesaurus.cpp b/src/thesaurus.cpp
index 415c59ed4fa82b052ced02c7143475fa8f231fa9..27db11eb36f8669ad248d407c99cac28b1bb7afb 100644
--- a/src/thesaurus.cpp
+++ b/src/thesaurus.cpp
@@ -35,89 +35,99 @@
 #include <boost/range/algorithm.hpp>
 
 Thesaurus::Thesaurus()
-: lang_listener(OPT_SUB("Tool/Thesaurus/Language", &Thesaurus::OnLanguageChanged, this))
-, dict_path_listener(OPT_SUB("Path/Dictionary", &Thesaurus::OnPathChanged, this))
+    : lang_listener(OPT_SUB("Tool/Thesaurus/Language", &Thesaurus::OnLanguageChanged, this))
+    , dict_path_listener(OPT_SUB("Path/Dictionary", &Thesaurus::OnPathChanged, this))
 {
-	OnLanguageChanged();
+    OnLanguageChanged();
 }
 
-Thesaurus::~Thesaurus() {
-	if (cancel_load) *cancel_load = true;
+Thesaurus::~Thesaurus()
+{
+    if (cancel_load) *cancel_load = true;
 }
 
-std::vector<Thesaurus::Entry> Thesaurus::Lookup(std::string word) {
-	if (!impl) return {};
-	boost::to_lower(word);
-	return impl->Lookup(word);
+std::vector<Thesaurus::Entry> Thesaurus::Lookup(std::string word)
+{
+    if (!impl) return {};
+    boost::to_lower(word);
+    return impl->Lookup(word);
 }
 
-static std::vector<std::string> langs(const char *ext) {
-	std::vector<std::string> paths;
-	auto data_path = config::path->Decode("?data/dictionaries/");
-	auto user_path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString());
+static std::vector<std::string> langs(const char *ext)
+{
+    std::vector<std::string> paths;
+    auto data_path = config::path->Decode("?data/dictionaries/");
+    auto user_path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString());
 
-	auto filter = std::string("th_*.") + ext;
-	agi::fs::DirectoryIterator(data_path, filter).GetAll(paths);
-	agi::fs::DirectoryIterator(user_path, filter).GetAll(paths);
+    auto filter = std::string("th_*.") + ext;
+    agi::fs::DirectoryIterator(data_path, filter).GetAll(paths);
+    agi::fs::DirectoryIterator(user_path, filter).GetAll(paths);
 
-	// Drop extensions and the th_ prefix
-	for (auto& fn : paths) fn = fn.substr(3, fn.size() - filter.size() + 1);
+    // Drop extensions and the th_ prefix
+    for (auto &fn : paths) fn = fn.substr(3, fn.size() - filter.size() + 1);
 
-	boost::sort(paths);
-	paths.erase(unique(begin(paths), end(paths)), end(paths));
+    boost::sort(paths);
+    paths.erase(unique(begin(paths), end(paths)), end(paths));
 
-	return paths;
+    return paths;
 }
 
-std::vector<std::string> Thesaurus::GetLanguageList() const {
-	if (languages.empty())
-		boost::set_intersection(langs("idx"), langs("dat"), back_inserter(languages));
-	return languages;
+std::vector<std::string> Thesaurus::GetLanguageList() const
+{
+    if (languages.empty())
+        boost::set_intersection(langs("idx"), langs("dat"), back_inserter(languages));
+    return languages;
 }
 
-static bool check_path(agi::fs::path const& path, std::string const& language, agi::fs::path& idx, agi::fs::path& dat) {
-	idx = path/agi::format("th_%s.idx", language);
-	dat = path/agi::format("th_%s.dat", language);
-	return agi::fs::FileExists(idx) && agi::fs::FileExists(dat);
+static bool check_path(agi::fs::path const &path, std::string const &language, agi::fs::path &idx, agi::fs::path &dat)
+{
+    idx = path / agi::format("th_%s.idx", language);
+    dat = path / agi::format("th_%s.dat", language);
+    return agi::fs::FileExists(idx) && agi::fs::FileExists(dat);
 }
 
-void Thesaurus::OnLanguageChanged() {
-	impl.reset();
-
-	auto language = OPT_GET("Tool/Thesaurus/Language")->GetString();
-	if (language.empty()) return;
-
-	agi::fs::path idx, dat;
-
-	auto path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString() + "/");
-	if (!check_path(path, language, idx, dat)) {
-		path = config::path->Decode("?data/dictionaries/");
-		if (!check_path(path, language, idx, dat))
-			return;
-	}
-
-	LOG_I("thesaurus/file") << "Using thesaurus: " << dat;
-
-	if (cancel_load) *cancel_load = true;
-	cancel_load = new bool{false};
-	auto cancel = cancel_load; // Needed to avoid capturing via `this`
-	agi::dispatch::Background().Async([=]{
-		try {
-			auto thes = agi::make_unique<agi::Thesaurus>(dat, idx);
-			agi::dispatch::Main().Sync([&thes, cancel, this]{
-				if (!*cancel) {
-					impl = std::move(thes);
-					cancel_load = nullptr;
-				}
-				delete cancel;
-			});
-		}
-		catch (agi::Exception const& e) {
-			LOG_E("thesaurus") << e.GetMessage();
-		}
-	});
+void Thesaurus::OnLanguageChanged()
+{
+    impl.reset();
+
+    auto language = OPT_GET("Tool/Thesaurus/Language")->GetString();
+    if (language.empty()) return;
+
+    agi::fs::path idx, dat;
+
+    auto path = config::path->Decode(OPT_GET("Path/Dictionary")->GetString() + "/");
+    if (!check_path(path, language, idx, dat)) {
+        path = config::path->Decode("?data/dictionaries/");
+        if (!check_path(path, language, idx, dat))
+            return;
+    }
+
+    LOG_I("thesaurus/file") << "Using thesaurus: " << dat;
+
+    if (cancel_load) *cancel_load = true;
+    cancel_load = new bool{false};
+    auto cancel = cancel_load; // Needed to avoid capturing via `this`
+    agi::dispatch::Background().Async([ = ] {
+        try
+        {
+            auto thes = agi::make_unique<agi::Thesaurus>(dat, idx);
+            agi::dispatch::Main().Sync([&thes, cancel, this] {
+                if (!*cancel)
+                {
+                    impl = std::move(thes);
+                    cancel_load = nullptr;
+                }
+                delete cancel;
+            });
+        }
+        catch (agi::Exception const &e)
+        {
+            LOG_E("thesaurus") << e.GetMessage();
+        }
+    });
 }
 
-void Thesaurus::OnPathChanged() {
-	languages.clear();
+void Thesaurus::OnPathChanged()
+{
+    languages.clear();
 }
diff --git a/src/thesaurus.h b/src/thesaurus.h
index 662dbb76b72102db9756c88d069a502f9076b54c..92a0ca0779467a9a1bb8bd802e2fed7ef61ce4bd 100644
--- a/src/thesaurus.h
+++ b/src/thesaurus.h
@@ -25,34 +25,34 @@ namespace agi { class Thesaurus; }
 /// @class Thesaurus
 /// @brief A wrapper around agi::Thesarus adding wx and Aegisub-specific stuff
 class Thesaurus {
-	/// The actual thesaurus implementation
-	std::unique_ptr<agi::Thesaurus> impl;
-	/// A cached list of languages available
-	mutable std::vector<std::string> languages;
+    /// The actual thesaurus implementation
+    std::unique_ptr<agi::Thesaurus> impl;
+    /// A cached list of languages available
+    mutable std::vector<std::string> languages;
 
-	/// Thesaurus language change slot
-	agi::signal::Connection lang_listener;
-	/// Thesaurus language change handler
-	void OnLanguageChanged();
+    /// Thesaurus language change slot
+    agi::signal::Connection lang_listener;
+    /// Thesaurus language change handler
+    void OnLanguageChanged();
 
-	/// Thesaurus path change slot
-	agi::signal::Connection dict_path_listener;
-	/// Thesaurus path change handler
-	void OnPathChanged();
+    /// Thesaurus path change slot
+    agi::signal::Connection dict_path_listener;
+    /// Thesaurus path change handler
+    void OnPathChanged();
 
-	bool *cancel_load = nullptr;
+    bool *cancel_load = nullptr;
 
 public:
-	/// A pair of a word and synonyms for that word
-	typedef std::pair<std::string, std::vector<std::string>> Entry;
+    /// A pair of a word and synonyms for that word
+    typedef std::pair<std::string, std::vector<std::string>> Entry;
 
-	Thesaurus();
-	~Thesaurus();
+    Thesaurus();
+    ~Thesaurus();
 
-	/// Get a list of synonyms for a word, grouped by possible meanings of the word
-	/// @param word Word to get synonyms for
-	std::vector<Entry> Lookup(std::string word);
+    /// Get a list of synonyms for a word, grouped by possible meanings of the word
+    /// @param word Word to get synonyms for
+    std::vector<Entry> Lookup(std::string word);
 
-	/// Get a list of language codes which thesauri are available for
-	std::vector<std::string> GetLanguageList() const;
+    /// Get a list of language codes which thesauri are available for
+    std::vector<std::string> GetLanguageList() const;
 };
diff --git a/src/time_range.h b/src/time_range.h
index 6c270f07d19bb7383152f9f26af6052150820a14..8344fa316245b6f603e05615daf31c8dd6f7e5c9 100644
--- a/src/time_range.h
+++ b/src/time_range.h
@@ -32,32 +32,30 @@
 /// @class TimeRange
 /// @brief Represents an immutable range of time
 class TimeRange {
-	int _begin;
-	int _end;
+    int _begin;
+    int _end;
 
 public:
-	/// @brief Constructor
-	/// @param begin Index of the first millisecond to include in the range
-	/// @param end   Index of one past the last millisecond to include in the range
-	TimeRange(int begin, int end)
-	: _begin(begin), _end(end)
-	{
-		assert(end >= begin);
-	}
+    /// @brief Constructor
+    /// @param begin Index of the first millisecond to include in the range
+    /// @param end   Index of one past the last millisecond to include in the range
+    TimeRange(int begin, int end)
+        : _begin(begin), _end(end) {
+        assert(end >= begin);
+    }
 
-	/// Get the length of the range in milliseconds
-	int length() const { return _end - _begin; }
-	/// Get the start time of the range in milliseconds
-	int begin() const { return _begin; }
-	/// Get the exclusive end time of the range in milliseconds
-	int end() const { return _end; }
+    /// Get the length of the range in milliseconds
+    int length() const { return _end - _begin; }
+    /// Get the start time of the range in milliseconds
+    int begin() const { return _begin; }
+    /// Get the exclusive end time of the range in milliseconds
+    int end() const { return _end; }
 
-	/// Determine whether the range contains a given time in milliseconds
-	bool contains(int ms) const { return ms >= begin() && ms < end(); }
+    /// Determine whether the range contains a given time in milliseconds
+    bool contains(int ms) const { return ms >= begin() && ms < end(); }
 
-	/// Determine whether there is an overlap between two ranges
-	bool overlaps(const TimeRange &other) const
-	{
-		return other.contains(_begin) || contains(other._begin);
-	}
+    /// Determine whether there is an overlap between two ranges
+    bool overlaps(const TimeRange &other) const {
+        return other.contains(_begin) || contains(other._begin);
+    }
 };
diff --git a/src/timeedit_ctrl.cpp b/src/timeedit_ctrl.cpp
index 4ccb454d37192a68afd168c4bfc44c27eabc1142..db59a6a8bd5fc7f05d26da6fd4294a14a46144f7 100644
--- a/src/timeedit_ctrl.cpp
+++ b/src/timeedit_ctrl.cpp
@@ -49,193 +49,206 @@
 #define TimeEditWindowStyle
 
 enum {
-	Time_Edit_Copy = 1320,
-	Time_Edit_Paste
+    Time_Edit_Copy = 1320,
+    Time_Edit_Paste
 };
 
-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)
-, isEnd(asEnd)
-, insert(!OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool())
-, insert_opt(OPT_SUB("Subtitle/Time Edit/Insert Mode", &TimeEdit::OnInsertChanged, this))
+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)
+    , isEnd(asEnd)
+    , insert(!OPT_GET("Subtitle/Time Edit/Insert Mode")->GetBool())
+    , insert_opt(OPT_SUB("Subtitle/Time Edit/Insert Mode", &TimeEdit::OnInsertChanged, this))
 {
-	// Set validator
-	wxTextValidator val(wxFILTER_INCLUDE_CHAR_LIST);
-	wxString includes[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", ":", ","};
-	val.SetIncludes(wxArrayString(countof(includes), includes));
-	SetValidator(val);
-
-	// Other stuff
-	if (value.empty()) SetValue(to_wx(time.GetAssFormatted()));
-
-	Bind(wxEVT_MENU, std::bind(&TimeEdit::CopyTime, this), Time_Edit_Copy);
-	Bind(wxEVT_MENU, std::bind(&TimeEdit::PasteTime, this), Time_Edit_Paste);
-	Bind(wxEVT_TEXT, &TimeEdit::OnModified, this);
-	Bind(wxEVT_CONTEXT_MENU, &TimeEdit::OnContextMenu, this);
-	Bind(wxEVT_CHAR_HOOK, &TimeEdit::OnKeyDown, this);
-	Bind(wxEVT_CHAR, &TimeEdit::OnChar, this);
-	Bind(wxEVT_KILL_FOCUS, &TimeEdit::OnFocusLost, this);
+    // Set validator
+    wxTextValidator val(wxFILTER_INCLUDE_CHAR_LIST);
+    wxString includes[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", ":", ","};
+    val.SetIncludes(wxArrayString(countof(includes), includes));
+    SetValidator(val);
+
+    // Other stuff
+    if (value.empty()) SetValue(to_wx(time.GetAssFormatted()));
+
+    Bind(wxEVT_MENU, std::bind(&TimeEdit::CopyTime, this), Time_Edit_Copy);
+    Bind(wxEVT_MENU, std::bind(&TimeEdit::PasteTime, this), Time_Edit_Paste);
+    Bind(wxEVT_TEXT, &TimeEdit::OnModified, this);
+    Bind(wxEVT_CONTEXT_MENU, &TimeEdit::OnContextMenu, this);
+    Bind(wxEVT_CHAR_HOOK, &TimeEdit::OnKeyDown, this);
+    Bind(wxEVT_CHAR, &TimeEdit::OnChar, this);
+    Bind(wxEVT_KILL_FOCUS, &TimeEdit::OnFocusLost, this);
 }
 
-void TimeEdit::SetTime(agi::Time new_time) {
-	if (time != new_time) {
-		time = new_time;
-		UpdateText();
-	}
+void TimeEdit::SetTime(agi::Time new_time)
+{
+    if (time != new_time) {
+        time = new_time;
+        UpdateText();
+    }
 }
 
-int TimeEdit::GetFrame() const {
-	return c->project->Timecodes().FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START);
+int TimeEdit::GetFrame() const
+{
+    return c->project->Timecodes().FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START);
 }
 
-void TimeEdit::SetFrame(int fn) {
-	SetTime(c->project->Timecodes().TimeAtFrame(fn, isEnd ? agi::vfr::END : agi::vfr::START));
+void TimeEdit::SetFrame(int fn)
+{
+    SetTime(c->project->Timecodes().TimeAtFrame(fn, isEnd ? agi::vfr::END : agi::vfr::START));
 }
 
-void TimeEdit::SetByFrame(bool enableByFrame) {
-	if (enableByFrame == byFrame) return;
+void TimeEdit::SetByFrame(bool enableByFrame)
+{
+    if (enableByFrame == byFrame) return;
 
-	byFrame = enableByFrame && c->project->Timecodes().IsLoaded();
-	UpdateText();
+    byFrame = enableByFrame && c->project->Timecodes().IsLoaded();
+    UpdateText();
 }
 
-void TimeEdit::OnModified(wxCommandEvent &event) {
-	event.Skip();
-	if (byFrame) {
-		long temp = 0;
-		GetValue().ToLong(&temp);
-		time = c->project->Timecodes().TimeAtFrame(temp, isEnd ? agi::vfr::END : agi::vfr::START);
-	}
-	else if (insert)
-		time = from_wx(GetValue());
+void TimeEdit::OnModified(wxCommandEvent &event)
+{
+    event.Skip();
+    if (byFrame) {
+        long temp = 0;
+        GetValue().ToLong(&temp);
+        time = c->project->Timecodes().TimeAtFrame(temp, isEnd ? agi::vfr::END : agi::vfr::START);
+    }
+    else if (insert)
+        time = from_wx(GetValue());
 }
 
-void TimeEdit::UpdateText() {
-	if (byFrame)
-		ChangeValue(std::to_wstring(c->project->Timecodes().FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START)));
-	else
-		ChangeValue(to_wx(time.GetAssFormatted()));
+void TimeEdit::UpdateText()
+{
+    if (byFrame)
+        ChangeValue(std::to_wstring(c->project->Timecodes().FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START)));
+    else
+        ChangeValue(to_wx(time.GetAssFormatted()));
 }
 
-void TimeEdit::OnKeyDown(wxKeyEvent &event) {
-	int kc = event.GetKeyCode();
-
-	// Needs to be done here to trump user-defined hotkeys
-	int key = event.GetUnicodeKey();
-	if (event.CmdDown()) {
-		if (key == 'C' || key == 'X')
-			CopyTime();
-		else if (key == 'V')
-			PasteTime();
-		else
-			event.Skip();
-		return;
-	}
-
-	// Shift-Insert would paste the stuff anyway
-	// but no one updates the private "time" variable.
-	if (event.ShiftDown() && kc == WXK_INSERT) {
-		PasteTime();
-		return;
-	}
-
-	if (byFrame || insert) {
-		event.Skip();
-		return;
-	}
-	// Overwrite mode stuff
-
-	// On OS X backspace is reported as delete
+void TimeEdit::OnKeyDown(wxKeyEvent &event)
+{
+    int kc = event.GetKeyCode();
+
+    // Needs to be done here to trump user-defined hotkeys
+    int key = event.GetUnicodeKey();
+    if (event.CmdDown()) {
+        if (key == 'C' || key == 'X')
+            CopyTime();
+        else if (key == 'V')
+            PasteTime();
+        else
+            event.Skip();
+        return;
+    }
+
+    // Shift-Insert would paste the stuff anyway
+    // but no one updates the private "time" variable.
+    if (event.ShiftDown() && kc == WXK_INSERT) {
+        PasteTime();
+        return;
+    }
+
+    if (byFrame || insert) {
+        event.Skip();
+        return;
+    }
+    // Overwrite mode stuff
+
+    // On OS X backspace is reported as delete
 #ifdef __APPLE__
-	if (kc == WXK_DELETE)
-		kc = WXK_BACK;
+    if (kc == WXK_DELETE)
+        kc = WXK_BACK;
 #endif
 
-	// Back just moves cursor back one without deleting
-	if (kc == WXK_BACK) {
-		long start = GetInsertionPoint();
-		if (start > 0)
-			SetInsertionPoint(start - 1);
-	}
-	// Delete just does nothing
-	else if (kc != WXK_DELETE)
-		event.Skip();
+    // Back just moves cursor back one without deleting
+    if (kc == WXK_BACK) {
+        long start = GetInsertionPoint();
+        if (start > 0)
+            SetInsertionPoint(start - 1);
+    }
+    // Delete just does nothing
+    else if (kc != WXK_DELETE)
+        event.Skip();
 }
 
-void TimeEdit::OnChar(wxKeyEvent &event) {
-	event.Skip();
-	if (byFrame || insert) return;
-
-	int key = event.GetUnicodeKey();
-	if ((key < '0' || key > '9') && key != ';' && key != '.' && key != ',') return;
-
-	event.Skip(false);
-
-	long start = GetInsertionPoint();
-	std::string text = from_wx(GetValue());
-	// Cursor is at the end so do nothing
-	if (start >= (long)text.size()) return;
-
-	// If the cursor is at punctuation, move it forward to the next digit
-	if (text[start] == ':' || text[start] == '.' || text[start] == ',')
-		++start;
-
-	// : and . hop over punctuation but never insert anything
-	if (key == ':' || key == ';' || key == '.' || key == ',') {
-		SetInsertionPoint(start);
-		return;
-	}
-
-	// Overwrite the digit
-	text[start] = (char)key;
-	time = text;
-	SetValue(to_wx(time.GetAssFormatted()));
-	SetInsertionPoint(start + 1);
+void TimeEdit::OnChar(wxKeyEvent &event)
+{
+    event.Skip();
+    if (byFrame || insert) return;
+
+    int key = event.GetUnicodeKey();
+    if ((key < '0' || key > '9') && key != ';' && key != '.' && key != ',') return;
+
+    event.Skip(false);
+
+    long start = GetInsertionPoint();
+    std::string text = from_wx(GetValue());
+    // Cursor is at the end so do nothing
+    if (start >= (long)text.size()) return;
+
+    // If the cursor is at punctuation, move it forward to the next digit
+    if (text[start] == ':' || text[start] == '.' || text[start] == ',')
+        ++start;
+
+    // : and . hop over punctuation but never insert anything
+    if (key == ':' || key == ';' || key == '.' || key == ',') {
+        SetInsertionPoint(start);
+        return;
+    }
+
+    // Overwrite the digit
+    text[start] = (char)key;
+    time = text;
+    SetValue(to_wx(time.GetAssFormatted()));
+    SetInsertionPoint(start + 1);
 }
 
-void TimeEdit::OnInsertChanged(agi::OptionValue const& opt) {
-	insert = !opt.GetBool();
+void TimeEdit::OnInsertChanged(agi::OptionValue const &opt)
+{
+    insert = !opt.GetBool();
 }
 
-void TimeEdit::OnContextMenu(wxContextMenuEvent &evt) {
-	if (byFrame || insert) {
-		evt.Skip();
-		return;
-	}
-
-	wxMenu menu;
-	menu.Append(Time_Edit_Copy, _("&Copy"));
-	menu.Append(Time_Edit_Paste, _("&Paste"));
-	PopupMenu(&menu);
+void TimeEdit::OnContextMenu(wxContextMenuEvent &evt)
+{
+    if (byFrame || insert) {
+        evt.Skip();
+        return;
+    }
+
+    wxMenu menu;
+    menu.Append(Time_Edit_Copy, _("&Copy"));
+    menu.Append(Time_Edit_Paste, _("&Paste"));
+    PopupMenu(&menu);
 }
 
-void TimeEdit::OnFocusLost(wxFocusEvent &evt) {
-	if (insert || byFrame)
-		UpdateText();
-	evt.Skip();
+void TimeEdit::OnFocusLost(wxFocusEvent &evt)
+{
+    if (insert || byFrame)
+        UpdateText();
+    evt.Skip();
 }
 
-void TimeEdit::CopyTime() {
-	SetClipboard(from_wx(GetValue()));
+void TimeEdit::CopyTime()
+{
+    SetClipboard(from_wx(GetValue()));
 }
 
-void TimeEdit::PasteTime() {
-	if (byFrame) {
-		Paste();
-		return;
-	}
-
-	std::string text(GetClipboard());
-	if (text.empty()) return;
-
-	agi::Time tempTime(text);
-	if (tempTime.GetAssFormatted() == text) {
-		SetTime(tempTime);
-		SetSelection(0, GetValue().size());
-
-		wxCommandEvent evt(wxEVT_TEXT, GetId());
-		evt.SetEventObject(this);
-		HandleWindowEvent(evt);
-	}
+void TimeEdit::PasteTime()
+{
+    if (byFrame) {
+        Paste();
+        return;
+    }
+
+    std::string text(GetClipboard());
+    if (text.empty()) return;
+
+    agi::Time tempTime(text);
+    if (tempTime.GetAssFormatted() == text) {
+        SetTime(tempTime);
+        SetSelection(0, GetValue().size());
+
+        wxCommandEvent evt(wxEVT_TEXT, GetId());
+        evt.SetEventObject(this);
+        HandleWindowEvent(evt);
+    }
 }
diff --git a/src/timeedit_ctrl.h b/src/timeedit_ctrl.h
index a056a11818c9f12687edbd5abd27adfa321d4a8c..75b01e833503e9a2af46ac0eb7e8fe3d992468a5 100644
--- a/src/timeedit_ctrl.h
+++ b/src/timeedit_ctrl.h
@@ -33,8 +33,8 @@
 #include <wx/textctrl.h>
 
 namespace agi {
-	class OptionValue;
-	struct Context;
+class OptionValue;
+struct Context;
 }
 
 /// @brief A text edit control for editing agi::Time objects
@@ -42,56 +42,56 @@ namespace agi {
 /// This control constrains values to valid times, and can display the time
 /// being edited as either a h:mm:ss.cc formatted time, or a frame number
 class TimeEdit final : public wxTextCtrl {
-	bool byFrame = false; ///< Is the time displayed as a frame number?
-	agi::Context *c; ///< Project context
-	bool isEnd;      ///< Should the time be treated as an end time for time <-> frame conversions?
-	agi::Time time;  ///< The time, which may be displayed as either a frame number or time
-	bool insert;     ///< If true, disable overwriting behavior in time mode
+    bool byFrame = false; ///< Is the time displayed as a frame number?
+    agi::Context *c; ///< Project context
+    bool isEnd;      ///< Should the time be treated as an end time for time <-> frame conversions?
+    agi::Time time;  ///< The time, which may be displayed as either a frame number or time
+    bool insert;     ///< If true, disable overwriting behavior in time mode
 
-	agi::signal::Connection insert_opt;
+    agi::signal::Connection insert_opt;
 
-	void CopyTime();
-	void PasteTime();
+    void CopyTime();
+    void PasteTime();
 
-	/// Set the value of the text box from the current time and byFrame setting
-	void UpdateText();
+    /// Set the value of the text box from the current time and byFrame setting
+    void UpdateText();
 
-	void OnContextMenu(wxContextMenuEvent &event);
-	void OnFocusLost(wxFocusEvent &evt);
-	void OnInsertChanged(agi::OptionValue const& opt);
-	void OnKeyDown(wxKeyEvent &event);
-	void OnChar(wxKeyEvent &event);
-	void OnModified(wxCommandEvent &event);
+    void OnContextMenu(wxContextMenuEvent &event);
+    void OnFocusLost(wxFocusEvent &evt);
+    void OnInsertChanged(agi::OptionValue const &opt);
+    void OnKeyDown(wxKeyEvent &event);
+    void OnChar(wxKeyEvent &event);
+    void OnModified(wxCommandEvent &event);
 
 #ifdef __WXGTK__
-	// IM processing completely breaks modifying a text ctrl's in response to
-	// wxEVT_CHAR (changing the value clears it and modifying the insertion
-	// point does nothing). IM processing should never be relevant here, so
-	// just disable it.
-	int GTKIMFilterKeypress(GdkEventKey *) const override { return 0; }
+    // IM processing completely breaks modifying a text ctrl's in response to
+    // wxEVT_CHAR (changing the value clears it and modifying the insertion
+    // point does nothing). IM processing should never be relevant here, so
+    // just disable it.
+    int GTKIMFilterKeypress(GdkEventKey *) const override { return 0; }
 #endif
 
 public:
-	/// Get the current time as an agi::Time object
-	agi::Time GetTime() const { return time; }
-	/// Set the time
-	void SetTime(agi::Time time);
+    /// Get the current time as an agi::Time object
+    agi::Time GetTime() const { return time; }
+    /// Set the time
+    void SetTime(agi::Time time);
 
-	/// Get the current time as a frame number, or 0 if timecodes are unavailable
-	int GetFrame() const;
-	/// Set the time to a frame number. Does nothing if timecodes are unavailable
-	void SetFrame(int fn);
+    /// Get the current time as a frame number, or 0 if timecodes are unavailable
+    int GetFrame() const;
+    /// Set the time to a frame number. Does nothing if timecodes are unavailable
+    void SetFrame(int fn);
 
-	/// Set whether the time is displayed as a time or the corresponding frame number
-	/// @param enableByFrame If true, frame numbers are displayed
-	void SetByFrame(bool enableByFrame);
+    /// Set whether the time is displayed as a time or the corresponding frame number
+    /// @param enableByFrame If true, frame numbers are displayed
+    void SetByFrame(bool enableByFrame);
 
-	/// Constructor
-	/// @param parent Parent window
-	/// @param id Window id
-	/// @param c Project context
-	/// @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 std::string& value = std::string(), const wxSize& size = wxDefaultSize, bool asEnd = false);
+    /// Constructor
+    /// @param parent Parent window
+    /// @param id Window id
+    /// @param c Project context
+    /// @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 std::string &value = std::string(), const wxSize &size = wxDefaultSize, bool asEnd = false);
 };
diff --git a/src/toggle_bitmap.cpp b/src/toggle_bitmap.cpp
index 71de3ca576dc69a602eeac27b490ef532a489e13..67530ecce7badeb6ac15f33e18c75f9d036aaa47 100644
--- a/src/toggle_bitmap.cpp
+++ b/src/toggle_bitmap.cpp
@@ -41,46 +41,48 @@
 #include <wx/settings.h>
 #include <wx/tglbtn.h>
 
-ToggleBitmap::ToggleBitmap(wxWindow *parent, agi::Context *context, const char *cmd_name, int icon_size, const char *ht_ctx, wxSize const& size)
-: wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER)
-, context(context)
-, command(*cmd::get(cmd_name))
-, img(command.Icon(icon_size))
+ToggleBitmap::ToggleBitmap(wxWindow *parent, agi::Context *context, const char *cmd_name, int icon_size, const char *ht_ctx, wxSize const &size)
+    : wxControl(parent, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER)
+    , context(context)
+    , command(*cmd::get(cmd_name))
+    , img(command.Icon(icon_size))
 {
-	int w = size.GetWidth() != -1 ? size.GetWidth() : img.GetWidth();
-	int h = size.GetHeight() != -1 ? size.GetHeight() : img.GetHeight();
-	SetClientSize(w, h);
-	GetSize(&w, &h);
-	SetSizeHints(w, h, w, h);
+    int w = size.GetWidth() != -1 ? size.GetWidth() : img.GetWidth();
+    int h = size.GetHeight() != -1 ? size.GetHeight() : img.GetHeight();
+    SetClientSize(w, h);
+    GetSize(&w, &h);
+    SetSizeHints(w, h, w, h);
 
-	SetBackgroundStyle(wxBG_STYLE_PAINT);
+    SetBackgroundStyle(wxBG_STYLE_PAINT);
 
-	ToolTipManager::Bind(this, command.StrHelp(), ht_ctx, cmd_name);
-	Bind(wxEVT_PAINT, &ToggleBitmap::OnPaint, this);
-	Bind(wxEVT_LEFT_DOWN, &ToggleBitmap::OnMouseEvent, this);
+    ToolTipManager::Bind(this, command.StrHelp(), ht_ctx, cmd_name);
+    Bind(wxEVT_PAINT, &ToggleBitmap::OnPaint, this);
+    Bind(wxEVT_LEFT_DOWN, &ToggleBitmap::OnMouseEvent, this);
 }
 
-void ToggleBitmap::OnMouseEvent(wxMouseEvent &) {
-	if (command.Validate(context))
-		command(context);
-	Refresh(false);
+void ToggleBitmap::OnMouseEvent(wxMouseEvent &)
+{
+    if (command.Validate(context))
+        command(context);
+    Refresh(false);
 }
 
-void ToggleBitmap::OnPaint(wxPaintEvent &) {
-	wxAutoBufferedPaintDC dc(this);
+void ToggleBitmap::OnPaint(wxPaintEvent &)
+{
+    wxAutoBufferedPaintDC dc(this);
 
-	// Get background color
-	wxColour bgColor = command.IsActive(context) ? wxColour(0,255,0) : wxColour(255,0,0);
-	wxColor sysCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT);
-	bgColor.Set(
-		(sysCol.Red() + bgColor.Red()) / 2,
-		(sysCol.Green() + bgColor.Green()) / 2,
-		(sysCol.Blue() + bgColor.Blue()) / 2);
+    // Get background color
+    wxColour bgColor = command.IsActive(context) ? wxColour(0, 255, 0) : wxColour(255, 0, 0);
+    wxColor sysCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT);
+    bgColor.Set(
+        (sysCol.Red() + bgColor.Red()) / 2,
+        (sysCol.Green() + bgColor.Green()) / 2,
+        (sysCol.Blue() + bgColor.Blue()) / 2);
 
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.SetBrush(wxBrush(bgColor));
-	dc.DrawRectangle(wxPoint(0, 0), GetClientSize());
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.SetBrush(wxBrush(bgColor));
+    dc.DrawRectangle(wxPoint(0, 0), GetClientSize());
 
-	wxSize excess = (GetClientSize() - img.GetSize()) / 2;
-	dc.DrawBitmap(img, excess.GetX(), excess.GetY(), true);
+    wxSize excess = (GetClientSize() - img.GetSize()) / 2;
+    dc.DrawBitmap(img, excess.GetX(), excess.GetY(), true);
 }
diff --git a/src/toggle_bitmap.h b/src/toggle_bitmap.h
index fec8fa31638b994a4ee07a47f56b0a48f8e83701..0f69f726bf8ba5c1a14d1865574e9e3a73b214b3 100644
--- a/src/toggle_bitmap.h
+++ b/src/toggle_bitmap.h
@@ -39,13 +39,13 @@ namespace agi { struct Context; }
 namespace cmd { class Command; }
 
 class ToggleBitmap final : public wxControl {
-	agi::Context *context;
-	cmd::Command &command;
-	wxBitmap img;
+    agi::Context *context;
+    cmd::Command &command;
+    wxBitmap img;
 
-	void OnMouseEvent(wxMouseEvent &evt);
-	void OnPaint(wxPaintEvent &evt);
+    void OnMouseEvent(wxMouseEvent &evt);
+    void OnPaint(wxPaintEvent &evt);
 
 public:
-	ToggleBitmap(wxWindow *parent, agi::Context *context, const char *command, int icon_size, const char *ht_ctx, wxSize const& size = wxDefaultSize);
+    ToggleBitmap(wxWindow *parent, agi::Context *context, const char *command, int icon_size, const char *ht_ctx, wxSize const &size = wxDefaultSize);
 };
diff --git a/src/toolbar.cpp b/src/toolbar.cpp
index 5718b19d5fda40065b6c3be3df6426eedcac5efd..409a46e63b14a0a96c96d0c363e0f105147f08a2 100644
--- a/src/toolbar.cpp
+++ b/src/toolbar.cpp
@@ -38,178 +38,179 @@
 #include <wx/toolbar.h>
 
 namespace {
-	json::Object const& get_root() {
-		static json::Object root;
-		if (root.empty()) {
-			boost::interprocess::ibufferstream stream((const char *)default_toolbar, sizeof(default_toolbar));
-			root = std::move(static_cast<json::Object&>(agi::json_util::parse(stream)));
-		}
-		return root;
-	}
-
-	class Toolbar final : public wxToolBar {
-		/// Window ID of first toolbar control
-		static const int TOOL_ID_BASE = 5000;
-
-		/// Toolbar name in config file
-		std::string name;
-		/// Project context
-		agi::Context *context;
-		/// Commands for each of the buttons
-		std::vector<cmd::Command *> commands;
-		/// Hotkey context
-		std::string ht_context;
-
-		RetinaHelper retina_helper;
-
-		/// Current icon size
-		int icon_size;
-
-		/// Listener for icon size change signal
-		agi::signal::Connection icon_size_slot;
-
-		/// Listener for hotkey change signal
-		agi::signal::Connection hotkeys_changed_slot;
-
-		/// Enable/disable the toolbar buttons
-		void OnIdle(wxIdleEvent &) {
-			for (size_t i = 0; i < commands.size(); ++i) {
-				if (commands[i]->Type() & cmd::COMMAND_VALIDATE)
-					EnableTool(TOOL_ID_BASE + i, commands[i]->Validate(context));
-				if (commands[i]->Type() & cmd::COMMAND_TOGGLE || commands[i]->Type() & cmd::COMMAND_RADIO)
-					ToggleTool(TOOL_ID_BASE + i, commands[i]->IsActive(context));
-			}
-		}
-
-		/// Toolbar button click handler
-		void OnClick(wxCommandEvent &evt) {
-			(*commands[evt.GetId() - TOOL_ID_BASE])(context);
-		}
-
-		/// Regenerate the toolbar when the icon size changes
-		void OnIconSizeChange(agi::OptionValue const& opt) {
-			icon_size = opt.GetInt();
-			RegenerateToolbar();
-		}
-
-		/// Clear the toolbar and recreate it
-		void RegenerateToolbar() {
-			Unbind(wxEVT_IDLE, &Toolbar::OnIdle, this);
-			ClearTools();
-			commands.clear();
-			Populate();
-		}
-
-		/// Populate the toolbar with buttons
-		void Populate() {
-			json::Object const& root = get_root();
-			auto root_it = root.find(name);
-			if (root_it == root.end()) {
-				// Toolbar names are all hardcoded so this should never happen
-				throw agi::InternalError("Toolbar named " + name + " not found.");
-			}
-
-			json::Array const& arr = root_it->second;
-			commands.reserve(arr.size());
-			bool needs_onidle = false;
-			bool last_was_sep = false;
-
-			for (json::String const& command_name : arr) {
-				if (command_name.empty()) {
-					if (!last_was_sep)
-						AddSeparator();
-					last_was_sep = true;
-					continue;
-				}
-
-				cmd::Command *command;
-				try {
-					command = cmd::get(command_name);
-				}
-				catch (cmd::CommandNotFound const&) {
-					LOG_W("toolbar/command/not_found") << "Command '" << command_name << "' not found; skipping";
-					continue;
-				}
-
-				last_was_sep = false;
-
-				int flags = command->Type();
-				wxItemKind kind =
-					flags & cmd::COMMAND_RADIO ? wxITEM_RADIO :
-					flags & cmd::COMMAND_TOGGLE ? wxITEM_CHECK :
-					wxITEM_NORMAL;
-
-				wxBitmap const& bitmap = command->Icon(icon_size, GetLayoutDirection());
-				AddTool(TOOL_ID_BASE + commands.size(), command->StrDisplay(context), bitmap, GetTooltip(command), kind);
-
-				commands.push_back(command);
-				needs_onidle = needs_onidle || flags != cmd::COMMAND_NORMAL;
-			}
-
-			// Only bind the update function if there are actually any dynamic tools
-			if (needs_onidle) {
-				Bind(wxEVT_IDLE, &Toolbar::OnIdle, this);
-			}
-
-			Realize();
-		}
-
-		wxString GetTooltip(cmd::Command *command) {
-			wxString ret = command->StrHelp();
-
-			std::vector<std::string> hotkeys = hotkey::get_hotkey_strs(ht_context, command->name());
-			if (!hotkeys.empty())
-				ret += to_wx(" (" + boost::join(hotkeys, "/") + ")");
-
-			return ret;
-		}
-
-	public:
-		Toolbar(wxWindow *parent, std::string name, agi::Context *c, std::string ht_context, bool vertical)
-		: wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_NODIVIDER | wxTB_FLAT | (vertical ? wxTB_VERTICAL : wxTB_HORIZONTAL))
-		, name(std::move(name))
-		, context(c)
-		, ht_context(std::move(ht_context))
-		, retina_helper(parent)
-		, icon_size(OPT_GET("App/Toolbar Icon Size")->GetInt())
-		, icon_size_slot(OPT_SUB("App/Toolbar Icon Size", &Toolbar::OnIconSizeChange, this))
-		, hotkeys_changed_slot(hotkey::inst->AddHotkeyChangeListener(&Toolbar::RegenerateToolbar, this))
-		{
-			Populate();
-			Bind(wxEVT_TOOL, &Toolbar::OnClick, this);
-		}
-
-		Toolbar(wxFrame *parent, std::string name, agi::Context *c, std::string ht_context)
-		: wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_HORIZONTAL)
-		, name(std::move(name))
-		, context(c)
-		, ht_context(std::move(ht_context))
-		, retina_helper(parent)
+json::Object const &get_root()
+{
+    static json::Object root;
+    if (root.empty()) {
+        boost::interprocess::ibufferstream stream((const char *)default_toolbar, sizeof(default_toolbar));
+        root = std::move(static_cast<json::Object &>(agi::json_util::parse(stream)));
+    }
+    return root;
+}
+
+class Toolbar final : public wxToolBar {
+    /// Window ID of first toolbar control
+    static const int TOOL_ID_BASE = 5000;
+
+    /// Toolbar name in config file
+    std::string name;
+    /// Project context
+    agi::Context *context;
+    /// Commands for each of the buttons
+    std::vector<cmd::Command *> commands;
+    /// Hotkey context
+    std::string ht_context;
+
+    RetinaHelper retina_helper;
+
+    /// Current icon size
+    int icon_size;
+
+    /// Listener for icon size change signal
+    agi::signal::Connection icon_size_slot;
+
+    /// Listener for hotkey change signal
+    agi::signal::Connection hotkeys_changed_slot;
+
+    /// Enable/disable the toolbar buttons
+    void OnIdle(wxIdleEvent &) {
+        for (size_t i = 0; i < commands.size(); ++i) {
+            if (commands[i]->Type() & cmd::COMMAND_VALIDATE)
+                EnableTool(TOOL_ID_BASE + i, commands[i]->Validate(context));
+            if (commands[i]->Type() & cmd::COMMAND_TOGGLE || commands[i]->Type() & cmd::COMMAND_RADIO)
+                ToggleTool(TOOL_ID_BASE + i, commands[i]->IsActive(context));
+        }
+    }
+
+    /// Toolbar button click handler
+    void OnClick(wxCommandEvent &evt) {
+        (*commands[evt.GetId() - TOOL_ID_BASE])(context);
+    }
+
+    /// Regenerate the toolbar when the icon size changes
+    void OnIconSizeChange(agi::OptionValue const &opt) {
+        icon_size = opt.GetInt();
+        RegenerateToolbar();
+    }
+
+    /// Clear the toolbar and recreate it
+    void RegenerateToolbar() {
+        Unbind(wxEVT_IDLE, &Toolbar::OnIdle, this);
+        ClearTools();
+        commands.clear();
+        Populate();
+    }
+
+    /// Populate the toolbar with buttons
+    void Populate() {
+        json::Object const &root = get_root();
+        auto root_it = root.find(name);
+        if (root_it == root.end()) {
+            // Toolbar names are all hardcoded so this should never happen
+            throw agi::InternalError("Toolbar named " + name + " not found.");
+        }
+
+        json::Array const &arr = root_it->second;
+        commands.reserve(arr.size());
+        bool needs_onidle = false;
+        bool last_was_sep = false;
+
+        for (json::String const &command_name : arr) {
+            if (command_name.empty()) {
+                if (!last_was_sep)
+                    AddSeparator();
+                last_was_sep = true;
+                continue;
+            }
+
+            cmd::Command *command;
+            try {
+                command = cmd::get(command_name);
+            }
+            catch (cmd::CommandNotFound const &) {
+                LOG_W("toolbar/command/not_found") << "Command '" << command_name << "' not found; skipping";
+                continue;
+            }
+
+            last_was_sep = false;
+
+            int flags = command->Type();
+            wxItemKind kind =
+                flags & cmd::COMMAND_RADIO ? wxITEM_RADIO :
+                flags & cmd::COMMAND_TOGGLE ? wxITEM_CHECK :
+                wxITEM_NORMAL;
+
+            wxBitmap const &bitmap = command->Icon(icon_size, GetLayoutDirection());
+            AddTool(TOOL_ID_BASE + commands.size(), command->StrDisplay(context), bitmap, GetTooltip(command), kind);
+
+            commands.push_back(command);
+            needs_onidle = needs_onidle || flags != cmd::COMMAND_NORMAL;
+        }
+
+        // Only bind the update function if there are actually any dynamic tools
+        if (needs_onidle) {
+            Bind(wxEVT_IDLE, &Toolbar::OnIdle, this);
+        }
+
+        Realize();
+    }
+
+    wxString GetTooltip(cmd::Command *command) {
+        wxString ret = command->StrHelp();
+
+        std::vector<std::string> hotkeys = hotkey::get_hotkey_strs(ht_context, command->name());
+        if (!hotkeys.empty())
+            ret += to_wx(" (" + boost::join(hotkeys, "/") + ")");
+
+        return ret;
+    }
+
+public:
+    Toolbar(wxWindow *parent, std::string name, agi::Context *c, std::string ht_context, bool vertical)
+        : wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_NODIVIDER | wxTB_FLAT | (vertical ? wxTB_VERTICAL : wxTB_HORIZONTAL))
+        , name(std::move(name))
+        , context(c)
+        , ht_context(std::move(ht_context))
+        , retina_helper(parent)
+        , icon_size(OPT_GET("App/Toolbar Icon Size")->GetInt())
+        , icon_size_slot(OPT_SUB("App/Toolbar Icon Size", &Toolbar::OnIconSizeChange, this))
+        , hotkeys_changed_slot(hotkey::inst->AddHotkeyChangeListener(&Toolbar::RegenerateToolbar, this)) {
+        Populate();
+        Bind(wxEVT_TOOL, &Toolbar::OnClick, this);
+    }
+
+    Toolbar(wxFrame *parent, std::string name, agi::Context *c, std::string ht_context)
+        : wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_HORIZONTAL)
+        , name(std::move(name))
+        , context(c)
+        , ht_context(std::move(ht_context))
+        , retina_helper(parent)
 #ifndef __WXMAC__
-		, icon_size(OPT_GET("App/Toolbar Icon Size")->GetInt())
-		, icon_size_slot(OPT_SUB("App/Toolbar Icon Size", &Toolbar::OnIconSizeChange, this))
+        , icon_size(OPT_GET("App/Toolbar Icon Size")->GetInt())
+        , icon_size_slot(OPT_SUB("App/Toolbar Icon Size", &Toolbar::OnIconSizeChange, this))
 #else
-		, icon_size(32 * retina_helper.GetScaleFactor())
-		, icon_size_slot(retina_helper.AddScaleFactorListener([=](double scale) {
-			icon_size = 32 * retina_helper.GetScaleFactor();
-			RegenerateToolbar();
-		}))
+        , icon_size(32 * retina_helper.GetScaleFactor())
+        , icon_size_slot(retina_helper.AddScaleFactorListener([ = ](double scale) {
+        icon_size = 32 * retina_helper.GetScaleFactor();
+        RegenerateToolbar();
+    }))
 #endif
-		, hotkeys_changed_slot(hotkey::inst->AddHotkeyChangeListener(&Toolbar::RegenerateToolbar, this))
-		{
-			parent->SetToolBar(this);
-			Populate();
-			Bind(wxEVT_TOOL, &Toolbar::OnClick, this);
-		}
-	};
+        , hotkeys_changed_slot(hotkey::inst->AddHotkeyChangeListener(&Toolbar::RegenerateToolbar, this)) {
+        parent->SetToolBar(this);
+        Populate();
+        Bind(wxEVT_TOOL, &Toolbar::OnClick, this);
+    }
+};
 }
 
 namespace toolbar {
-	void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *c, std::string const& hotkey) {
-		new Toolbar(frame, name, c, hotkey);
-	}
+void AttachToolbar(wxFrame *frame, std::string const &name, agi::Context *c, std::string const &hotkey)
+{
+    new Toolbar(frame, name, c, hotkey);
+}
 
-	wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& hotkey, bool vertical) {
-		return new Toolbar(parent, name, c, hotkey, vertical);
-	}
+wxToolBar *GetToolbar(wxWindow *parent, std::string const &name, agi::Context *c, std::string const &hotkey, bool vertical)
+{
+    return new Toolbar(parent, name, c, hotkey, vertical);
+}
 }
diff --git a/src/tooltip_manager.cpp b/src/tooltip_manager.cpp
index 5c381e0d6ae6fcaa3b42fb1de44780df4e158f88..f7720f3b03ae70b563f7e979991ada0c1920bf99 100644
--- a/src/tooltip_manager.cpp
+++ b/src/tooltip_manager.cpp
@@ -44,36 +44,38 @@
 #include <wx/window.h>
 
 struct ToolTipBinding {
-	wxWeakRef<wxWindow> window;
-	wxString toolTip;
-	const char *command;
-	const char *context;
-	void Update();
+    wxWeakRef<wxWindow> window;
+    wxString toolTip;
+    const char *command;
+    const char *context;
+    void Update();
 };
 
-void ToolTipManager::Bind(wxWindow *window, wxString tooltip, const char *context, const char *command) {
-	ToolTipBinding tip{window, tooltip, command, context};
-	tip.Update();
+void ToolTipManager::Bind(wxWindow *window, wxString tooltip, const char *context, const char *command)
+{
+    ToolTipBinding tip{window, tooltip, command, context};
+    tip.Update();
 
-	static std::list<ToolTipBinding> tips;
-	tips.push_back(tip);
-	hotkey::inst->AddHotkeyChangeListener(&ToolTipBinding::Update, &tips.back());
+    static std::list<ToolTipBinding> tips;
+    tips.push_back(tip);
+    hotkey::inst->AddHotkeyChangeListener(&ToolTipBinding::Update, &tips.back());
 }
 
-void ToolTipBinding::Update() {
-	if (!window) return;
+void ToolTipBinding::Update()
+{
+    if (!window) return;
 
-	std::vector<std::string> hotkeys = hotkey::get_hotkey_strs(context, command);
+    std::vector<std::string> hotkeys = hotkey::get_hotkey_strs(context, command);
 
-	std::string str;
-	for (size_t i = 0; i < hotkeys.size(); ++i) {
-		if (i > 0) str += "/";
-		str += hotkeys[i];
-	}
-	if (str.empty()) {
-		window->SetToolTip(toolTip);
-	}
-	else {
-		window->SetToolTip(toolTip + to_wx(" (" + str + ")"));
-	}
+    std::string str;
+    for (size_t i = 0; i < hotkeys.size(); ++i) {
+        if (i > 0) str += "/";
+        str += hotkeys[i];
+    }
+    if (str.empty()) {
+        window->SetToolTip(toolTip);
+    }
+    else {
+        window->SetToolTip(toolTip + to_wx(" (" + str + ")"));
+    }
 }
diff --git a/src/tooltip_manager.h b/src/tooltip_manager.h
index 698f0f607e5bd003adba868d515dea1de7f1b60e..13c930c69529793b39faf470943b79c87d467362 100644
--- a/src/tooltip_manager.h
+++ b/src/tooltip_manager.h
@@ -32,5 +32,5 @@ class wxWindow;
 
 class ToolTipManager {
 public:
-	static void Bind(wxWindow *window, wxString tooltip, const char *context, const char *command);
+    static void Bind(wxWindow *window, wxString tooltip, const char *context, const char *command);
 };
diff --git a/src/utils.cpp b/src/utils.cpp
index 2416aa7a22c6b4f8236f50689b24286574f8b6c5..5dbf00394592bfd497f0dec96c07b2cc1661ffbf 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -56,156 +56,169 @@
 #endif
 
 /// @brief There shall be no kiB, MiB stuff here Pretty reading of size
-wxString PrettySize(int bytes) {
-	const char *suffix[] = { "", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
-
-	// Set size
-	size_t i = 0;
-	double size = bytes;
-	while (size > 1024 && i + 1 < sizeof(suffix) / sizeof(suffix[0])) {
-		size /= 1024.0;
-		i++;
-	}
-
-	// Set number of decimal places
-	const char *fmt = "%.0f";
-	if (size < 10)
-		fmt = "%.2f";
-	else if (size < 100)
-		fmt = "%1.f";
-	return agi::wxformat(fmt, size) + " " + suffix[i];
+wxString PrettySize(int bytes)
+{
+    const char *suffix[] = { "", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
+
+    // Set size
+    size_t i = 0;
+    double size = bytes;
+    while (size > 1024 && i + 1 < sizeof(suffix) / sizeof(suffix[0])) {
+        size /= 1024.0;
+        i++;
+    }
+
+    // Set number of decimal places
+    const char *fmt = "%.0f";
+    if (size < 10)
+        fmt = "%.2f";
+    else if (size < 100)
+        fmt = "%1.f";
+    return agi::wxformat(fmt, size) + " " + suffix[i];
 }
 
-std::string float_to_string(double val) {
-	std::string s = agi::format("%.3f", val);
-	size_t pos = s.find_last_not_of("0");
-	if (pos != s.find(".")) ++pos;
-	s.erase(begin(s) + pos, end(s));
-	return s;
+std::string float_to_string(double val)
+{
+    std::string s = agi::format("%.3f", val);
+    size_t pos = s.find_last_not_of("0");
+    if (pos != s.find(".")) ++pos;
+    s.erase(begin(s) + pos, end(s));
+    return s;
 }
 
-int SmallestPowerOf2(int x) {
-	x--;
-	x |= (x >> 1);
-	x |= (x >> 2);
-	x |= (x >> 4);
-	x |= (x >> 8);
-	x |= (x >> 16);
-	x++;
-	return x;
+int SmallestPowerOf2(int x)
+{
+    x--;
+    x |= (x >> 1);
+    x |= (x >> 2);
+    x |= (x >> 4);
+    x |= (x >> 8);
+    x |= (x >> 16);
+    x++;
+    return x;
 }
 
 #ifndef __WXMAC__
-void RestartAegisub() {
-	config::opt->Flush();
+void RestartAegisub()
+{
+    config::opt->Flush();
 
 #if defined(__WXMSW__)
-	wxExecute("\"" + wxStandardPaths::Get().GetExecutablePath() + "\"");
+    wxExecute("\"" + wxStandardPaths::Get().GetExecutablePath() + "\"");
 #else
-	wxExecute(wxStandardPaths::Get().GetExecutablePath());
+    wxExecute(wxStandardPaths::Get().GetExecutablePath());
 #endif
 }
 #endif
 
-bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt) {
-	wxWindow *target = wxFindWindowAtPoint(wxGetMousePosition());
-	if (!target || target == source) return true;
-
-	// If the mouse is over a parent of the source window just pretend it's
-	// over the source window, so that the mouse wheel works on borders and such
-	wxWindow *parent = source->GetParent();
-	while (parent && parent != target) parent = parent->GetParent();
-	if (parent == target) return true;
-
-	// Otherwise send it to the new target
-	target->GetEventHandler()->ProcessEvent(evt);
-	evt.Skip(false);
-	return false;
+bool ForwardMouseWheelEvent(wxWindow *source, wxMouseEvent &evt)
+{
+    wxWindow *target = wxFindWindowAtPoint(wxGetMousePosition());
+    if (!target || target == source) return true;
+
+    // If the mouse is over a parent of the source window just pretend it's
+    // over the source window, so that the mouse wheel works on borders and such
+    wxWindow *parent = source->GetParent();
+    while (parent && parent != target) parent = parent->GetParent();
+    if (parent == target) return true;
+
+    // Otherwise send it to the new target
+    target->GetEventHandler()->ProcessEvent(evt);
+    evt.Skip(false);
+    return false;
 }
 
-std::string GetClipboard() {
-	wxString data;
-	wxClipboard *cb = wxClipboard::Get();
-	if (cb->Open()) {
-		if (cb->IsSupported(wxDF_TEXT) || cb->IsSupported(wxDF_UNICODETEXT)) {
-			wxTextDataObject raw_data;
-			cb->GetData(raw_data);
-			data = raw_data.GetText();
-		}
-		cb->Close();
-	}
-	return from_wx(data);
+std::string GetClipboard()
+{
+    wxString data;
+    wxClipboard *cb = wxClipboard::Get();
+    if (cb->Open()) {
+        if (cb->IsSupported(wxDF_TEXT) || cb->IsSupported(wxDF_UNICODETEXT)) {
+            wxTextDataObject raw_data;
+            cb->GetData(raw_data);
+            data = raw_data.GetText();
+        }
+        cb->Close();
+    }
+    return from_wx(data);
 }
 
-void SetClipboard(std::string const& new_data) {
-	wxClipboard *cb = wxClipboard::Get();
-	if (cb->Open()) {
-		cb->SetData(new wxTextDataObject(to_wx(new_data)));
-		cb->Flush();
-		cb->Close();
-	}
+void SetClipboard(std::string const &new_data)
+{
+    wxClipboard *cb = wxClipboard::Get();
+    if (cb->Open()) {
+        cb->SetData(new wxTextDataObject(to_wx(new_data)));
+        cb->Flush();
+        cb->Close();
+    }
 }
 
-void SetClipboard(wxBitmap const& new_data) {
-	wxClipboard *cb = wxClipboard::Get();
-	if (cb->Open()) {
-		cb->SetData(new wxBitmapDataObject(new_data));
-		cb->Flush();
-		cb->Close();
-	}
+void SetClipboard(wxBitmap const &new_data)
+{
+    wxClipboard *cb = wxClipboard::Get();
+    if (cb->Open()) {
+        cb->SetData(new wxBitmapDataObject(new_data));
+        cb->Flush();
+        cb->Close();
+    }
 }
 
-void CleanCache(agi::fs::path const& directory, std::string const& file_type, uint64_t max_size, uint64_t max_files) {
-	static std::unique_ptr<agi::dispatch::Queue> queue;
-	if (!queue)
-		queue = agi::dispatch::Create();
-
-	max_size <<= 20;
-	if (max_files == 0)
-		max_files = std::numeric_limits<uint64_t>::max();
-	queue->Async([=]{
-		LOG_D("utils/clean_cache") << "cleaning " << directory/file_type;
-		uint64_t total_size = 0;
-		using cache_item = std::pair<int64_t, agi::fs::path>;
-		std::vector<cache_item> cachefiles;
-		for (auto const& file : agi::fs::DirectoryIterator(directory, file_type)) {
-			agi::fs::path path = directory/file;
-			cachefiles.push_back({agi::fs::ModifiedTime(path), path});
-			total_size += agi::fs::Size(path);
-		}
-
-		if (cachefiles.size() <= max_files && total_size <= max_size) {
-			LOG_D("utils/clean_cache") << agi::format("cache does not need cleaning (maxsize=%d, cursize=%d, maxfiles=%d, numfiles=%d), exiting"
-				, max_size, total_size, max_files, cachefiles.size());
-			return;
-		}
-
-		sort(begin(cachefiles), end(cachefiles), [](cache_item const& a, cache_item const& b) {
-			return a.first < b.first;
-		});
-
-		int deleted = 0;
-		for (auto const& i : cachefiles) {
-			// stop cleaning?
-			if ((total_size <= max_size && cachefiles.size() - deleted <= max_files) || cachefiles.size() - deleted < 2)
-				break;
-
-			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.GetMessage();
-				continue;
-			}
-
-			total_size -= size;
-			++deleted;
-		}
-
-		LOG_D("utils/clean_cache") << "deleted " << deleted << " files, exiting";
-	});
+void CleanCache(agi::fs::path const &directory, std::string const &file_type, uint64_t max_size, uint64_t max_files)
+{
+    static std::unique_ptr<agi::dispatch::Queue> queue;
+    if (!queue)
+        queue = agi::dispatch::Create();
+
+    max_size <<= 20;
+    if (max_files == 0)
+        max_files = std::numeric_limits<uint64_t>::max();
+    queue->Async([ = ] {
+        LOG_D("utils/clean_cache") << "cleaning " << directory / file_type;
+        uint64_t total_size = 0;
+        using cache_item = std::pair<int64_t, agi::fs::path>;
+        std::vector<cache_item> cachefiles;
+        for (auto const &file : agi::fs::DirectoryIterator(directory, file_type))
+        {
+            agi::fs::path path = directory / file;
+            cachefiles.push_back({agi::fs::ModifiedTime(path), path});
+            total_size += agi::fs::Size(path);
+        }
+
+        if (cachefiles.size() <= max_files && total_size <= max_size)
+        {
+            LOG_D("utils/clean_cache") << agi::format("cache does not need cleaning (maxsize=%d, cursize=%d, maxfiles=%d, numfiles=%d), exiting"
+                                       , max_size, total_size, max_files, cachefiles.size());
+            return;
+        }
+
+        sort(begin(cachefiles), end(cachefiles), [](cache_item const & a, cache_item const & b)
+        {
+            return a.first < b.first;
+        });
+
+        int deleted = 0;
+        for (auto const &i : cachefiles)
+        {
+            // stop cleaning?
+            if ((total_size <= max_size && cachefiles.size() - deleted <= max_files) || cachefiles.size() - deleted < 2)
+                break;
+
+            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.GetMessage();
+                continue;
+            }
+
+            total_size -= size;
+            ++deleted;
+        }
+
+        LOG_D("utils/clean_cache") << "deleted " << deleted << " files, exiting";
+    });
 }
 
 #ifndef __WXOSX_COCOA__
@@ -219,71 +232,78 @@ RetinaHelper::~RetinaHelper() { }
 int RetinaHelper::GetScaleFactor() const { return 1; }
 
 // OS X implementation in scintilla_ime.mm
-namespace osx { namespace ime {
-	void inject(wxStyledTextCtrl *) { }
-	void invalidate(wxStyledTextCtrl *) { }
-	bool process_key_event(wxStyledTextCtrl *, wxKeyEvent&) { return false; }
-} }
+namespace osx {
+namespace ime {
+void inject(wxStyledTextCtrl *) { }
+void invalidate(wxStyledTextCtrl *) { }
+bool process_key_event(wxStyledTextCtrl *, wxKeyEvent &) { return false; }
+}
+}
 #endif
 
-wxString FontFace(std::string opt_prefix) {
-	opt_prefix += "/Font Face";
-	auto value = OPT_GET(opt_prefix)->GetString();
+wxString FontFace(std::string opt_prefix)
+{
+    opt_prefix += "/Font Face";
+    auto value = OPT_GET(opt_prefix)->GetString();
 #ifdef __WXOSX_COCOA__
-	if (value.empty()) {
-		auto default_font = CTFontCreateUIFontForLanguage(kCTFontUserFontType, 0, nullptr);
-		auto default_font_name = CTFontCopyPostScriptName(default_font);
-		CFRelease(default_font);
-
-		auto utf8_str = CFStringGetCStringPtr(default_font_name, kCFStringEncodingUTF8);
-		if (utf8_str)
-			value = utf8_str;
-		else {
-			char buffer[1024];
-			CFStringGetCString(default_font_name, buffer, sizeof(buffer), kCFStringEncodingUTF8);
-			buffer[1023] = '\0';
-			value = buffer;
-		}
-
-		CFRelease(default_font_name);
-	}
+    if (value.empty()) {
+        auto default_font = CTFontCreateUIFontForLanguage(kCTFontUserFontType, 0, nullptr);
+        auto default_font_name = CTFontCopyPostScriptName(default_font);
+        CFRelease(default_font);
+
+        auto utf8_str = CFStringGetCStringPtr(default_font_name, kCFStringEncodingUTF8);
+        if (utf8_str)
+            value = utf8_str;
+        else {
+            char buffer[1024];
+            CFStringGetCString(default_font_name, buffer, sizeof(buffer), kCFStringEncodingUTF8);
+            buffer[1023] = '\0';
+            value = buffer;
+        }
+
+        CFRelease(default_font_name);
+    }
 #endif
-	return to_wx(value);
+    return to_wx(value);
 }
 
-static agi::fs::path FileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, std::string const& wildcard, int flags, wxWindow *parent) {
-	wxString path;
-	if (!option_name.empty())
-		path = to_wx(OPT_GET(option_name)->GetString());
-	agi::fs::path filename = wxFileSelector(message, path, to_wx(default_filename), to_wx(default_extension), to_wx(wildcard), flags, parent).wx_str();
-	if (!filename.empty() && !option_name.empty())
-		OPT_SET(option_name)->SetString(filename.parent_path().string());
-	return filename;
+static agi::fs::path FileSelector(wxString const &message, std::string const &option_name, std::string const &default_filename, std::string const &default_extension, std::string const &wildcard, int flags, wxWindow *parent)
+{
+    wxString path;
+    if (!option_name.empty())
+        path = to_wx(OPT_GET(option_name)->GetString());
+    agi::fs::path filename = wxFileSelector(message, path, to_wx(default_filename), to_wx(default_extension), to_wx(wildcard), flags, parent).wx_str();
+    if (!filename.empty() && !option_name.empty())
+        OPT_SET(option_name)->SetString(filename.parent_path().string());
+    return filename;
 }
 
-agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, std::string const& wildcard, wxWindow *parent) {
-	return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST, parent);
+agi::fs::path OpenFileSelector(wxString const &message, std::string const &option_name, std::string const &default_filename, std::string const &default_extension, std::string const &wildcard, wxWindow *parent)
+{
+    return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST, parent);
 }
 
-agi::fs::path SaveFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, std::string const& wildcard, wxWindow *parent) {
-	return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
+agi::fs::path SaveFileSelector(wxString const &message, std::string const &option_name, std::string const &default_filename, std::string const &default_extension, std::string const &wildcard, wxWindow *parent)
+{
+    return FileSelector(message, option_name, default_filename, default_extension, wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT, parent);
 }
 
-wxString LocalizedLanguageName(wxString const& lang) {
-	icu::Locale iculoc(lang.c_str());
-	if (!iculoc.isBogus()) {
-		icu::UnicodeString ustr;
-		iculoc.getDisplayName(iculoc, ustr);
+wxString LocalizedLanguageName(wxString const &lang)
+{
+    icu::Locale iculoc(lang.c_str());
+    if (!iculoc.isBogus()) {
+        icu::UnicodeString ustr;
+        iculoc.getDisplayName(iculoc, ustr);
 #ifdef _MSC_VER
-		return wxString(ustr.getBuffer());
+        return wxString(ustr.getBuffer());
 #else
-		std::string utf8;
-		ustr.toUTF8String(utf8);
-		return to_wx(utf8);
+        std::string utf8;
+        ustr.toUTF8String(utf8);
+        return to_wx(utf8);
 #endif
-	}
+    }
 
-	if (auto info = wxLocale::FindLanguageInfo(lang))
-		return info->Description;
-	return lang;
+    if (auto info = wxLocale::FindLanguageInfo(lang))
+        return info->Description;
+    return lang;
 }
diff --git a/src/utils.h b/src/utils.h
index 269100de809a8133b71ccc4e1849a7a481fb89bf..2c19761b69b052803a12ec6f8c63f8bf36e74957 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -73,46 +73,47 @@ 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(agi::fs::path const& directory, std::string const& file_type, uint64_t max_size, uint64_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 <typename T> T tabs(T x) { return x < 0 ? -x : x; }
 
 /// Get the middle value of a, b, and c (i.e. clamp b to [a,c])
 /// @precondition a <= c
-template<typename T> inline T mid(T a, T b, T c) {
-	return a > b ? a : (b > c ? c : b);
+template<typename T> inline T mid(T a, T b, T c)
+{
+    return a > b ? a : (b > c ? c : b);
 }
 
 /// Get the text contents of the clipboard, or empty string on failure
 std::string GetClipboard();
 /// Try to set the clipboard to the given string
-void SetClipboard(std::string const& new_value);
-void SetClipboard(wxBitmap const& new_value);
+void SetClipboard(std::string const &new_value);
+void SetClipboard(wxBitmap const &new_value);
 
 #define countof(array) (sizeof(array) / sizeof(array[0]))
 
 wxString FontFace(std::string opt_prefix);
 
-agi::fs::path OpenFileSelector(wxString const& message, std::string const& option_name, std::string const& default_filename, std::string const& default_extension, std::string 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, std::string const& wildcard, wxWindow *parent);
+agi::fs::path OpenFileSelector(wxString const &message, std::string const &option_name, std::string const &default_filename, std::string const &default_extension, std::string 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, std::string const &wildcard, wxWindow *parent);
 
-wxString LocalizedLanguageName(wxString const& lang);
+wxString LocalizedLanguageName(wxString const &lang);
 
 namespace osx {
-	/// Make the given menu the OS X Window menu
-	void make_windows_menu(wxMenu *wxmenu);
-	/// Activate a top-level document window other than the given one
-	bool activate_top_window_other_than(wxFrame *wx);
-	// Bring all windows to the front, maintaining relative z-order
-	void bring_to_front();
+/// Make the given menu the OS X Window menu
+void make_windows_menu(wxMenu *wxmenu);
+/// Activate a top-level document window other than the given one
+bool activate_top_window_other_than(wxFrame *wx);
+// Bring all windows to the front, maintaining relative z-order
+void bring_to_front();
 
 namespace ime {
-	/// Inject the IME helper into the given wxSTC
-	void inject(wxStyledTextCtrl *ctrl);
-	/// Invalidate any pending text from the IME
-	void invalidate(wxStyledTextCtrl *ctrl);
-	/// Give the IME a chance to process a key event and return whether it did
-	bool process_key_event(wxStyledTextCtrl *ctrl, wxKeyEvent &);
+/// Inject the IME helper into the given wxSTC
+void inject(wxStyledTextCtrl *ctrl);
+/// Invalidate any pending text from the IME
+void invalidate(wxStyledTextCtrl *ctrl);
+/// Give the IME a chance to process a key event and return whether it did
+bool process_key_event(wxStyledTextCtrl *ctrl, wxKeyEvent &);
 }
 }
diff --git a/src/validators.cpp b/src/validators.cpp
index ac6e024a644d72d40fd28ff863e8d49c6affc9f3..e42d59d85d88c6b1f27ff59a5af80b211eaaf45a 100644
--- a/src/validators.cpp
+++ b/src/validators.cpp
@@ -26,183 +26,196 @@
 #include <wx/textctrl.h>
 
 namespace {
-std::string new_value(wxTextCtrl *ctrl, int chr) {
-	long from, to;
-	ctrl->GetSelection(&from, &to);
-	auto value = ctrl->GetValue();
-	return from_wx(value.substr(0, from) + (wxChar)chr + value.substr(to));
+std::string new_value(wxTextCtrl *ctrl, int chr)
+{
+    long from, to;
+    ctrl->GetSelection(&from, &to);
+    auto value = ctrl->GetValue();
+    return from_wx(value.substr(0, from) + (wxChar)chr + value.substr(to));
 }
 
-wxChar decimal_separator() {
-	auto sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
-	return sep.empty() ? '.' : sep[0];
+wxChar decimal_separator()
+{
+    auto sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
+    return sep.empty() ? '.' : sep[0];
 }
 }
 
 IntValidator::IntValidator(int val, bool allow_negative)
-: value(val)
-, allow_negative(allow_negative)
+    : value(val)
+    , allow_negative(allow_negative)
 {
-	Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
+    Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
 }
 
-IntValidator::IntValidator(IntValidator const& rgt)
-: value(rgt.value)
-, allow_negative(rgt.allow_negative)
+IntValidator::IntValidator(IntValidator const &rgt)
+    : value(rgt.value)
+    , allow_negative(rgt.allow_negative)
 {
-	SetWindow(rgt.GetWindow());
-	Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
+    SetWindow(rgt.GetWindow());
+    Bind(wxEVT_CHAR, &IntValidator::OnChar, this);
 }
 
-bool IntValidator::TransferToWindow() {
-	static_cast<wxTextCtrl *>(GetWindow())->SetValue(std::to_wstring(value));
-	return true;
+bool IntValidator::TransferToWindow()
+{
+    static_cast<wxTextCtrl *>(GetWindow())->SetValue(std::to_wstring(value));
+    return true;
 }
 
-void IntValidator::OnChar(wxKeyEvent& event) {
-	int chr = event.GetKeyCode();
-	if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
-		event.Skip();
-		return;
-	}
-
-	auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
-	auto str = new_value(ctrl, chr);
-	int parsed;
-	if (allow_negative && str == "-")
-		event.Skip();
-	else if (agi::util::try_parse(str, &parsed) && (allow_negative || parsed >= 0))
-		event.Skip();
-	else if (!wxValidator::IsSilent())
-		wxBell();
+void IntValidator::OnChar(wxKeyEvent &event)
+{
+    int chr = event.GetKeyCode();
+    if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
+        event.Skip();
+        return;
+    }
+
+    auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
+    auto str = new_value(ctrl, chr);
+    int parsed;
+    if (allow_negative && str == "-")
+        event.Skip();
+    else if (agi::util::try_parse(str, &parsed) && (allow_negative || parsed >= 0))
+        event.Skip();
+    else if (!wxValidator::IsSilent())
+        wxBell();
 }
 
 DoubleValidator::DoubleValidator(double *val, bool allow_negative)
-: value(val)
-, min(allow_negative ? std::numeric_limits<double>::lowest() : 0)
-, max(std::numeric_limits<double>::max())
-, decimal_sep(decimal_separator())
+    : value(val)
+    , min(allow_negative ? std::numeric_limits<double>::lowest() : 0)
+    , max(std::numeric_limits<double>::max())
+    , decimal_sep(decimal_separator())
 {
-	Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
+    Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
 }
 
 DoubleValidator::DoubleValidator(double *val, double min, double max)
-: value(val)
-, min(min)
-, max(max)
-, decimal_sep(decimal_separator())
-{
-	Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
-}
-
-DoubleValidator::DoubleValidator(DoubleValidator const& rgt)
-: value(rgt.value)
-, min(rgt.min)
-, max(rgt.max)
-, decimal_sep(rgt.decimal_sep)
-{
-	Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
-	SetWindow(rgt.GetWindow());
-}
-
-void DoubleValidator::OnChar(wxKeyEvent& event) {
-	int chr = event.GetKeyCode();
-	if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
-		event.Skip();
-		return;
-	}
-
-	if (chr == decimal_sep)
-		chr = '.';
-
-	auto str = new_value(static_cast<wxTextCtrl *>(GetWindow()), chr);
-	if (decimal_sep != '.')
-		replace(begin(str), end(str), (char)decimal_sep, '.');
-
-	double parsed;
-	bool can_parse = agi::util::try_parse(str, &parsed);
-	if ((min < 0 && str == "-") || str == ".")
-		event.Skip();
-	else if (can_parse && parsed >= min && parsed <= max)
-		event.Skip();
-	else if (can_parse && min < 0 && chr == '-') // allow negating an existing value even if it results in being out of range
-		event.Skip();
-	else if (!wxValidator::IsSilent())
-		wxBell();
-}
-
-bool DoubleValidator::TransferToWindow() {
-	auto str = std::to_wstring(*value);
-	if (decimal_sep != '.')
-		std::replace(str.begin(), str.end(), L'.', decimal_sep);
-	if (str.find(decimal_sep) != str.npos) {
-		while (str.back() == '0')
-			str.pop_back();
-	}
-	static_cast<wxTextCtrl *>(GetWindow())->SetValue(str);
-	return true;
-}
-
-bool DoubleValidator::TransferFromWindow() {
-	auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
-	if (!Validate(ctrl)) return false;
-	auto str = from_wx(ctrl->GetValue());
-	if (decimal_sep != '.')
-		replace(begin(str), end(str), (char)decimal_sep, '.');
-	agi::util::try_parse(str, value);
-	return true;
-}
-
-bool DoubleSpinValidator::TransferToWindow() {
-	static_cast<wxSpinCtrlDouble*>(GetWindow())->SetValue(*value);
-	return true;
-}
-
-bool DoubleSpinValidator::TransferFromWindow() {
-	auto ctrl = static_cast<wxSpinCtrlDouble*>(GetWindow());
+    : value(val)
+    , min(min)
+    , max(max)
+    , decimal_sep(decimal_separator())
+{
+    Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
+}
+
+DoubleValidator::DoubleValidator(DoubleValidator const &rgt)
+    : value(rgt.value)
+    , min(rgt.min)
+    , max(rgt.max)
+    , decimal_sep(rgt.decimal_sep)
+{
+    Bind(wxEVT_CHAR, &DoubleValidator::OnChar, this);
+    SetWindow(rgt.GetWindow());
+}
+
+void DoubleValidator::OnChar(wxKeyEvent &event)
+{
+    int chr = event.GetKeyCode();
+    if (chr < WXK_SPACE || chr == WXK_DELETE || chr > WXK_START) {
+        event.Skip();
+        return;
+    }
+
+    if (chr == decimal_sep)
+        chr = '.';
+
+    auto str = new_value(static_cast<wxTextCtrl *>(GetWindow()), chr);
+    if (decimal_sep != '.')
+        replace(begin(str), end(str), (char)decimal_sep, '.');
+
+    double parsed;
+    bool can_parse = agi::util::try_parse(str, &parsed);
+    if ((min < 0 && str == "-") || str == ".")
+        event.Skip();
+    else if (can_parse && parsed >= min && parsed <= max)
+        event.Skip();
+    else if (can_parse && min < 0 && chr == '-') // allow negating an existing value even if it results in being out of range
+        event.Skip();
+    else if (!wxValidator::IsSilent())
+        wxBell();
+}
+
+bool DoubleValidator::TransferToWindow()
+{
+    auto str = std::to_wstring(*value);
+    if (decimal_sep != '.')
+        std::replace(str.begin(), str.end(), L'.', decimal_sep);
+    if (str.find(decimal_sep) != str.npos) {
+        while (str.back() == '0')
+            str.pop_back();
+    }
+    static_cast<wxTextCtrl *>(GetWindow())->SetValue(str);
+    return true;
+}
+
+bool DoubleValidator::TransferFromWindow()
+{
+    auto ctrl = static_cast<wxTextCtrl *>(GetWindow());
+    if (!Validate(ctrl)) return false;
+    auto str = from_wx(ctrl->GetValue());
+    if (decimal_sep != '.')
+        replace(begin(str), end(str), (char)decimal_sep, '.');
+    agi::util::try_parse(str, value);
+    return true;
+}
+
+bool DoubleSpinValidator::TransferToWindow()
+{
+    static_cast<wxSpinCtrlDouble *>(GetWindow())->SetValue(*value);
+    return true;
+}
+
+bool DoubleSpinValidator::TransferFromWindow()
+{
+    auto ctrl = static_cast<wxSpinCtrlDouble *>(GetWindow());
 #ifndef wxHAS_NATIVE_SPINCTRLDOUBLE
-	wxFocusEvent evt;
-	ctrl->OnTextLostFocus(evt);
+    wxFocusEvent evt;
+    ctrl->OnTextLostFocus(evt);
 #endif
-	*value = ctrl->GetValue();
-	return true;
-}
-
-int EnumBinderBase::Get() {
-	if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
-		return rb->GetSelection();
-	if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
-		return rb->GetSelection();
-	throw agi::InternalError("Control type not supported by EnumBinder");
-}
-
-void EnumBinderBase::Set(int value) {
-	if (auto rb = dynamic_cast<wxRadioBox*>(GetWindow()))
-		rb->SetSelection(value);
-	else if (auto rb = dynamic_cast<wxComboBox*>(GetWindow()))
-		rb->SetSelection(value);
-	else
-		throw agi::InternalError("Control type not supported by EnumBinder");
-}
-
-bool StringBinder::TransferFromWindow() {
-	wxWindow *window = GetWindow();
-	if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
-		*value = from_wx(ctrl->GetValue());
-	else if (wxComboBox *ctrl = dynamic_cast<wxComboBox*>(window))
-		*value = from_wx(ctrl->GetValue());
-	else
-		throw agi::InternalError("Unsupported control type");
-	return true;
-}
-
-bool StringBinder::TransferToWindow() {
-	wxWindow *window = GetWindow();
-	if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl*>(window))
-		ctrl->SetValue(to_wx(*value));
-	else if (wxComboBox *ctrl = dynamic_cast<wxComboBox*>(window))
-		ctrl->SetValue(to_wx(*value));
-	else
-		throw agi::InternalError("Unsupported control type");
-	return true;
+    *value = ctrl->GetValue();
+    return true;
+}
+
+int EnumBinderBase::Get()
+{
+    if (auto rb = dynamic_cast<wxRadioBox *>(GetWindow()))
+        return rb->GetSelection();
+    if (auto rb = dynamic_cast<wxComboBox *>(GetWindow()))
+        return rb->GetSelection();
+    throw agi::InternalError("Control type not supported by EnumBinder");
+}
+
+void EnumBinderBase::Set(int value)
+{
+    if (auto rb = dynamic_cast<wxRadioBox *>(GetWindow()))
+        rb->SetSelection(value);
+    else if (auto rb = dynamic_cast<wxComboBox *>(GetWindow()))
+        rb->SetSelection(value);
+    else
+        throw agi::InternalError("Control type not supported by EnumBinder");
+}
+
+bool StringBinder::TransferFromWindow()
+{
+    wxWindow *window = GetWindow();
+    if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl *>(window))
+        * value = from_wx(ctrl->GetValue());
+    else if (wxComboBox *ctrl = dynamic_cast<wxComboBox *>(window))
+        * value = from_wx(ctrl->GetValue());
+    else
+        throw agi::InternalError("Unsupported control type");
+    return true;
+}
+
+bool StringBinder::TransferToWindow()
+{
+    wxWindow *window = GetWindow();
+    if (wxTextCtrl *ctrl = dynamic_cast<wxTextCtrl *>(window))
+        ctrl->SetValue(to_wx(*value));
+    else if (wxComboBox *ctrl = dynamic_cast<wxComboBox *>(window))
+        ctrl->SetValue(to_wx(*value));
+    else
+        throw agi::InternalError("Unsupported control type");
+    return true;
 }
diff --git a/src/validators.h b/src/validators.h
index ae55d192cf9fda6ba476b8817af02bf9480a784d..8874fdb8496d46a0cad55bc7523231343947a4b9 100644
--- a/src/validators.h
+++ b/src/validators.h
@@ -22,97 +22,98 @@
 #include <wx/validate.h>
 
 class IntValidator final : public wxValidator {
-	int value;
-	bool allow_negative;
+    int value;
+    bool allow_negative;
 
-	bool CheckCharacter(int chr, bool is_first) const;
-	void OnChar(wxKeyEvent& event);
+    bool CheckCharacter(int chr, bool is_first) const;
+    void OnChar(wxKeyEvent &event);
 
-	bool Validate(wxWindow *) override { return true; }
-	wxObject* Clone() const override { return new IntValidator(*this); }
-	bool TransferToWindow() override;
-	bool TransferFromWindow() override { return true; }
+    bool Validate(wxWindow *) override { return true; }
+    wxObject *Clone() const override { return new IntValidator(*this); }
+    bool TransferToWindow() override;
+    bool TransferFromWindow() override { return true; }
 
-	IntValidator(IntValidator const& rgt);
+    IntValidator(IntValidator const &rgt);
 
 public:
-	explicit IntValidator(int val=0, bool allow_negative=false);
+    explicit IntValidator(int val = 0, bool allow_negative = false);
 };
 
 class DoubleValidator final : public wxValidator {
-	double *value;
-	double min;
-	double max;
-	wxChar decimal_sep;
+    double *value;
+    double min;
+    double max;
+    wxChar decimal_sep;
 
-	bool Validate(wxWindow* parent) override { return true; }
-	bool CheckCharacter(int chr, bool is_first, bool *got_decimal) const;
-	void OnChar(wxKeyEvent& event);
+    bool Validate(wxWindow *parent) override { return true; }
+    bool CheckCharacter(int chr, bool is_first, bool *got_decimal) const;
+    void OnChar(wxKeyEvent &event);
 
-	DoubleValidator(DoubleValidator const& rgt);
+    DoubleValidator(DoubleValidator const &rgt);
 
-	wxObject* Clone() const override { return new DoubleValidator(*this); }
-	bool TransferToWindow() override;
-	bool TransferFromWindow() override;
+    wxObject *Clone() const override { return new DoubleValidator(*this); }
+    bool TransferToWindow() override;
+    bool TransferFromWindow() override;
 
 public:
-	explicit DoubleValidator(double *val, bool allow_negative=false);
-	explicit DoubleValidator(double *val, double min, double max);
+    explicit DoubleValidator(double *val, bool allow_negative = false);
+    explicit DoubleValidator(double *val, double min, double max);
 };
 
 class DoubleSpinValidator final : public wxValidator {
-	double *value;
-	wxValidator *Clone() const override { return new DoubleSpinValidator(value); }
-	bool Validate(wxWindow*) override { return true; }
-	bool TransferToWindow() override;
-	bool TransferFromWindow() override;
+    double *value;
+    wxValidator *Clone() const override { return new DoubleSpinValidator(value); }
+    bool Validate(wxWindow *) override { return true; }
+    bool TransferToWindow() override;
+    bool TransferFromWindow() override;
 
 public:
-	DoubleSpinValidator(double *value) : value(value) { }
+    DoubleSpinValidator(double *value) : value(value) { }
 };
 
 class EnumBinderBase : public wxValidator {
-	bool Validate(wxWindow *) override { return true; }
+    bool Validate(wxWindow *) override { return true; }
 
 protected:
-	int Get();
-	void Set(int value);
+    int Get();
+    void Set(int value);
 };
 
 template<typename T>
 class EnumBinder final : public EnumBinderBase {
-	T *value;
+    T *value;
 
-	wxObject *Clone() const override { return new EnumBinder<T>(value); }
+    wxObject *Clone() const override { return new EnumBinder<T>(value); }
 
-	bool TransferFromWindow() override {
-		*value = static_cast<T>(Get());
-		return true;
-	}
+    bool TransferFromWindow() override {
+        *value = static_cast<T>(Get());
+        return true;
+    }
 
-	bool TransferToWindow() override {
-		Set(static_cast<int>(*value));
-		return true;
-	}
+    bool TransferToWindow() override {
+        Set(static_cast<int>(*value));
+        return true;
+    }
 
 public:
-	explicit EnumBinder(T *value) : value(value) { }
-	EnumBinder(EnumBinder const& rhs) : EnumBinderBase(rhs), value(rhs.value) { }
+    explicit EnumBinder(T *value) : value(value) { }
+    EnumBinder(EnumBinder const &rhs) : EnumBinderBase(rhs), value(rhs.value) { }
 };
 
 template<typename T>
-EnumBinder<T> MakeEnumBinder(T *value) {
-	return EnumBinder<T>(value);
+EnumBinder<T> MakeEnumBinder(T *value)
+{
+    return EnumBinder<T>(value);
 }
 
 class StringBinder final : public wxValidator {
-	std::string *value;
+    std::string *value;
 
-	wxObject* Clone() const override { return new StringBinder(value); }
-	bool Validate(wxWindow*) override { return true;}
-	bool TransferToWindow() override;
-	bool TransferFromWindow() override;
+    wxObject *Clone() const override { return new StringBinder(value); }
+    bool Validate(wxWindow *) override { return true;}
+    bool TransferToWindow() override;
+    bool TransferFromWindow() override;
 
 public:
-	explicit StringBinder(std::string *value) : value(value) { }
+    explicit StringBinder(std::string *value) : value(value) { }
 };
diff --git a/src/value_event.h b/src/value_event.h
index 23a434988222addc14cc64dad9aeafda817e325f..d23ea0ebe369a671ff0453359fabe8455e10fcb0 100644
--- a/src/value_event.h
+++ b/src/value_event.h
@@ -21,16 +21,16 @@
 /// A wxEvent which holds a single templated value
 template<typename T>
 class ValueEvent : public wxEvent {
-	const T value;
+    const T value;
 
 public:
-	ValueEvent(wxEventType type, int id, T value)
-	: wxEvent(id, type)
-	, value(std::move(value))
-	{ }
+    ValueEvent(wxEventType type, int id, T value)
+        : wxEvent(id, type)
+        , value(std::move(value))
+    { }
 
-	wxEvent *Clone() const override;
-	T const& Get() const { return value; }
+    wxEvent *Clone() const override;
+    T const &Get() const { return value; }
 };
 
 // Defined out-of-line so that `extern template` can suppress the emission of
diff --git a/src/vector2d.cpp b/src/vector2d.cpp
index 8bb81de3ccfae7993da9df72fe8d6b5c0d4b2d62..40c04499f6b32429d3755a62168cd6b2f2d87b7d 100644
--- a/src/vector2d.cpp
+++ b/src/vector2d.cpp
@@ -28,65 +28,78 @@
 #include <limits>
 
 Vector2D::Vector2D()
-: x(std::numeric_limits<float>::min())
-, y(std::numeric_limits<float>::min())
+    : x(std::numeric_limits<float>::min())
+    , y(std::numeric_limits<float>::min())
 {
 }
 
-Vector2D operator *(float f, Vector2D v) {
-	return Vector2D(v.X() * f, v.Y() * f);
+Vector2D operator *(float f, Vector2D v)
+{
+    return Vector2D(v.X() * f, v.Y() * f);
 }
 
-Vector2D operator /(float f, Vector2D v) {
-	return Vector2D(f / v.X(), f / v.Y());
+Vector2D operator /(float f, Vector2D v)
+{
+    return Vector2D(f / v.X(), f / v.Y());
 }
 
-Vector2D operator +(float f, Vector2D v) {
-	return Vector2D(v.X() + f, v.Y() + f);
+Vector2D operator +(float f, Vector2D v)
+{
+    return Vector2D(v.X() + f, v.Y() + f);
 }
 
-Vector2D operator -(float f, Vector2D v) {
-	return Vector2D(f - v.X(), f - v.Y());
+Vector2D operator -(float f, Vector2D v)
+{
+    return Vector2D(f - v.X(), f - v.Y());
 }
 
-Vector2D Vector2D::Unit() const {
-	float len = Len();
-	if (len == 0)
-		return Vector2D(0, 0);
-	return *this / len;
+Vector2D Vector2D::Unit() const
+{
+    float len = Len();
+    if (len == 0)
+        return Vector2D(0, 0);
+    return *this / len;
 }
 
-Vector2D Vector2D::SingleAxis() const {
-	if (std::abs(x) < std::abs(y))
-		return Vector2D(0, y);
-	else
-		return Vector2D(x, 0);
+Vector2D Vector2D::SingleAxis() const
+{
+    if (std::abs(x) < std::abs(y))
+        return Vector2D(0, y);
+    else
+        return Vector2D(x, 0);
 }
 
-Vector2D Vector2D::Max(Vector2D param) const {
-	return Vector2D(std::max(x, param.x), std::max(y, param.y));
+Vector2D Vector2D::Max(Vector2D param) const
+{
+    return Vector2D(std::max(x, param.x), std::max(y, param.y));
 }
 
-Vector2D Vector2D::Min(Vector2D param) const {
-	return Vector2D(std::min(x, param.x), std::min(y, param.y));
+Vector2D Vector2D::Min(Vector2D param) const
+{
+    return Vector2D(std::min(x, param.x), std::min(y, param.y));
 }
 
-Vector2D Vector2D::Round(float step) const {
-	return Vector2D(floorf(x / step + .5f) * step, floorf(y / step + .5f) * step);
+Vector2D Vector2D::Round(float step) const
+{
+    return Vector2D(floorf(x / step + .5f) * step, floorf(y / step + .5f) * step);
 }
 
-Vector2D::operator bool() const {
-	return *this != Vector2D();
+Vector2D::operator bool() const
+{
+    return *this != Vector2D();
 }
 
-std::string Vector2D::PStr(char sep) const {
-	return "(" + Str(sep) + ")";
+std::string Vector2D::PStr(char sep) const
+{
+    return "(" + Str(sep) + ")";
 }
 
-std::string Vector2D::DStr(char sep) const {
-	return agi::format("%d%c%d", (int)x, sep, (int)y);
+std::string Vector2D::DStr(char sep) const
+{
+    return agi::format("%d%c%d", (int)x, sep, (int)y);
 }
 
-std::string Vector2D::Str(char sep) const {
-	return float_to_string(x) + sep + float_to_string(y);
+std::string Vector2D::Str(char sep) const
+{
+    return float_to_string(x) + sep + float_to_string(y);
 }
diff --git a/src/vector2d.h b/src/vector2d.h
index 3f353434e0c7530dc0a4fc27e577b0012789d10c..9c48168ebfc024b0a25b0daf3cb017fb9457f1a5 100644
--- a/src/vector2d.h
+++ b/src/vector2d.h
@@ -26,57 +26,57 @@
 #include <wx/gdicmn.h>
 
 class Vector2D {
-	float x, y;
+    float x, y;
 
 public:
-	float X() const { return x; }
-	float Y() const { return y; }
-
-	Vector2D();
-	Vector2D(float x, float y) : x(x), y(y) { }
-	Vector2D(wxPoint pt) : x(pt.x), y(pt.y) { }
-	Vector2D(Vector2D x, Vector2D y) : x(x.x), y(y.y) { }
-	Vector2D(float x, Vector2D y) : x(x), y(y.y) { }
-	Vector2D(Vector2D x, float y) : x(x.x), y(y) { }
-
-	bool operator ==(const Vector2D r) const { return x == r.x && y == r.y; }
-	bool operator !=(const Vector2D r) const { return x != r.x || y != r.y; }
-	explicit operator bool() const;
-
-	Vector2D operator -() const { return Vector2D(-x, -y); }
-	Vector2D operator +(const Vector2D r) const { return Vector2D(x + r.x, y + r.y); }
-	Vector2D operator -(const Vector2D r) const { return Vector2D(x - r.x, y - r.y); }
-	Vector2D operator *(const Vector2D r) const { return Vector2D(x * r.x, y * r.y); }
-	Vector2D operator /(const Vector2D r) const { return Vector2D(x / r.x, y / r.y); }
-	Vector2D operator +(float param) const { return Vector2D(x + param, y + param); }
-	Vector2D operator -(float param) const { return Vector2D(x - param, y - param); }
-	Vector2D operator *(float param) const { return Vector2D(x * param, y * param); }
-	Vector2D operator /(float param) const { return Vector2D(x / param, y / param); }
-
-	Vector2D Unit() const;
-	Vector2D SingleAxis() const;
-
-	Vector2D Perpendicular() const { return Vector2D(-y, x); }
-
-	Vector2D Max(Vector2D param) const;
-	Vector2D Min(Vector2D param) const;
-	Vector2D Round(float step) const;
-
-	float Cross(const Vector2D param) const { return x * param.y - y * param.x; }
-	float Dot(const Vector2D param) const { return x * param.x + y * param.y; }
-
-	float Len() const { return sqrt(x*x + y*y); }
-	float SquareLen() const { return x*x + y*y; }
-	float Angle() const { return atan2(y, x); }
-
-	/// Get as string with given separator
-	std::string Str(char sep = ',') const;
-	/// Get as string surrounded by parentheses with given separator
-	std::string PStr(char sep = ',') const;
-	/// Get as string with given separator with values rounded to ints
-	std::string DStr(char sep = ',') const;
-
-	static Vector2D FromAngle(float angle) { return Vector2D(cos(-angle), sin(-angle)); }
+    float X() const { return x; }
+    float Y() const { return y; }
+
+    Vector2D();
+    Vector2D(float x, float y) : x(x), y(y) { }
+    Vector2D(wxPoint pt) : x(pt.x), y(pt.y) { }
+    Vector2D(Vector2D x, Vector2D y) : x(x.x), y(y.y) { }
+    Vector2D(float x, Vector2D y) : x(x), y(y.y) { }
+    Vector2D(Vector2D x, float y) : x(x.x), y(y) { }
+
+    bool operator ==(const Vector2D r) const { return x == r.x && y == r.y; }
+    bool operator !=(const Vector2D r) const { return x != r.x || y != r.y; }
+    explicit operator bool() const;
+
+    Vector2D operator -() const { return Vector2D(-x, -y); }
+    Vector2D operator +(const Vector2D r) const { return Vector2D(x + r.x, y + r.y); }
+    Vector2D operator -(const Vector2D r) const { return Vector2D(x - r.x, y - r.y); }
+    Vector2D operator *(const Vector2D r) const { return Vector2D(x * r.x, y * r.y); }
+    Vector2D operator /(const Vector2D r) const { return Vector2D(x / r.x, y / r.y); }
+    Vector2D operator +(float param) const { return Vector2D(x + param, y + param); }
+    Vector2D operator -(float param) const { return Vector2D(x - param, y - param); }
+    Vector2D operator *(float param) const { return Vector2D(x * param, y * param); }
+    Vector2D operator /(float param) const { return Vector2D(x / param, y / param); }
+
+    Vector2D Unit() const;
+    Vector2D SingleAxis() const;
+
+    Vector2D Perpendicular() const { return Vector2D(-y, x); }
+
+    Vector2D Max(Vector2D param) const;
+    Vector2D Min(Vector2D param) const;
+    Vector2D Round(float step) const;
+
+    float Cross(const Vector2D param) const { return x * param.y - y * param.x; }
+    float Dot(const Vector2D param) const { return x * param.x + y * param.y; }
+
+    float Len() const { return sqrt(x * x + y * y); }
+    float SquareLen() const { return x * x + y * y; }
+    float Angle() const { return atan2(y, x); }
+
+    /// Get as string with given separator
+    std::string Str(char sep = ',') const;
+    /// Get as string surrounded by parentheses with given separator
+    std::string PStr(char sep = ',') const;
+    /// Get as string with given separator with values rounded to ints
+    std::string DStr(char sep = ',') const;
+
+    static Vector2D FromAngle(float angle) { return Vector2D(cos(-angle), sin(-angle)); }
 };
 
 Vector2D operator * (float f, Vector2D v);
diff --git a/src/version.cpp b/src/version.cpp
index 9851dd719b5fa22c20971967986dce7d093c1461..f7cc870e6d9471ed12288ecd54f6f0a320e87eba 100644
--- a/src/version.cpp
+++ b/src/version.cpp
@@ -36,52 +36,60 @@
 #include "git_version.h"
 
 #ifdef _DEBUG
-	#define DEBUG_SUFFIX " [DEBUG VERSION]"
+#define DEBUG_SUFFIX " [DEBUG VERSION]"
 #else
-	#define DEBUG_SUFFIX ""
+#define DEBUG_SUFFIX ""
 #endif
 
 #if defined(BUILD_CREDIT) && !TAGGED_RELEASE
-	#define BUILD_CREDIT_SUFFIX ", " BUILD_CREDIT
+#define BUILD_CREDIT_SUFFIX ", " BUILD_CREDIT
 #else
-	#define BUILD_CREDIT_SUFFIX ""
+#define BUILD_CREDIT_SUFFIX ""
 #endif
 
-const char *GetAegisubLongVersionString() {
-	return BUILD_GIT_VERSION_STRING BUILD_CREDIT_SUFFIX DEBUG_SUFFIX;
+const char *GetAegisubLongVersionString()
+{
+    return BUILD_GIT_VERSION_STRING BUILD_CREDIT_SUFFIX DEBUG_SUFFIX;
 }
 
-const char *GetAegisubShortVersionString() {
-	return BUILD_GIT_VERSION_STRING DEBUG_SUFFIX;
+const char *GetAegisubShortVersionString()
+{
+    return BUILD_GIT_VERSION_STRING DEBUG_SUFFIX;
 }
 
 #ifdef BUILD_CREDIT
-const char *GetAegisubBuildTime() {
-	return __DATE__ " " __TIME__;
+const char *GetAegisubBuildTime()
+{
+    return __DATE__ " " __TIME__;
 }
 
-const char *GetAegisubBuildCredit() {
-	return BUILD_CREDIT;
-	return "";
+const char *GetAegisubBuildCredit()
+{
+    return BUILD_CREDIT;
+    return "";
 }
 #endif
 
-bool GetIsOfficialRelease() {
-	return false;
+bool GetIsOfficialRelease()
+{
+    return false;
 }
 
-const char *GetVersionNumber() {
-	return BUILD_GIT_VERSION_STRING;
+const char *GetVersionNumber()
+{
+    return BUILD_GIT_VERSION_STRING;
 }
 
-const char *GetReleaseVersion() {
-	return RELEASE_VERSION;
+const char *GetReleaseVersion()
+{
+    return RELEASE_VERSION;
 }
 
-int GetSVNRevision() {
+int GetSVNRevision()
+{
 #ifdef BUILD_GIT_VERSION_NUMBER
-	return BUILD_GIT_VERSION_NUMBER;
+    return BUILD_GIT_VERSION_NUMBER;
 #else
-	return 0;
+    return 0;
 #endif
 }
diff --git a/src/video_box.cpp b/src/video_box.cpp
index 49fff80aace878800d1c8075a867435406da6029..d9a53792b8832372dd1eff475915d5fe2eadff41 100644
--- a/src/video_box.cpp
+++ b/src/video_box.cpp
@@ -50,89 +50,90 @@
 #include <wx/toolbar.h>
 
 VideoBox::VideoBox(wxWindow *parent, bool isDetached, agi::Context *context)
-: wxPanel(parent, -1)
-, context(context)
+    : wxPanel(parent, -1)
+    , context(context)
 {
-	auto videoSlider = new VideoSlider(this, context);
-	videoSlider->SetToolTip(_("Seek video"));
-
-	auto mainToolbar = toolbar::GetToolbar(this, "video", context, "Video", false);
-
-	VideoPosition = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(110, 20), wxTE_READONLY);
-	VideoPosition->SetToolTip(_("Current frame time and number"));
-
-	VideoSubsPos = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(110, 20), wxTE_READONLY);
-	VideoSubsPos->SetToolTip(_("Time of this frame relative to start and end of current subs"));
-
-	wxArrayString choices;
-	for (int i = 1; i <= 24; ++i)
-		choices.Add(fmt_wx("%g%%", i * 12.5));
-	auto zoomBox = new wxComboBox(this, -1, "75%", wxDefaultPosition, wxDefaultSize, choices, wxCB_DROPDOWN | wxTE_PROCESS_ENTER);
-
-	auto visualToolBar = toolbar::GetToolbar(this, "visual_tools", context, "Video", true);
-	auto visualSubToolBar = new wxToolBar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_VERTICAL | wxTB_BOTTOM | wxTB_FLAT);
-
-	auto videoDisplay = new VideoDisplay(visualSubToolBar, isDetached, zoomBox, this, context);
-	videoDisplay->MoveBeforeInTabOrder(videoSlider);
-
-	auto toolbarSizer = new wxBoxSizer(wxVERTICAL);
-	toolbarSizer->Add(visualToolBar, wxSizerFlags(1));
-	toolbarSizer->Add(visualSubToolBar, wxSizerFlags());
-
-	auto topSizer = new wxBoxSizer(wxHORIZONTAL);
-	topSizer->Add(toolbarSizer, 0, wxEXPAND);
-	topSizer->Add(videoDisplay, isDetached, isDetached ? wxEXPAND : 0);
-
-	auto videoBottomSizer = new wxBoxSizer(wxHORIZONTAL);
-	videoBottomSizer->Add(mainToolbar, wxSizerFlags(0).Center());
-	videoBottomSizer->Add(VideoPosition, wxSizerFlags(1).Center().Border(wxLEFT));
-	videoBottomSizer->Add(VideoSubsPos, wxSizerFlags(1).Center().Border(wxLEFT));
-	videoBottomSizer->Add(zoomBox, wxSizerFlags(0).Center().Border(wxLEFT | wxRIGHT));
-
-	auto VideoSizer = new wxBoxSizer(wxVERTICAL);
-	VideoSizer->Add(topSizer, 1, wxEXPAND, 0);
-	VideoSizer->Add(new wxStaticLine(this), 0, wxEXPAND, 0);
-	VideoSizer->Add(videoSlider, 0, wxEXPAND, 0);
-	VideoSizer->Add(videoBottomSizer, 0, wxEXPAND | wxBOTTOM, 5);
-	SetSizer(VideoSizer);
-
-	UpdateTimeBoxes();
-
-	connections = agi::signal::make_vector({
-		context->ass->AddCommitListener(&VideoBox::UpdateTimeBoxes, this),
-		context->project->AddKeyframesListener(&VideoBox::UpdateTimeBoxes, this),
-		context->project->AddTimecodesListener(&VideoBox::UpdateTimeBoxes, this),
-		context->project->AddVideoProviderListener(&VideoBox::UpdateTimeBoxes, this),
-		context->selectionController->AddSelectionListener(&VideoBox::UpdateTimeBoxes, this),
-		context->videoController->AddSeekListener(&VideoBox::UpdateTimeBoxes, this),
-	});
+    auto videoSlider = new VideoSlider(this, context);
+    videoSlider->SetToolTip(_("Seek video"));
+
+    auto mainToolbar = toolbar::GetToolbar(this, "video", context, "Video", false);
+
+    VideoPosition = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(110, 20), wxTE_READONLY);
+    VideoPosition->SetToolTip(_("Current frame time and number"));
+
+    VideoSubsPos = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(110, 20), wxTE_READONLY);
+    VideoSubsPos->SetToolTip(_("Time of this frame relative to start and end of current subs"));
+
+    wxArrayString choices;
+    for (int i = 1; i <= 24; ++i)
+        choices.Add(fmt_wx("%g%%", i * 12.5));
+    auto zoomBox = new wxComboBox(this, -1, "75%", wxDefaultPosition, wxDefaultSize, choices, wxCB_DROPDOWN | wxTE_PROCESS_ENTER);
+
+    auto visualToolBar = toolbar::GetToolbar(this, "visual_tools", context, "Video", true);
+    auto visualSubToolBar = new wxToolBar(this, -1, wxDefaultPosition, wxDefaultSize, wxTB_VERTICAL | wxTB_BOTTOM | wxTB_FLAT);
+
+    auto videoDisplay = new VideoDisplay(visualSubToolBar, isDetached, zoomBox, this, context);
+    videoDisplay->MoveBeforeInTabOrder(videoSlider);
+
+    auto toolbarSizer = new wxBoxSizer(wxVERTICAL);
+    toolbarSizer->Add(visualToolBar, wxSizerFlags(1));
+    toolbarSizer->Add(visualSubToolBar, wxSizerFlags());
+
+    auto topSizer = new wxBoxSizer(wxHORIZONTAL);
+    topSizer->Add(toolbarSizer, 0, wxEXPAND);
+    topSizer->Add(videoDisplay, isDetached, isDetached ? wxEXPAND : 0);
+
+    auto videoBottomSizer = new wxBoxSizer(wxHORIZONTAL);
+    videoBottomSizer->Add(mainToolbar, wxSizerFlags(0).Center());
+    videoBottomSizer->Add(VideoPosition, wxSizerFlags(1).Center().Border(wxLEFT));
+    videoBottomSizer->Add(VideoSubsPos, wxSizerFlags(1).Center().Border(wxLEFT));
+    videoBottomSizer->Add(zoomBox, wxSizerFlags(0).Center().Border(wxLEFT | wxRIGHT));
+
+    auto VideoSizer = new wxBoxSizer(wxVERTICAL);
+    VideoSizer->Add(topSizer, 1, wxEXPAND, 0);
+    VideoSizer->Add(new wxStaticLine(this), 0, wxEXPAND, 0);
+    VideoSizer->Add(videoSlider, 0, wxEXPAND, 0);
+    VideoSizer->Add(videoBottomSizer, 0, wxEXPAND | wxBOTTOM, 5);
+    SetSizer(VideoSizer);
+
+    UpdateTimeBoxes();
+
+    connections = agi::signal::make_vector({
+        context->ass->AddCommitListener(&VideoBox::UpdateTimeBoxes, this),
+        context->project->AddKeyframesListener(&VideoBox::UpdateTimeBoxes, this),
+        context->project->AddTimecodesListener(&VideoBox::UpdateTimeBoxes, this),
+        context->project->AddVideoProviderListener(&VideoBox::UpdateTimeBoxes, this),
+        context->selectionController->AddSelectionListener(&VideoBox::UpdateTimeBoxes, this),
+        context->videoController->AddSeekListener(&VideoBox::UpdateTimeBoxes, this),
+    });
 }
 
-void VideoBox::UpdateTimeBoxes() {
-	if (!context->project->VideoProvider()) return;
-
-	int frame = context->videoController->GetFrameN();
-	int time = context->videoController->TimeAtFrame(frame, agi::vfr::EXACT);
-
-	// Set the text box for frame number and time
-	VideoPosition->SetValue(fmt_wx("%s - %d", agi::Time(time).GetAssFormatted(true), frame));
-	if (boost::binary_search(context->project->Keyframes(), frame)) {
-		// Set the background color to indicate this is a keyframe
-		VideoPosition->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selection")->GetColor()));
-		VideoPosition->SetForegroundColour(to_wx(OPT_GET("Colour/Subtitle Grid/Selection")->GetColor()));
-	}
-	else {
-		VideoPosition->SetBackgroundColour(wxNullColour);
-		VideoPosition->SetForegroundColour(wxNullColour);
-	}
-
-	AssDialogue *active_line = context->selectionController->GetActiveLine();
-	if (!active_line)
-		VideoSubsPos->SetValue("");
-	else {
-		VideoSubsPos->SetValue(fmt_wx(
-			"%+dms; %+dms",
-			time - active_line->Start,
-			time - active_line->End));
-	}
+void VideoBox::UpdateTimeBoxes()
+{
+    if (!context->project->VideoProvider()) return;
+
+    int frame = context->videoController->GetFrameN();
+    int time = context->videoController->TimeAtFrame(frame, agi::vfr::EXACT);
+
+    // Set the text box for frame number and time
+    VideoPosition->SetValue(fmt_wx("%s - %d", agi::Time(time).GetAssFormatted(true), frame));
+    if (boost::binary_search(context->project->Keyframes(), frame)) {
+        // Set the background color to indicate this is a keyframe
+        VideoPosition->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selection")->GetColor()));
+        VideoPosition->SetForegroundColour(to_wx(OPT_GET("Colour/Subtitle Grid/Selection")->GetColor()));
+    }
+    else {
+        VideoPosition->SetBackgroundColour(wxNullColour);
+        VideoPosition->SetForegroundColour(wxNullColour);
+    }
+
+    AssDialogue *active_line = context->selectionController->GetActiveLine();
+    if (!active_line)
+        VideoSubsPos->SetValue("");
+    else {
+        VideoSubsPos->SetValue(fmt_wx(
+                                   "%+dms; %+dms",
+                                   time - active_line->Start,
+                                   time - active_line->End));
+    }
 }
diff --git a/src/video_box.h b/src/video_box.h
index 1e00e73adf860d7df9b0d002e3fb9599b78430de..bef081c0b4767c31bae8e3f7e0109bab934041aa 100644
--- a/src/video_box.h
+++ b/src/video_box.h
@@ -38,14 +38,14 @@ class wxTextCtrl;
 /// @class VideoBox
 /// @brief The box containing the video display and associated controls
 class VideoBox final : public wxPanel {
-	std::vector<agi::signal::Connection> connections;
-	agi::Context *context;     ///< Project context
-	wxTextCtrl *VideoPosition; ///< Current frame/time
-	wxTextCtrl *VideoSubsPos;  ///< Time relative to the active subtitle line
+    std::vector<agi::signal::Connection> connections;
+    agi::Context *context;     ///< Project context
+    wxTextCtrl *VideoPosition; ///< Current frame/time
+    wxTextCtrl *VideoSubsPos;  ///< Time relative to the active subtitle line
 
-	/// Update VideoPosition and VideoSubsPos
-	void UpdateTimeBoxes();
+    /// Update VideoPosition and VideoSubsPos
+    void UpdateTimeBoxes();
 
 public:
-	VideoBox(wxWindow *parent, bool isDetached, agi::Context *context);
+    VideoBox(wxWindow *parent, bool isDetached, agi::Context *context);
 };
diff --git a/src/video_controller.cpp b/src/video_controller.cpp
index 6c4427eaf97989a365ee3896e2bddb8896c4994f..e0174fc8fc7d8016516ee33caf94b5328755804f 100644
--- a/src/video_controller.cpp
+++ b/src/video_controller.cpp
@@ -46,191 +46,210 @@
 #include <wx/log.h>
 
 VideoController::VideoController(agi::Context *c)
-: context(c)
-, playAudioOnStep(OPT_GET("Audio/Plays When Stepping Video"))
-, connections(agi::signal::make_vector({
-	context->ass->AddCommitListener(&VideoController::OnSubtitlesCommit, this),
-	context->project->AddVideoProviderListener(&VideoController::OnNewVideoProvider, this),
-	context->selectionController->AddActiveLineListener(&VideoController::OnActiveLineChanged, this),
+    : context(c)
+    , playAudioOnStep(OPT_GET("Audio/Plays When Stepping Video"))
+    , connections(agi::signal::make_vector({
+    context->ass->AddCommitListener(&VideoController::OnSubtitlesCommit, this),
+    context->project->AddVideoProviderListener(&VideoController::OnNewVideoProvider, this),
+    context->selectionController->AddActiveLineListener(&VideoController::OnActiveLineChanged, this),
 }))
 {
-	Bind(EVT_VIDEO_ERROR, &VideoController::OnVideoError, this);
-	Bind(EVT_SUBTITLES_ERROR, &VideoController::OnSubtitlesError, this);
-	playback.Bind(wxEVT_TIMER, &VideoController::OnPlayTimer, this);
+    Bind(EVT_VIDEO_ERROR, &VideoController::OnVideoError, this);
+    Bind(EVT_SUBTITLES_ERROR, &VideoController::OnSubtitlesError, this);
+    playback.Bind(wxEVT_TIMER, &VideoController::OnPlayTimer, this);
 }
 
-void VideoController::OnNewVideoProvider(AsyncVideoProvider *new_provider) {
-	Stop();
-	provider = new_provider;
-	color_matrix = provider ? provider->GetColorSpace() : "";
+void VideoController::OnNewVideoProvider(AsyncVideoProvider *new_provider)
+{
+    Stop();
+    provider = new_provider;
+    color_matrix = provider ? provider->GetColorSpace() : "";
 }
 
-void VideoController::OnSubtitlesCommit(int type, const AssDialogue *changed) {
-	if (!provider) return;
+void VideoController::OnSubtitlesCommit(int type, const AssDialogue *changed)
+{
+    if (!provider) return;
 
-	if ((type & AssFile::COMMIT_SCRIPTINFO) || type == AssFile::COMMIT_NEW) {
-		auto new_matrix = context->ass->GetScriptInfo("YCbCr Matrix");
-		if (!new_matrix.empty() && new_matrix != color_matrix) {
-			color_matrix = new_matrix;
-			provider->SetColorSpace(new_matrix);
-		}
-	}
+    if ((type & AssFile::COMMIT_SCRIPTINFO) || type == AssFile::COMMIT_NEW) {
+        auto new_matrix = context->ass->GetScriptInfo("YCbCr Matrix");
+        if (!new_matrix.empty() && new_matrix != color_matrix) {
+            color_matrix = new_matrix;
+            provider->SetColorSpace(new_matrix);
+        }
+    }
 
-	if (!changed)
-		provider->LoadSubtitles(context->ass.get());
-	else
-		provider->UpdateSubtitles(context->ass.get(), changed);
+    if (!changed)
+        provider->LoadSubtitles(context->ass.get());
+    else
+        provider->UpdateSubtitles(context->ass.get(), changed);
 }
 
-void VideoController::OnActiveLineChanged(AssDialogue *line) {
-	if (line && provider && OPT_GET("Video/Subtitle Sync")->GetBool()) {
-		Stop();
-		JumpToTime(line->Start);
-	}
+void VideoController::OnActiveLineChanged(AssDialogue *line)
+{
+    if (line && provider && OPT_GET("Video/Subtitle Sync")->GetBool()) {
+        Stop();
+        JumpToTime(line->Start);
+    }
 }
 
-void VideoController::RequestFrame() {
-	context->ass->Properties.video_position = frame_n;
-	provider->RequestFrame(frame_n, TimeAtFrame(frame_n));
+void VideoController::RequestFrame()
+{
+    context->ass->Properties.video_position = frame_n;
+    provider->RequestFrame(frame_n, TimeAtFrame(frame_n));
 }
 
-void VideoController::JumpToFrame(int n) {
-	if (!provider) return;
+void VideoController::JumpToFrame(int n)
+{
+    if (!provider) return;
 
-	bool was_playing = IsPlaying();
-	if (was_playing)
-		Stop();
+    bool was_playing = IsPlaying();
+    if (was_playing)
+        Stop();
 
-	frame_n = mid(0, n, provider->GetFrameCount() - 1);
-	RequestFrame();
-	Seek(frame_n);
+    frame_n = mid(0, n, provider->GetFrameCount() - 1);
+    RequestFrame();
+    Seek(frame_n);
 
-	if (was_playing)
-		Play();
+    if (was_playing)
+        Play();
 }
 
-void VideoController::JumpToTime(int ms, agi::vfr::Time end) {
-	JumpToFrame(FrameAtTime(ms, end));
+void VideoController::JumpToTime(int ms, agi::vfr::Time end)
+{
+    JumpToFrame(FrameAtTime(ms, end));
 }
 
-void VideoController::NextFrame() {
-	if (!provider || IsPlaying() || frame_n == provider->GetFrameCount())
-		return;
+void VideoController::NextFrame()
+{
+    if (!provider || IsPlaying() || frame_n == provider->GetFrameCount())
+        return;
 
-	JumpToFrame(frame_n + 1);
-	if (playAudioOnStep->GetBool())
-		context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n - 1), TimeAtFrame(frame_n)));
+    JumpToFrame(frame_n + 1);
+    if (playAudioOnStep->GetBool())
+        context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n - 1), TimeAtFrame(frame_n)));
 }
 
-void VideoController::PrevFrame() {
-	if (!provider || IsPlaying() || frame_n == 0)
-		return;
+void VideoController::PrevFrame()
+{
+    if (!provider || IsPlaying() || frame_n == 0)
+        return;
 
-	JumpToFrame(frame_n - 1);
-	if (playAudioOnStep->GetBool())
-		context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n), TimeAtFrame(frame_n + 1)));
+    JumpToFrame(frame_n - 1);
+    if (playAudioOnStep->GetBool())
+        context->audioController->PlayRange(TimeRange(TimeAtFrame(frame_n), TimeAtFrame(frame_n + 1)));
 }
 
-void VideoController::Play() {
-	if (IsPlaying()) {
-		Stop();
-		return;
-	}
+void VideoController::Play()
+{
+    if (IsPlaying()) {
+        Stop();
+        return;
+    }
 
-	if (!provider) return;
+    if (!provider) return;
 
-	start_ms = TimeAtFrame(frame_n);
-	end_frame = provider->GetFrameCount() - 1;
+    start_ms = TimeAtFrame(frame_n);
+    end_frame = provider->GetFrameCount() - 1;
 
-	context->audioController->PlayToEnd(start_ms);
+    context->audioController->PlayToEnd(start_ms);
 
-	playback_start_time = std::chrono::steady_clock::now();
-	playback.Start(10);
+    playback_start_time = std::chrono::steady_clock::now();
+    playback.Start(10);
 }
 
-void VideoController::PlayLine() {
-	Stop();
+void VideoController::PlayLine()
+{
+    Stop();
 
-	AssDialogue *curline = context->selectionController->GetActiveLine();
-	if (!curline) return;
+    AssDialogue *curline = context->selectionController->GetActiveLine();
+    if (!curline) return;
 
-	context->audioController->PlayRange(TimeRange(curline->Start, curline->End));
+    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);
-	start_ms = TimeAtFrame(startFrame);
-	end_frame = FrameAtTime(context->selectionController->GetActiveLine()->End, agi::vfr::END) + 1;
+    // Round-trip conversion to convert start to exact
+    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;
 
-	JumpToFrame(startFrame);
+    JumpToFrame(startFrame);
 
-	playback_start_time = std::chrono::steady_clock::now();
-	playback.Start(10);
+    playback_start_time = std::chrono::steady_clock::now();
+    playback.Start(10);
 }
 
-void VideoController::Stop() {
-	if (IsPlaying()) {
-		playback.Stop();
-		context->audioController->Stop();
-	}
+void VideoController::Stop()
+{
+    if (IsPlaying()) {
+        playback.Stop();
+        context->audioController->Stop();
+    }
 }
 
-void VideoController::OnPlayTimer(wxTimerEvent &) {
-	using namespace std::chrono;
-	int next_frame = FrameAtTime(start_ms + duration_cast<milliseconds>(steady_clock::now() - playback_start_time).count());
-	if (next_frame == frame_n) return;
+void VideoController::OnPlayTimer(wxTimerEvent &)
+{
+    using namespace std::chrono;
+    int next_frame = FrameAtTime(start_ms + duration_cast<milliseconds>(steady_clock::now() - playback_start_time).count());
+    if (next_frame == frame_n) return;
 
-	if (next_frame >= end_frame)
-		Stop();
-	else {
-		frame_n = next_frame;
-		RequestFrame();
-		Seek(frame_n);
-	}
+    if (next_frame >= end_frame)
+        Stop();
+    else {
+        frame_n = next_frame;
+        RequestFrame();
+        Seek(frame_n);
+    }
 }
 
-double VideoController::GetARFromType(AspectRatio type) const {
-	switch (type) {
-		case AspectRatio::Default:    return (double)provider->GetWidth()/provider->GetHeight();
-		case AspectRatio::Fullscreen: return 4.0/3.0;
-		case AspectRatio::Widescreen: return 16.0/9.0;
-		case AspectRatio::Cinematic:  return 2.35;
-        default: throw agi::InternalError("Bad AR type");
-	}
+double VideoController::GetARFromType(AspectRatio type) const
+{
+    switch (type) {
+    case AspectRatio::Default:    return (double)provider->GetWidth() / provider->GetHeight();
+    case AspectRatio::Fullscreen: return 4.0 / 3.0;
+    case AspectRatio::Widescreen: return 16.0 / 9.0;
+    case AspectRatio::Cinematic:  return 2.35;
+    default: throw agi::InternalError("Bad AR type");
+    }
 }
 
-void VideoController::SetAspectRatio(double value) {
-	ar_type = AspectRatio::Custom;
-	ar_value = mid(.5, value, 5.);
-	context->ass->Properties.ar_mode = (int)ar_type;
-	context->ass->Properties.ar_value = ar_value;
-	ARChange(ar_type, ar_value);
+void VideoController::SetAspectRatio(double value)
+{
+    ar_type = AspectRatio::Custom;
+    ar_value = mid(.5, value, 5.);
+    context->ass->Properties.ar_mode = (int)ar_type;
+    context->ass->Properties.ar_value = ar_value;
+    ARChange(ar_type, ar_value);
 }
 
-void VideoController::SetAspectRatio(AspectRatio type) {
-	ar_value = mid(.5, GetARFromType(type), 5.);
-	ar_type = type;
-	context->ass->Properties.ar_mode = (int)ar_type;
-	context->ass->Properties.ar_value = ar_value;
-	ARChange(ar_type, ar_value);
+void VideoController::SetAspectRatio(AspectRatio type)
+{
+    ar_value = mid(.5, GetARFromType(type), 5.);
+    ar_type = type;
+    context->ass->Properties.ar_mode = (int)ar_type;
+    context->ass->Properties.ar_value = ar_value;
+    ARChange(ar_type, ar_value);
 }
 
-int VideoController::TimeAtFrame(int frame, agi::vfr::Time type) const {
-	return context->project->Timecodes().TimeAtFrame(frame, type);
+int VideoController::TimeAtFrame(int frame, agi::vfr::Time type) const
+{
+    return context->project->Timecodes().TimeAtFrame(frame, type);
 }
 
-int VideoController::FrameAtTime(int time, agi::vfr::Time type) const {
-	return context->project->Timecodes().FrameAtTime(time, type);
+int VideoController::FrameAtTime(int time, agi::vfr::Time type) const
+{
+    return context->project->Timecodes().FrameAtTime(time, type);
 }
 
-void VideoController::OnVideoError(VideoProviderErrorEvent const& err) {
-	wxLogError(
-		"Failed seeking video. The video file may be corrupt or incomplete.\n"
-		"Error message reported: %s",
-		to_wx(err.GetMessage()));
+void VideoController::OnVideoError(VideoProviderErrorEvent const &err)
+{
+    wxLogError(
+        "Failed seeking video. The video file may be corrupt or incomplete.\n"
+        "Error message reported: %s",
+        to_wx(err.GetMessage()));
 }
 
-void VideoController::OnSubtitlesError(SubtitlesProviderErrorEvent const& err) {
-	wxLogError(
-		"Failed rendering subtitles. Error message reported: %s",
-		to_wx(err.GetMessage()));
+void VideoController::OnSubtitlesError(SubtitlesProviderErrorEvent const &err)
+{
+    wxLogError(
+        "Failed rendering subtitles. Error message reported: %s",
+        to_wx(err.GetMessage()));
 }
diff --git a/src/video_controller.h b/src/video_controller.h
index c15c5c8f159fe7342910b0de2be8980c6d0c2218..8046e3b2420eb1edc0d74257c1e8d8847b5837cc 100644
--- a/src/video_controller.h
+++ b/src/video_controller.h
@@ -41,122 +41,122 @@ struct SubtitlesProviderErrorEvent;
 struct VideoProviderErrorEvent;
 
 namespace agi {
-	struct Context;
-	class OptionValue;
+struct Context;
+class OptionValue;
 }
 
 enum class AspectRatio {
-	Default = 0,
-	Fullscreen,
-	Widescreen,
-	Cinematic,
-	Custom
+    Default = 0,
+    Fullscreen,
+    Widescreen,
+    Cinematic,
+    Custom
 };
 
 /// Manage stuff related to video playback
 class VideoController final : public wxEvtHandler {
-	/// Current frame number changed (new frame number)
-	agi::signal::Signal<int> Seek;
-	/// Aspect ratio was changed (type, value)
-	agi::signal::Signal<AspectRatio, double> ARChange;
+    /// Current frame number changed (new frame number)
+    agi::signal::Signal<int> Seek;
+    /// Aspect ratio was changed (type, value)
+    agi::signal::Signal<AspectRatio, double> ARChange;
 
-	agi::Context *context;
+    agi::Context *context;
 
-	/// The video provider owned by the threaded frame source, or nullptr if no
-	/// video is open
-	AsyncVideoProvider *provider = nullptr;
+    /// The video provider owned by the threaded frame source, or nullptr if no
+    /// video is open
+    AsyncVideoProvider *provider = nullptr;
 
-	/// Last seen script color matrix
-	std::string color_matrix;
+    /// Last seen script color matrix
+    std::string color_matrix;
 
-	/// Playback timer used to periodically check if we should go to the next
-	/// frame while playing video
-	wxTimer playback;
+    /// Playback timer used to periodically check if we should go to the next
+    /// frame while playing video
+    wxTimer playback;
 
-	/// Time when playback was last started
-	std::chrono::steady_clock::time_point playback_start_time;
+    /// 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 start_ms = 0;
+    /// The start time of the first frame of the current playback; undefined if
+    /// video is not currently playing
+    int start_ms = 0;
 
-	/// The last frame to play if video is currently playing
-	int end_frame = 0;
+    /// The last frame to play if video is currently playing
+    int end_frame = 0;
 
-	/// The frame number which was last requested from the video provider,
-	/// which may not be the same thing as the currently displayed frame
-	int frame_n = 0;
+    /// The frame number which was last requested from the video provider,
+    /// which may not be the same thing as the currently displayed frame
+    int frame_n = 0;
 
-	/// The picture aspect ratio of the video if the aspect ratio has been
-	/// overridden by the user
-	double ar_value = 1.;
+    /// The picture aspect ratio of the video if the aspect ratio has been
+    /// overridden by the user
+    double ar_value = 1.;
 
-	/// The current AR type
-	AspectRatio ar_type = AspectRatio::Default;
+    /// The current AR type
+    AspectRatio ar_type = AspectRatio::Default;
 
-	/// Cached option for audio playing when frame stepping
-	const agi::OptionValue* playAudioOnStep;
+    /// Cached option for audio playing when frame stepping
+    const agi::OptionValue *playAudioOnStep;
 
-	std::vector<agi::signal::Connection> connections;
+    std::vector<agi::signal::Connection> connections;
 
-	void OnPlayTimer(wxTimerEvent &event);
+    void OnPlayTimer(wxTimerEvent &event);
 
-	void OnVideoError(VideoProviderErrorEvent const& err);
-	void OnSubtitlesError(SubtitlesProviderErrorEvent const& err);
+    void OnVideoError(VideoProviderErrorEvent const &err);
+    void OnSubtitlesError(SubtitlesProviderErrorEvent const &err);
 
-	void OnSubtitlesCommit(int type, const AssDialogue *changed);
-	void OnNewVideoProvider(AsyncVideoProvider *provider);
-	void OnActiveLineChanged(AssDialogue *line);
+    void OnSubtitlesCommit(int type, const AssDialogue *changed);
+    void OnNewVideoProvider(AsyncVideoProvider *provider);
+    void OnActiveLineChanged(AssDialogue *line);
 
-	void RequestFrame();
+    void RequestFrame();
 
 public:
-	VideoController(agi::Context *context);
-
-	/// Is the video currently playing?
-	bool IsPlaying() const { return playback.IsRunning(); }
-
-	/// Get the current frame number
-	int GetFrameN() const { return frame_n; }
-
-	/// Get the actual aspect ratio from a predefined AR type
-	double GetARFromType(AspectRatio type) const;
-
-	/// Override the aspect ratio of the currently loaded video
-	void SetAspectRatio(double value);
-
-	/// Override the aspect ratio of the currently loaded video
-	/// @param type Predefined type to set the AR to. Must not be Custom.
-	void SetAspectRatio(AspectRatio type);
-
-	/// Get the current AR type
-	AspectRatio GetAspectRatioType() const { return ar_type; }
-
-	/// Get the current aspect ratio of the video
-	double GetAspectRatioValue() const { return ar_value; }
-
-	/// @brief Jump to the beginning of a frame
-	/// @param n Frame number to jump to
-	void JumpToFrame(int n);
-	/// @brief Jump to a time
-	/// @param ms Time to jump to in milliseconds
-	/// @param end Type of time
-	void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START);
-
-	/// Starting playing the video
-	void Play();
-	/// Play the next frame then stop
-	void NextFrame();
-	/// Play the previous frame then stop
-	void PrevFrame();
-	/// Seek to the beginning of the current line, then play to the end of it
-	void PlayLine();
-	/// Stop playing
-	void Stop();
-
-	DEFINE_SIGNAL_ADDERS(Seek, AddSeekListener)
-	DEFINE_SIGNAL_ADDERS(ARChange, AddARChangeListener)
-
-	int TimeAtFrame(int frame, agi::vfr::Time type = agi::vfr::EXACT) const;
-	int FrameAtTime(int time, agi::vfr::Time type = agi::vfr::EXACT) const;
+    VideoController(agi::Context *context);
+
+    /// Is the video currently playing?
+    bool IsPlaying() const { return playback.IsRunning(); }
+
+    /// Get the current frame number
+    int GetFrameN() const { return frame_n; }
+
+    /// Get the actual aspect ratio from a predefined AR type
+    double GetARFromType(AspectRatio type) const;
+
+    /// Override the aspect ratio of the currently loaded video
+    void SetAspectRatio(double value);
+
+    /// Override the aspect ratio of the currently loaded video
+    /// @param type Predefined type to set the AR to. Must not be Custom.
+    void SetAspectRatio(AspectRatio type);
+
+    /// Get the current AR type
+    AspectRatio GetAspectRatioType() const { return ar_type; }
+
+    /// Get the current aspect ratio of the video
+    double GetAspectRatioValue() const { return ar_value; }
+
+    /// @brief Jump to the beginning of a frame
+    /// @param n Frame number to jump to
+    void JumpToFrame(int n);
+    /// @brief Jump to a time
+    /// @param ms Time to jump to in milliseconds
+    /// @param end Type of time
+    void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START);
+
+    /// Starting playing the video
+    void Play();
+    /// Play the next frame then stop
+    void NextFrame();
+    /// Play the previous frame then stop
+    void PrevFrame();
+    /// Seek to the beginning of the current line, then play to the end of it
+    void PlayLine();
+    /// Stop playing
+    void Stop();
+
+    DEFINE_SIGNAL_ADDERS(Seek, AddSeekListener)
+    DEFINE_SIGNAL_ADDERS(ARChange, AddARChangeListener)
+
+    int TimeAtFrame(int frame, agi::vfr::Time type = agi::vfr::EXACT) const;
+    int FrameAtTime(int time, agi::vfr::Time type = agi::vfr::EXACT) const;
 };
diff --git a/src/video_display.cpp b/src/video_display.cpp
index ef8bed67bffe02639dbcd874807978981cc7f508..0bc39dea5d15cb98e283ec330b4a2fe8cc1577c5 100644
--- a/src/video_display.cpp
+++ b/src/video_display.cpp
@@ -66,384 +66,406 @@
 #endif
 
 /// Attribute list for gl canvases; set the canvases to doublebuffered rgba with an 8 bit stencil buffer
-int attribList[] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 };
+int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 };
 
 /// An OpenGL error occurred while uploading or displaying a frame
 class OpenGlException final : public agi::Exception {
 public:
-	OpenGlException(const char *func, int err)
-	: agi::Exception(agi::format("%s failed with error code %d", func, err))
-	{ }
+    OpenGlException(const char *func, int err)
+        : agi::Exception(agi::format("%s failed with error code %d", func, err))
+    { }
 };
 
 #define E(cmd) cmd; if (GLenum err = glGetError()) throw OpenGlException(#cmd, err)
 
 VideoDisplay::VideoDisplay(wxToolBar *toolbar, bool freeSize, wxComboBox *zoomBox, wxWindow *parent, agi::Context *c)
-: wxGLCanvas(parent, -1, attribList)
-, autohideTools(OPT_GET("Tool/Visual/Autohide"))
-, con(c)
-, zoomValue(OPT_GET("Video/Default Zoom")->GetInt() * .125 + .125)
-, toolBar(toolbar)
-, zoomBox(zoomBox)
-, freeSize(freeSize)
-, retina_helper(agi::make_unique<RetinaHelper>(this))
-, scale_factor(retina_helper->GetScaleFactor())
-, scale_factor_connection(retina_helper->AddScaleFactorListener([=](int new_scale_factor) {
-	double new_zoom = zoomValue * new_scale_factor / scale_factor;
-	scale_factor = new_scale_factor;
-	SetZoom(new_zoom);
+    : wxGLCanvas(parent, -1, attribList)
+    , autohideTools(OPT_GET("Tool/Visual/Autohide"))
+    , con(c)
+    , zoomValue(OPT_GET("Video/Default Zoom")->GetInt() * .125 + .125)
+    , toolBar(toolbar)
+    , zoomBox(zoomBox)
+    , freeSize(freeSize)
+    , retina_helper(agi::make_unique<RetinaHelper>(this))
+    , scale_factor(retina_helper->GetScaleFactor())
+    , scale_factor_connection(retina_helper->AddScaleFactorListener([ = ](int new_scale_factor)
+{
+    double new_zoom = zoomValue * new_scale_factor / scale_factor;
+    scale_factor = new_scale_factor;
+    SetZoom(new_zoom);
 }))
 {
-	zoomBox->SetValue(fmt_wx("%g%%", zoomValue * 100.));
-	zoomBox->Bind(wxEVT_COMBOBOX, &VideoDisplay::SetZoomFromBox, this);
-	zoomBox->Bind(wxEVT_TEXT_ENTER, &VideoDisplay::SetZoomFromBoxText, this);
-
-	con->videoController->Bind(EVT_FRAME_READY, &VideoDisplay::UploadFrameData, this);
-	connections = agi::signal::make_vector({
-		con->project->AddVideoProviderListener(&VideoDisplay::UpdateSize, this),
-		con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this),
-	});
-
-	Bind(wxEVT_PAINT, std::bind(&VideoDisplay::Render, this));
-	Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this);
-	Bind(wxEVT_CONTEXT_MENU, &VideoDisplay::OnContextMenu, this);
-	Bind(wxEVT_ENTER_WINDOW, &VideoDisplay::OnMouseEvent, this);
-	Bind(wxEVT_CHAR_HOOK, &VideoDisplay::OnKeyDown, this);
-	Bind(wxEVT_LEAVE_WINDOW, &VideoDisplay::OnMouseLeave, this);
-	Bind(wxEVT_LEFT_DCLICK, &VideoDisplay::OnMouseEvent, this);
-	Bind(wxEVT_LEFT_DOWN, &VideoDisplay::OnMouseEvent, this);
-	Bind(wxEVT_LEFT_UP, &VideoDisplay::OnMouseEvent, this);
-	Bind(wxEVT_MOTION, &VideoDisplay::OnMouseEvent, this);
-	Bind(wxEVT_MOUSEWHEEL, &VideoDisplay::OnMouseWheel, this);
-
-	SetCursor(wxNullCursor);
-
-	c->videoDisplay = this;
-
-	con->videoController->JumpToFrame(con->videoController->GetFrameN());
-
-	SetLayoutDirection(wxLayout_LeftToRight);
+    zoomBox->SetValue(fmt_wx("%g%%", zoomValue * 100.));
+    zoomBox->Bind(wxEVT_COMBOBOX, &VideoDisplay::SetZoomFromBox, this);
+    zoomBox->Bind(wxEVT_TEXT_ENTER, &VideoDisplay::SetZoomFromBoxText, this);
+
+    con->videoController->Bind(EVT_FRAME_READY, &VideoDisplay::UploadFrameData, this);
+    connections = agi::signal::make_vector({
+        con->project->AddVideoProviderListener(&VideoDisplay::UpdateSize, this),
+        con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this),
+    });
+
+    Bind(wxEVT_PAINT, std::bind(&VideoDisplay::Render, this));
+    Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this);
+    Bind(wxEVT_CONTEXT_MENU, &VideoDisplay::OnContextMenu, this);
+    Bind(wxEVT_ENTER_WINDOW, &VideoDisplay::OnMouseEvent, this);
+    Bind(wxEVT_CHAR_HOOK, &VideoDisplay::OnKeyDown, this);
+    Bind(wxEVT_LEAVE_WINDOW, &VideoDisplay::OnMouseLeave, this);
+    Bind(wxEVT_LEFT_DCLICK, &VideoDisplay::OnMouseEvent, this);
+    Bind(wxEVT_LEFT_DOWN, &VideoDisplay::OnMouseEvent, this);
+    Bind(wxEVT_LEFT_UP, &VideoDisplay::OnMouseEvent, this);
+    Bind(wxEVT_MOTION, &VideoDisplay::OnMouseEvent, this);
+    Bind(wxEVT_MOUSEWHEEL, &VideoDisplay::OnMouseWheel, this);
+
+    SetCursor(wxNullCursor);
+
+    c->videoDisplay = this;
+
+    con->videoController->JumpToFrame(con->videoController->GetFrameN());
+
+    SetLayoutDirection(wxLayout_LeftToRight);
 }
 
-VideoDisplay::~VideoDisplay () {
-	Unload();
-	con->videoController->Unbind(EVT_FRAME_READY, &VideoDisplay::UploadFrameData, this);
+VideoDisplay::~VideoDisplay ()
+{
+    Unload();
+    con->videoController->Unbind(EVT_FRAME_READY, &VideoDisplay::UploadFrameData, this);
 }
 
-bool VideoDisplay::InitContext() {
-	if (!IsShownOnScreen())
-		return false;
+bool VideoDisplay::InitContext()
+{
+    if (!IsShownOnScreen())
+        return false;
 
-	// If this display is in a minimized detached dialog IsShownOnScreen will
-	// return true, but the client size is guaranteed to be 0
-	if (GetClientSize() == wxSize(0, 0))
-		return false;
+    // If this display is in a minimized detached dialog IsShownOnScreen will
+    // return true, but the client size is guaranteed to be 0
+    if (GetClientSize() == wxSize(0, 0))
+        return false;
 
-	if (!glContext)
-		glContext = agi::make_unique<wxGLContext>(this);
+    if (!glContext)
+        glContext = agi::make_unique<wxGLContext>(this);
 
-	SetCurrent(*glContext);
-	return true;
+    SetCurrent(*glContext);
+    return true;
 }
 
-void VideoDisplay::UploadFrameData(FrameReadyEvent &evt) {
-	pending_frame = evt.frame;
-	Render();
+void VideoDisplay::UploadFrameData(FrameReadyEvent &evt)
+{
+    pending_frame = evt.frame;
+    Render();
 }
 
-void VideoDisplay::Render() try {
-	if (!con->project->VideoProvider() || !InitContext() || (!videoOut && !pending_frame))
-		return;
-
-	if (!videoOut)
-		videoOut = agi::make_unique<VideoOutGL>();
-
-	if (!tool)
-		cmd::call("video/tool/cross", con);
-
-	try {
-		if (pending_frame) {
-			videoOut->UploadFrameData(*pending_frame);
-			pending_frame.reset();
-		}
-	}
-	catch (const VideoOutInitException& err) {
-		wxLogError(
-			"Failed to initialize video display. Closing other running "
-			"programs and updating your video card drivers may fix this.\n"
-			"Error message reported: %s",
-			err.GetMessage());
-		con->project->CloseVideo();
-		return;
-	}
-	catch (const VideoOutRenderException& err) {
-		wxLogError(
-			"Could not upload video frame to graphics card.\n"
-			"Error message reported: %s",
-			err.GetMessage());
-		return;
-	}
-
-	if (videoSize.GetWidth() == 0) videoSize.SetWidth(1);
-	if (videoSize.GetHeight() == 0) videoSize.SetHeight(1);
-
-	if (!viewport_height || !viewport_width)
-		PositionVideo();
-
-	videoOut->Render(viewport_left, viewport_bottom, viewport_width, viewport_height);
-	E(glViewport(0, std::min(viewport_bottom, 0), videoSize.GetWidth(), videoSize.GetHeight()));
-
-	E(glMatrixMode(GL_PROJECTION));
-	E(glLoadIdentity());
-	E(glOrtho(0.0f, videoSize.GetWidth() / scale_factor, videoSize.GetHeight() / scale_factor, 0.0f, -1000.0f, 1000.0f));
-
-	if (OPT_GET("Video/Overscan Mask")->GetBool()) {
-		double ar = con->videoController->GetAspectRatioValue();
-
-		// Based on BBC's guidelines: http://www.bbc.co.uk/guidelines/dq/pdf/tv/tv_standards_london.pdf
-		// 16:9 or wider
-		if (ar > 1.75) {
-			DrawOverscanMask(.1f, .05f);
-			DrawOverscanMask(0.035f, 0.035f);
-		}
-		// Less wide than 16:9 (use 4:3 standard)
-		else {
-			DrawOverscanMask(.067f, .05f);
-			DrawOverscanMask(0.033f, 0.035f);
-		}
-	}
-
-	if ((mouse_pos || !autohideTools->GetBool()) && tool)
-		tool->Draw();
-
-	SwapBuffers();
+void VideoDisplay::Render() try
+{
+    if (!con->project->VideoProvider() || !InitContext() || (!videoOut && !pending_frame))
+        return;
+
+    if (!videoOut)
+        videoOut = agi::make_unique<VideoOutGL>();
+
+    if (!tool)
+        cmd::call("video/tool/cross", con);
+
+    try {
+        if (pending_frame) {
+            videoOut->UploadFrameData(*pending_frame);
+            pending_frame.reset();
+        }
+    }
+    catch (const VideoOutInitException &err) {
+        wxLogError(
+            "Failed to initialize video display. Closing other running "
+            "programs and updating your video card drivers may fix this.\n"
+            "Error message reported: %s",
+            err.GetMessage());
+        con->project->CloseVideo();
+        return;
+    }
+    catch (const VideoOutRenderException &err) {
+        wxLogError(
+            "Could not upload video frame to graphics card.\n"
+            "Error message reported: %s",
+            err.GetMessage());
+        return;
+    }
+
+    if (videoSize.GetWidth() == 0) videoSize.SetWidth(1);
+    if (videoSize.GetHeight() == 0) videoSize.SetHeight(1);
+
+    if (!viewport_height || !viewport_width)
+        PositionVideo();
+
+    videoOut->Render(viewport_left, viewport_bottom, viewport_width, viewport_height);
+    E(glViewport(0, std::min(viewport_bottom, 0), videoSize.GetWidth(), videoSize.GetHeight()));
+
+    E(glMatrixMode(GL_PROJECTION));
+    E(glLoadIdentity());
+    E(glOrtho(0.0f, videoSize.GetWidth() / scale_factor, videoSize.GetHeight() / scale_factor, 0.0f, -1000.0f, 1000.0f));
+
+    if (OPT_GET("Video/Overscan Mask")->GetBool()) {
+        double ar = con->videoController->GetAspectRatioValue();
+
+        // Based on BBC's guidelines: http://www.bbc.co.uk/guidelines/dq/pdf/tv/tv_standards_london.pdf
+        // 16:9 or wider
+        if (ar > 1.75) {
+            DrawOverscanMask(.1f, .05f);
+            DrawOverscanMask(0.035f, 0.035f);
+        }
+        // Less wide than 16:9 (use 4:3 standard)
+        else {
+            DrawOverscanMask(.067f, .05f);
+            DrawOverscanMask(0.033f, 0.035f);
+        }
+    }
+
+    if ((mouse_pos || !autohideTools->GetBool()) && tool)
+        tool->Draw();
+
+    SwapBuffers();
 }
-catch (const agi::Exception &err) {
-	wxLogError(
-		"An error occurred trying to render the video frame on the screen.\n"
-		"Error message reported: %s",
-		err.GetMessage());
-	con->project->CloseVideo();
+catch (const agi::Exception &err)
+{
+    wxLogError(
+        "An error occurred trying to render the video frame on the screen.\n"
+        "Error message reported: %s",
+        err.GetMessage());
+    con->project->CloseVideo();
 }
 
-void VideoDisplay::DrawOverscanMask(float horizontal_percent, float vertical_percent) const {
-	Vector2D v(viewport_width, viewport_height);
-	Vector2D size = Vector2D(horizontal_percent, vertical_percent) / 2 * v;
-
-	// Clockwise from top-left
-	Vector2D corners[] = {
-		size,
-		Vector2D(viewport_width - size.X(), size),
-		v - size,
-		Vector2D(size, viewport_height - size.Y())
-	};
-
-	// Shift to compensate for black bars
-	Vector2D pos(viewport_left, viewport_top);
-	for (auto& corner : corners)
-		corner = corner + pos;
-
-	int count = 0;
-	std::vector<float> points;
-	for (size_t i = 0; i < 4; ++i) {
-		size_t prev = (i + 3) % 4;
-		size_t next = (i + 1) % 4;
-		count += SplineCurve(
-				(corners[prev] + corners[i] * 4) / 5,
-				corners[i], corners[i],
-				(corners[next] + corners[i] * 4) / 5)
-			.GetPoints(points);
-	}
-
-	OpenGLWrapper gl;
-	gl.SetFillColour(wxColor(30, 70, 200), .5f);
-	gl.SetLineColour(*wxBLACK, 0, 1);
-
-	std::vector<int> vstart(1, 0);
-	std::vector<int> vcount(1, count);
-	gl.DrawMultiPolygon(points, vstart, vcount, Vector2D(viewport_left, viewport_top), Vector2D(viewport_width, viewport_height), true);
+void VideoDisplay::DrawOverscanMask(float horizontal_percent, float vertical_percent) const
+{
+    Vector2D v(viewport_width, viewport_height);
+    Vector2D size = Vector2D(horizontal_percent, vertical_percent) / 2 * v;
+
+    // Clockwise from top-left
+    Vector2D corners[] = {
+        size,
+        Vector2D(viewport_width - size.X(), size),
+        v - size,
+        Vector2D(size, viewport_height - size.Y())
+    };
+
+    // Shift to compensate for black bars
+    Vector2D pos(viewport_left, viewport_top);
+    for (auto &corner : corners)
+        corner = corner + pos;
+
+    int count = 0;
+    std::vector<float> points;
+    for (size_t i = 0; i < 4; ++i) {
+        size_t prev = (i + 3) % 4;
+        size_t next = (i + 1) % 4;
+        count += SplineCurve(
+                     (corners[prev] + corners[i] * 4) / 5,
+                     corners[i], corners[i],
+                     (corners[next] + corners[i] * 4) / 5)
+                 .GetPoints(points);
+    }
+
+    OpenGLWrapper gl;
+    gl.SetFillColour(wxColor(30, 70, 200), .5f);
+    gl.SetLineColour(*wxBLACK, 0, 1);
+
+    std::vector<int> vstart(1, 0);
+    std::vector<int> vcount(1, count);
+    gl.DrawMultiPolygon(points, vstart, vcount, Vector2D(viewport_left, viewport_top), Vector2D(viewport_width, viewport_height), true);
 }
 
-void VideoDisplay::PositionVideo() {
-	auto provider = con->project->VideoProvider();
-	if (!provider || !IsShownOnScreen()) return;
-
-	viewport_left = 0;
-	viewport_bottom = GetClientSize().GetHeight() * scale_factor - videoSize.GetHeight();
-	viewport_top = 0;
-	viewport_width = videoSize.GetWidth();
-	viewport_height = videoSize.GetHeight();
-
-	if (freeSize) {
-		int vidW = provider->GetWidth();
-		int vidH = provider->GetHeight();
-
-		AspectRatio arType = con->videoController->GetAspectRatioType();
-		double displayAr = double(viewport_width) / viewport_height;
-		double videoAr = arType == AspectRatio::Default ? double(vidW) / vidH : con->videoController->GetAspectRatioValue();
-
-		// Window is wider than video, blackbox left/right
-		if (displayAr - videoAr > 0.01) {
-			int delta = viewport_width - videoAr * viewport_height;
-			viewport_left = delta / 2;
-			viewport_width -= delta;
-		}
-		// Video is wider than window, blackbox top/bottom
-		else if (videoAr - displayAr > 0.01) {
-			int delta = viewport_height - viewport_width / videoAr;
-			viewport_top = viewport_bottom = delta / 2;
-			viewport_height -= delta;
-		}
-	}
-
-	if (tool)
-		tool->SetDisplayArea(viewport_left / scale_factor, viewport_top / scale_factor,
-		                     viewport_width / scale_factor, viewport_height / scale_factor);
-
-	Render();
+void VideoDisplay::PositionVideo()
+{
+    auto provider = con->project->VideoProvider();
+    if (!provider || !IsShownOnScreen()) return;
+
+    viewport_left = 0;
+    viewport_bottom = GetClientSize().GetHeight() * scale_factor - videoSize.GetHeight();
+    viewport_top = 0;
+    viewport_width = videoSize.GetWidth();
+    viewport_height = videoSize.GetHeight();
+
+    if (freeSize) {
+        int vidW = provider->GetWidth();
+        int vidH = provider->GetHeight();
+
+        AspectRatio arType = con->videoController->GetAspectRatioType();
+        double displayAr = double(viewport_width) / viewport_height;
+        double videoAr = arType == AspectRatio::Default ? double(vidW) / vidH : con->videoController->GetAspectRatioValue();
+
+        // Window is wider than video, blackbox left/right
+        if (displayAr - videoAr > 0.01) {
+            int delta = viewport_width - videoAr * viewport_height;
+            viewport_left = delta / 2;
+            viewport_width -= delta;
+        }
+        // Video is wider than window, blackbox top/bottom
+        else if (videoAr - displayAr > 0.01) {
+            int delta = viewport_height - viewport_width / videoAr;
+            viewport_top = viewport_bottom = delta / 2;
+            viewport_height -= delta;
+        }
+    }
+
+    if (tool)
+        tool->SetDisplayArea(viewport_left / scale_factor, viewport_top / scale_factor,
+                             viewport_width / scale_factor, viewport_height / scale_factor);
+
+    Render();
 }
 
-void VideoDisplay::UpdateSize() {
-	auto provider = con->project->VideoProvider();
-	if (!provider || !IsShownOnScreen()) return;
-
-	videoSize.Set(provider->GetWidth(), provider->GetHeight());
-	videoSize *= zoomValue;
-	if (con->videoController->GetAspectRatioType() != AspectRatio::Default)
-		videoSize.SetWidth(videoSize.GetHeight() * con->videoController->GetAspectRatioValue());
-
-	wxEventBlocker blocker(this);
-	if (freeSize) {
-		wxWindow *top = GetParent();
-		while (!top->IsTopLevel()) top = top->GetParent();
-
-		wxSize cs = GetClientSize();
-		wxSize oldSize = top->GetSize();
-		top->SetSize(top->GetSize() + videoSize / scale_factor - cs);
-		SetClientSize(cs + top->GetSize() - oldSize);
-	}
-	else {
-		SetMinClientSize(videoSize / scale_factor);
-		SetMaxClientSize(videoSize / scale_factor);
-
-		GetGrandParent()->Layout();
-	}
-
-	PositionVideo();
+void VideoDisplay::UpdateSize()
+{
+    auto provider = con->project->VideoProvider();
+    if (!provider || !IsShownOnScreen()) return;
+
+    videoSize.Set(provider->GetWidth(), provider->GetHeight());
+    videoSize *= zoomValue;
+    if (con->videoController->GetAspectRatioType() != AspectRatio::Default)
+        videoSize.SetWidth(videoSize.GetHeight() * con->videoController->GetAspectRatioValue());
+
+    wxEventBlocker blocker(this);
+    if (freeSize) {
+        wxWindow *top = GetParent();
+        while (!top->IsTopLevel()) top = top->GetParent();
+
+        wxSize cs = GetClientSize();
+        wxSize oldSize = top->GetSize();
+        top->SetSize(top->GetSize() + videoSize / scale_factor - cs);
+        SetClientSize(cs + top->GetSize() - oldSize);
+    }
+    else {
+        SetMinClientSize(videoSize / scale_factor);
+        SetMaxClientSize(videoSize / scale_factor);
+
+        GetGrandParent()->Layout();
+    }
+
+    PositionVideo();
 }
 
-void VideoDisplay::OnSizeEvent(wxSizeEvent &event) {
-	if (freeSize) {
-		videoSize = GetClientSize() * scale_factor;
-		PositionVideo();
-		zoomValue = double(viewport_height) / con->project->VideoProvider()->GetHeight();
-		zoomBox->ChangeValue(fmt_wx("%g%%", zoomValue * 100.));
-		con->ass->Properties.video_zoom = zoomValue;
-	}
-	else {
-		PositionVideo();
-	}
+void VideoDisplay::OnSizeEvent(wxSizeEvent &event)
+{
+    if (freeSize) {
+        videoSize = GetClientSize() * scale_factor;
+        PositionVideo();
+        zoomValue = double(viewport_height) / con->project->VideoProvider()->GetHeight();
+        zoomBox->ChangeValue(fmt_wx("%g%%", zoomValue * 100.));
+        con->ass->Properties.video_zoom = zoomValue;
+    }
+    else {
+        PositionVideo();
+    }
 }
 
-void VideoDisplay::OnMouseEvent(wxMouseEvent& event) {
-	if (event.ButtonDown())
-		SetFocus();
+void VideoDisplay::OnMouseEvent(wxMouseEvent &event)
+{
+    if (event.ButtonDown())
+        SetFocus();
 
-	last_mouse_pos = mouse_pos = event.GetPosition();
+    last_mouse_pos = mouse_pos = event.GetPosition();
 
-	if (tool)
-		tool->OnMouseEvent(event);
+    if (tool)
+        tool->OnMouseEvent(event);
 }
 
-void VideoDisplay::OnMouseLeave(wxMouseEvent& event) {
-	mouse_pos = Vector2D();
-	if (tool)
-		tool->OnMouseEvent(event);
+void VideoDisplay::OnMouseLeave(wxMouseEvent &event)
+{
+    mouse_pos = Vector2D();
+    if (tool)
+        tool->OnMouseEvent(event);
 }
 
-void VideoDisplay::OnMouseWheel(wxMouseEvent& event) {
-	if (int wheel = event.GetWheelRotation()) {
-		if (ForwardMouseWheelEvent(this, event))
-			SetZoom(zoomValue + .125 * (wheel / event.GetWheelDelta()));
-	}
+void VideoDisplay::OnMouseWheel(wxMouseEvent &event)
+{
+    if (int wheel = event.GetWheelRotation()) {
+        if (ForwardMouseWheelEvent(this, event))
+            SetZoom(zoomValue + .125 * (wheel / event.GetWheelDelta()));
+    }
 }
 
-void VideoDisplay::OnContextMenu(wxContextMenuEvent&) {
-	if (!context_menu) context_menu = menu::GetMenu("video_context", con);
-	SetCursor(wxNullCursor);
-	menu::OpenPopupMenu(context_menu.get(), this);
+void VideoDisplay::OnContextMenu(wxContextMenuEvent &)
+{
+    if (!context_menu) context_menu = menu::GetMenu("video_context", con);
+    SetCursor(wxNullCursor);
+    menu::OpenPopupMenu(context_menu.get(), this);
 }
 
-void VideoDisplay::OnKeyDown(wxKeyEvent &event) {
-	hotkey::check("Video", con, event);
+void VideoDisplay::OnKeyDown(wxKeyEvent &event)
+{
+    hotkey::check("Video", con, event);
 }
 
-void VideoDisplay::SetZoom(double value) {
-	if (value == 0) return;
-	zoomValue = std::max(value, .125);
-	size_t selIndex = zoomValue / .125 - 1;
-	if (selIndex < zoomBox->GetCount())
-		zoomBox->SetSelection(selIndex);
-	zoomBox->ChangeValue(fmt_wx("%g%%", zoomValue * 100.));
-	con->ass->Properties.video_zoom = zoomValue;
-	UpdateSize();
+void VideoDisplay::SetZoom(double value)
+{
+    if (value == 0) return;
+    zoomValue = std::max(value, .125);
+    size_t selIndex = zoomValue / .125 - 1;
+    if (selIndex < zoomBox->GetCount())
+        zoomBox->SetSelection(selIndex);
+    zoomBox->ChangeValue(fmt_wx("%g%%", zoomValue * 100.));
+    con->ass->Properties.video_zoom = zoomValue;
+    UpdateSize();
 }
 
-void VideoDisplay::SetZoomFromBox(wxCommandEvent &) {
-	int sel = zoomBox->GetSelection();
-	if (sel != wxNOT_FOUND) {
-		zoomValue = (sel + 1) * .125;
-		con->ass->Properties.video_zoom = zoomValue;
-		UpdateSize();
-	}
+void VideoDisplay::SetZoomFromBox(wxCommandEvent &)
+{
+    int sel = zoomBox->GetSelection();
+    if (sel != wxNOT_FOUND) {
+        zoomValue = (sel + 1) * .125;
+        con->ass->Properties.video_zoom = zoomValue;
+        UpdateSize();
+    }
 }
 
-void VideoDisplay::SetZoomFromBoxText(wxCommandEvent &) {
-	wxString strValue = zoomBox->GetValue();
-	if (strValue.EndsWith("%"))
-		strValue.RemoveLast();
+void VideoDisplay::SetZoomFromBoxText(wxCommandEvent &)
+{
+    wxString strValue = zoomBox->GetValue();
+    if (strValue.EndsWith("%"))
+        strValue.RemoveLast();
 
-	double value;
-	if (strValue.ToDouble(&value))
-		SetZoom(value / 100.);
+    double value;
+    if (strValue.ToDouble(&value))
+        SetZoom(value / 100.);
 }
 
-void VideoDisplay::SetTool(std::unique_ptr<VisualToolBase> new_tool) {
-	toolBar->ClearTools();
-	toolBar->Realize();
-	toolBar->Show(false);
-
-	tool = std::move(new_tool);
-	tool->SetToolbar(toolBar);
-
-	// Update size as the new typesetting tool may have changed the subtoolbar size
-	if (!freeSize)
-		UpdateSize();
-	else {
-		// UpdateSize fits the window to the video, which we don't want to do
-		GetGrandParent()->Layout();
-		tool->SetDisplayArea(viewport_left / scale_factor, viewport_top / scale_factor,
-		                     viewport_width / scale_factor, viewport_height / scale_factor);
-	}
+void VideoDisplay::SetTool(std::unique_ptr<VisualToolBase> new_tool)
+{
+    toolBar->ClearTools();
+    toolBar->Realize();
+    toolBar->Show(false);
+
+    tool = std::move(new_tool);
+    tool->SetToolbar(toolBar);
+
+    // Update size as the new typesetting tool may have changed the subtoolbar size
+    if (!freeSize)
+        UpdateSize();
+    else {
+        // UpdateSize fits the window to the video, which we don't want to do
+        GetGrandParent()->Layout();
+        tool->SetDisplayArea(viewport_left / scale_factor, viewport_top / scale_factor,
+                             viewport_width / scale_factor, viewport_height / scale_factor);
+    }
 }
 
-bool VideoDisplay::ToolIsType(std::type_info const& type) const {
+bool VideoDisplay::ToolIsType(std::type_info const &type) const
+{
     if (! tool)
         return false;
     auto &tool_type = *tool;
-	return typeid(tool_type) == type;
+    return typeid(tool_type) == type;
 }
 
-Vector2D VideoDisplay::GetMousePosition() const {
-	return last_mouse_pos ? tool->ToScriptCoords(last_mouse_pos) : last_mouse_pos;
+Vector2D VideoDisplay::GetMousePosition() const
+{
+    return last_mouse_pos ? tool->ToScriptCoords(last_mouse_pos) : last_mouse_pos;
 }
 
-void VideoDisplay::Unload() {
-	if (glContext) {
-		SetCurrent(*glContext);
-	}
-	videoOut.reset();
-	tool.reset();
-	glContext.reset();
-	pending_frame.reset();
+void VideoDisplay::Unload()
+{
+    if (glContext) {
+        SetCurrent(*glContext);
+    }
+    videoOut.reset();
+    tool.reset();
+    glContext.reset();
+    pending_frame.reset();
 }
diff --git a/src/video_display.h b/src/video_display.h
index f0d65edb671a8f85acd96443fb522d84bec1de93..254790bf69c8f22fe65070820528b4677a7ce2fa 100644
--- a/src/video_display.h
+++ b/src/video_display.h
@@ -53,120 +53,120 @@ struct FrameReadyEvent;
 struct VideoFrame;
 
 namespace agi {
-	struct Context;
-	class OptionValue;
+struct Context;
+class OptionValue;
 }
 
 class VideoDisplay final : public wxGLCanvas {
-	/// Signals the display is connected to
-	std::vector<agi::signal::Connection> connections;
+    /// Signals the display is connected to
+    std::vector<agi::signal::Connection> connections;
 
-	const agi::OptionValue* autohideTools;
+    const agi::OptionValue *autohideTools;
 
-	agi::Context *con;
+    agi::Context *con;
 
-	std::unique_ptr<wxMenu> context_menu;
+    std::unique_ptr<wxMenu> context_menu;
 
-	/// The size of the video in screen at the current zoom level, which may not
-	/// be the same as the actual client size of the display
-	wxSize videoSize;
+    /// The size of the video in screen at the current zoom level, which may not
+    /// be the same as the actual client size of the display
+    wxSize videoSize;
 
-	Vector2D last_mouse_pos, mouse_pos;
+    Vector2D last_mouse_pos, mouse_pos;
 
-	/// Screen pixels between the left of the canvas and the left of the video
-	int viewport_left = 0;
-	/// The width of the video in screen pixels
-	int viewport_width = 0;
-	/// Screen pixels between the bottom of the canvas and the bottom of the video; used for glViewport
-	int viewport_bottom = 0;
-	/// Screen pixels between the bottom of the canvas and the top of the video; used for coordinate space conversion
-	int viewport_top = 0;
-	/// The height of the video in screen pixels
-	int viewport_height = 0;
+    /// Screen pixels between the left of the canvas and the left of the video
+    int viewport_left = 0;
+    /// The width of the video in screen pixels
+    int viewport_width = 0;
+    /// Screen pixels between the bottom of the canvas and the bottom of the video; used for glViewport
+    int viewport_bottom = 0;
+    /// Screen pixels between the bottom of the canvas and the top of the video; used for coordinate space conversion
+    int viewport_top = 0;
+    /// The height of the video in screen pixels
+    int viewport_height = 0;
 
-	/// The current zoom level, where 1.0 = 100%
-	double zoomValue;
+    /// The current zoom level, where 1.0 = 100%
+    double zoomValue;
 
-	/// The video renderer
-	std::unique_ptr<VideoOutGL> videoOut;
+    /// The video renderer
+    std::unique_ptr<VideoOutGL> videoOut;
 
-	/// The active visual typesetting tool
-	std::unique_ptr<VisualToolBase> tool;
-	/// The toolbar used by individual typesetting tools
-	wxToolBar* toolBar;
+    /// The active visual typesetting tool
+    std::unique_ptr<VisualToolBase> tool;
+    /// The toolbar used by individual typesetting tools
+    wxToolBar *toolBar;
 
-	/// The OpenGL context for this display
-	std::unique_ptr<wxGLContext> glContext;
+    /// The OpenGL context for this display
+    std::unique_ptr<wxGLContext> glContext;
 
-	/// The dropdown box for selecting zoom levels
-	wxComboBox *zoomBox;
+    /// The dropdown box for selecting zoom levels
+    wxComboBox *zoomBox;
 
-	/// Whether the display can be freely resized by the user
-	bool freeSize;
+    /// Whether the display can be freely resized by the user
+    bool freeSize;
 
-	/// Frame which will replace the currently visible frame on the next render
-	std::shared_ptr<VideoFrame> pending_frame;
+    /// Frame which will replace the currently visible frame on the next render
+    std::shared_ptr<VideoFrame> pending_frame;
 
-	std::unique_ptr<RetinaHelper> retina_helper;
-	int scale_factor;
-	agi::signal::Connection scale_factor_connection;
+    std::unique_ptr<RetinaHelper> retina_helper;
+    int scale_factor;
+    agi::signal::Connection scale_factor_connection;
 
-	/// @brief Draw an overscan mask
-	/// @param horizontal_percent The percent of the video reserved horizontally
-	/// @param vertical_percent The percent of the video reserved vertically
-	void DrawOverscanMask(float horizontal_percent, float vertical_percent) const;
+    /// @brief Draw an overscan mask
+    /// @param horizontal_percent The percent of the video reserved horizontally
+    /// @param vertical_percent The percent of the video reserved vertically
+    void DrawOverscanMask(float horizontal_percent, float vertical_percent) const;
 
-	/// Upload the image for the current frame to the video card
-	void UploadFrameData(FrameReadyEvent&);
+    /// Upload the image for the current frame to the video card
+    void UploadFrameData(FrameReadyEvent &);
 
-	/// @brief Initialize the gl context and set the active context to this one
-	/// @return Could the context be set?
-	bool InitContext();
+    /// @brief Initialize the gl context and set the active context to this one
+    /// @return Could the context be set?
+    bool InitContext();
 
-	/// @brief Set the size of the display based on the current zoom and video resolution
-	void UpdateSize();
-	void PositionVideo();
-	/// Set the zoom level to that indicated by the dropdown
-	void SetZoomFromBox(wxCommandEvent&);
-	/// Set the zoom level to that indicated by the text
-	void SetZoomFromBoxText(wxCommandEvent&);
+    /// @brief Set the size of the display based on the current zoom and video resolution
+    void UpdateSize();
+    void PositionVideo();
+    /// Set the zoom level to that indicated by the dropdown
+    void SetZoomFromBox(wxCommandEvent &);
+    /// Set the zoom level to that indicated by the text
+    void SetZoomFromBoxText(wxCommandEvent &);
 
-	/// @brief Key event handler
-	void OnKeyDown(wxKeyEvent &event);
-	/// @brief Mouse event handler
-	void OnMouseEvent(wxMouseEvent& event);
-	void OnMouseWheel(wxMouseEvent& event);
-	void OnMouseLeave(wxMouseEvent& event);
-	/// @brief Recalculate video positioning and scaling when the available area or zoom changes
-	void OnSizeEvent(wxSizeEvent &event);
-	void OnContextMenu(wxContextMenuEvent&);
+    /// @brief Key event handler
+    void OnKeyDown(wxKeyEvent &event);
+    /// @brief Mouse event handler
+    void OnMouseEvent(wxMouseEvent &event);
+    void OnMouseWheel(wxMouseEvent &event);
+    void OnMouseLeave(wxMouseEvent &event);
+    /// @brief Recalculate video positioning and scaling when the available area or zoom changes
+    void OnSizeEvent(wxSizeEvent &event);
+    void OnContextMenu(wxContextMenuEvent &);
 
 public:
-	/// @brief Constructor
-	VideoDisplay(
-		wxToolBar *visualSubToolBar,
-		bool isDetached,
-		wxComboBox *zoomBox,
-		wxWindow* parent,
-		agi::Context *context);
-	~VideoDisplay();
+    /// @brief Constructor
+    VideoDisplay(
+        wxToolBar *visualSubToolBar,
+        bool isDetached,
+        wxComboBox *zoomBox,
+        wxWindow *parent,
+        agi::Context *context);
+    ~VideoDisplay();
 
-	/// @brief Render the currently visible frame
-	void Render();
+    /// @brief Render the currently visible frame
+    void Render();
 
-	/// @brief Set the zoom level
-	/// @param value The new zoom level
-	void SetZoom(double value);
-	/// @brief Get the current zoom level
-	double GetZoom() const { return zoomValue; }
+    /// @brief Set the zoom level
+    /// @param value The new zoom level
+    void SetZoom(double value);
+    /// @brief Get the current zoom level
+    double GetZoom() const { return zoomValue; }
 
-	/// Get the last seen position of the mouse in script coordinates
-	Vector2D GetMousePosition() const;
+    /// Get the last seen position of the mouse in script coordinates
+    Vector2D GetMousePosition() const;
 
-	void SetTool(std::unique_ptr<VisualToolBase> new_tool);
+    void SetTool(std::unique_ptr<VisualToolBase> new_tool);
 
-	bool ToolIsType(std::type_info const& type) const;
+    bool ToolIsType(std::type_info const &type) const;
 
-	/// Discard all OpenGL state
-	void Unload();
+    /// Discard all OpenGL state
+    void Unload();
 };
diff --git a/src/video_frame.cpp b/src/video_frame.cpp
index 9f22f506376b427342a504f62dd669ee06328d4b..06edba8fde78cea45d0d4f92073d84f338e824cd 100644
--- a/src/video_frame.cpp
+++ b/src/video_frame.cpp
@@ -24,27 +24,28 @@
 #include <wx/image.h>
 
 namespace {
-	// We actually have bgr_, not bgra, so we need a custom converter which ignores the alpha channel
-	struct color_converter {
-		template <typename P1, typename P2>
-		void operator()(P1 const& src, P2& dst) const {
-			using namespace boost::gil;
-			dst = rgb8_pixel_t(
-				get_color(src, red_t()),
-				get_color(src, green_t()),
-				get_color(src, blue_t()));
-		}
-	};
+// We actually have bgr_, not bgra, so we need a custom converter which ignores the alpha channel
+struct color_converter {
+    template <typename P1, typename P2>
+    void operator()(P1 const &src, P2 &dst) const {
+        using namespace boost::gil;
+        dst = rgb8_pixel_t(
+                  get_color(src, red_t()),
+                  get_color(src, green_t()),
+                  get_color(src, blue_t()));
+    }
+};
 }
 
-wxImage GetImage(VideoFrame const& frame) {
-	using namespace boost::gil;
+wxImage GetImage(VideoFrame const &frame)
+{
+    using namespace boost::gil;
 
-	wxImage img(frame.width, frame.height);
-	auto src = interleaved_view(frame.width, frame.height, (bgra8_pixel_t*)frame.data.data(), frame.pitch);
-	auto dst = interleaved_view(frame.width, frame.height, (rgb8_pixel_t*)img.GetData(), 3 * frame.width);
-	if (frame.flipped)
-		src = flipped_up_down_view(src);
-	copy_and_convert_pixels(src, dst, color_converter());
-	return img;
+    wxImage img(frame.width, frame.height);
+    auto src = interleaved_view(frame.width, frame.height, (bgra8_pixel_t *)frame.data.data(), frame.pitch);
+    auto dst = interleaved_view(frame.width, frame.height, (rgb8_pixel_t *)img.GetData(), 3 * frame.width);
+    if (frame.flipped)
+        src = flipped_up_down_view(src);
+    copy_and_convert_pixels(src, dst, color_converter());
+    return img;
 }
diff --git a/src/video_frame.h b/src/video_frame.h
index 2a47ed69c023fa037f00e779b73e648ef67405c3..f743d8eac98937d8f0c4749b912e362fa302c039 100644
--- a/src/video_frame.h
+++ b/src/video_frame.h
@@ -19,11 +19,11 @@
 class wxImage;
 
 struct VideoFrame {
-	std::vector<unsigned char> data;
-	size_t width;
-	size_t height;
-	size_t pitch;
-	bool flipped;
+    std::vector<unsigned char> data;
+    size_t width;
+    size_t height;
+    size_t pitch;
+    bool flipped;
 };
 
-wxImage GetImage(VideoFrame const& frame);
+wxImage GetImage(VideoFrame const &frame);
diff --git a/src/video_out_gl.cpp b/src/video_out_gl.cpp
index 75c6eae6ba5bded701c9f62caef33ad875fec3ca..fca88cb39847c4d2348f8dc36b6e72a1c1e7434d 100644
--- a/src/video_out_gl.cpp
+++ b/src/video_out_gl.cpp
@@ -37,9 +37,10 @@
 
 namespace {
 template<typename Exception>
-BOOST_NOINLINE void throw_error(GLenum err, const char *msg) {
-	LOG_E("video/out/gl") << msg << " failed with error code " << err;
-	throw Exception(msg, err);
+BOOST_NOINLINE void throw_error(GLenum err, const char *msg)
+{
+    LOG_E("video/out/gl") << msg << " failed with error code " << err;
+    throw Exception(msg, err);
 }
 }
 
@@ -55,10 +56,10 @@ BOOST_NOINLINE void throw_error(GLenum err, const char *msg) {
 
 /// @brief Structure tracking all precomputable information about a subtexture
 struct VideoOutGL::TextureInfo {
-	GLuint textureID = 0;
-	int dataOffset = 0;
-	int sourceH = 0;
-	int sourceW = 0;
+    GLuint textureID = 0;
+    int dataOffset = 0;
+    int sourceH = 0;
+    int sourceW = 0;
 };
 
 /// @brief Test if a texture can be created
@@ -66,33 +67,35 @@ struct VideoOutGL::TextureInfo {
 /// @param height The height of the texture
 /// @param format The texture's format
 /// @return Whether the texture could be created.
-static bool TestTexture(int width, int height, GLint format) {
-	glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-	glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
-	while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
-
-	LOG_I("video/out/gl") << "VideoOutGL::TestTexture: " << width << "x" << height;
-	return format != 0;
+static bool TestTexture(int width, int height, GLint format)
+{
+    glTexImage2D(GL_PROXY_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+    glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
+    while (glGetError()) { } // Silently swallow all errors as we don't care why it failed if it did
+
+    LOG_I("video/out/gl") << "VideoOutGL::TestTexture: " << width << "x" << height;
+    return format != 0;
 }
 
 VideoOutGL::VideoOutGL() { }
 
 /// @brief Runtime detection of required OpenGL capabilities
-void VideoOutGL::DetectOpenGLCapabilities() {
-	if (maxTextureSize != 0) return;
-
-	// Test for supported internalformats
-	if (TestTexture(64, 64, GL_RGBA8)) internalFormat = GL_RGBA8;
-	else if (TestTexture(64, 64, GL_RGBA)) internalFormat = GL_RGBA;
-	else throw VideoOutInitException("Could not create a 64x64 RGB texture in any format.");
-
-	// Test for the maximum supported texture size
-	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
-	while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
-	LOG_I("video/out/gl") << "Maximum texture size is " << maxTextureSize << "x" << maxTextureSize;
-
-	// Test for rectangular texture support
-	supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
+void VideoOutGL::DetectOpenGLCapabilities()
+{
+    if (maxTextureSize != 0) return;
+
+    // Test for supported internalformats
+    if (TestTexture(64, 64, GL_RGBA8)) internalFormat = GL_RGBA8;
+    else if (TestTexture(64, 64, GL_RGBA)) internalFormat = GL_RGBA;
+    else throw VideoOutInitException("Could not create a 64x64 RGB texture in any format.");
+
+    // Test for the maximum supported texture size
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    while (maxTextureSize > 64 && !TestTexture(maxTextureSize, maxTextureSize, internalFormat)) maxTextureSize >>= 1;
+    LOG_I("video/out/gl") << "Maximum texture size is " << maxTextureSize << "x" << maxTextureSize;
+
+    // Test for rectangular texture support
+    supportsRectangularTextures = TestTexture(maxTextureSize, maxTextureSize >> 1, internalFormat);
 }
 
 /// @brief If needed, create the grid of textures for displaying frames of the given format
@@ -100,184 +103,188 @@ void VideoOutGL::DetectOpenGLCapabilities() {
 /// @param height The frame's height
 /// @param format The frame's format
 /// @param bpp The frame's bytes per pixel
-void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped) {
-	using namespace std;
-
-	// Do nothing if the frame size and format are unchanged
-	if (width == frameWidth && height == frameHeight && format == frameFormat && flipped == frameFlipped) return;
-	frameWidth  = width;
-	frameHeight = height;
-	frameFormat = format;
-	frameFlipped = flipped;
-	LOG_I("video/out/gl") << "Video size: " << width << "x" << height;
-
-	DetectOpenGLCapabilities();
-
-	// Clean up old textures
-	if (textureIdList.size() > 0) {
-		CHECK_INIT_ERROR(glDeleteTextures(textureIdList.size(), &textureIdList[0]));
-		textureIdList.clear();
-		textureList.clear();
-	}
-
-	// Create the textures
-	int textureArea = maxTextureSize - 2;
-	textureRows  = (int)ceil(double(height) / textureArea);
-	textureCols  = (int)ceil(double(width) / textureArea);
-	textureCount = textureRows * textureCols;
-	textureIdList.resize(textureCount);
-	textureList.resize(textureCount);
-	CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
-	vector<pair<int, int>> textureSizes;
-	textureSizes.reserve(textureCount);
-
-	/* Unfortunately, we can't simply use one of the two standard ways to do
-	 * tiled textures to work around texture size limits in OpenGL, due to our
-	 * need to support Microsoft's OpenGL emulation for RDP/VPC/video card
-	 * drivers that don't support OpenGL (such as the ones which Windows
-	 * Update pushes for ATI cards in Windows 7). GL_CLAMP_TO_EDGE requires
-	 * OpenGL 1.2, but the emulation only supports 1.1. GL_CLAMP + borders has
-	 * correct results, but takes several seconds to render each frame. As a
-	 * result, the code below essentially manually reimplements borders, by
-	 * just not using the edge when mapping the texture onto a quad. The one
-	 * exception to this is the texture edges which are also frame edges, as
-	 * there does not appear to be a trivial way to mirror the edges, and the
-	 * nontrivial ways are more complex that is worth to avoid a single row of
-	 * slightly discolored pixels along the edges at zooms over 100%.
-	 *
-	 * Given a 64x64 maximum texture size:
-	 *     Quads touching the top of the frame are 63 pixels tall
-	 *     Quads touching the bottom of the frame are up to 63 pixels tall
-	 *     All other quads are 62 pixels tall
-	 *     Quads not on the top skip the first row of the texture
-	 *     Quads not on the bottom skip the last row of the texture
-	 *     Width behaves in the same way with respect to left/right edges
-	 */
-
-	// Set up the display list
-	CHECK_ERROR(dl = glGenLists(1));
-	CHECK_ERROR(glNewList(dl, GL_COMPILE));
-
-	CHECK_ERROR(glClearColor(0,0,0,0));
-	CHECK_ERROR(glClearStencil(0));
-	CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
-
-	CHECK_ERROR(glShadeModel(GL_FLAT));
-	CHECK_ERROR(glDisable(GL_BLEND));
-
-	// Switch to video coordinates
-	CHECK_ERROR(glMatrixMode(GL_PROJECTION));
-	CHECK_ERROR(glLoadIdentity());
-	CHECK_ERROR(glPushMatrix());
-	if (frameFlipped) {
-		CHECK_ERROR(glOrtho(0.0f, frameWidth, 0.0f, frameHeight, -1000.0f, 1000.0f));
-	}
-	else {
-		CHECK_ERROR(glOrtho(0.0f, frameWidth, frameHeight, 0.0f, -1000.0f, 1000.0f));
-	}
-
-	CHECK_ERROR(glEnable(GL_TEXTURE_2D));
-
-	// Calculate the position information for each texture
-	int lastRow = textureRows - 1;
-	int lastCol = textureCols - 1;
-	for (int row = 0; row < textureRows; ++row) {
-		for (int col = 0; col < textureCols; ++col) {
-			TextureInfo& ti = textureList[row * textureCols + col];
-
-			// Width and height of the area read from the frame data
-			int sourceX = col * textureArea;
-			int sourceY = row * textureArea;
-			ti.sourceW  = std::min(frameWidth  - sourceX, maxTextureSize);
-			ti.sourceH  = std::min(frameHeight - sourceY, maxTextureSize);
-
-			// Used instead of GL_PACK_SKIP_ROWS/GL_PACK_SKIP_PIXELS due to
-			// performance issues with the emulation
-			ti.dataOffset = sourceY * frameWidth * bpp + sourceX * bpp;
-
-			int textureHeight = SmallestPowerOf2(ti.sourceH);
-			int textureWidth  = SmallestPowerOf2(ti.sourceW);
-			if (!supportsRectangularTextures) {
-				textureWidth = textureHeight = std::max(textureWidth, textureHeight);
-			}
-
-			// Location where this texture is placed
-			// X2/Y2 will be offscreen unless the video frame happens to
-			// exactly use all of the texture
-			float x1 = sourceX + (col != 0);
-			float y1 = sourceY + (row != 0);
-			float x2 = sourceX + textureWidth - (col != lastCol);
-			float y2 = sourceY + textureHeight - (row != lastRow);
-
-			// Portion of the texture actually used
-			float top    = row == 0 ? 0 : 1.0f / textureHeight;
-			float left   = col == 0 ? 0 : 1.0f / textureWidth;
-			float bottom = row == lastRow ? 1.0f : 1.0f - 1.0f / textureHeight;
-			float right  = col == lastCol ? 1.0f : 1.0f - 1.0f / textureWidth;
-
-			// Store the stuff needed later
-			ti.textureID = textureIdList[row * textureCols + col];
-			textureSizes.push_back(make_pair(textureWidth, textureHeight));
-
-			CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
-			CHECK_ERROR(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
-
-			// Place the texture
-			glBegin(GL_QUADS);
-				glTexCoord2f(left,  top);     glVertex2f(x1, y1);
-				glTexCoord2f(right, top);     glVertex2f(x2, y1);
-				glTexCoord2f(right, bottom);  glVertex2f(x2, y2);
-				glTexCoord2f(left,  bottom);  glVertex2f(x1, y2);
-			glEnd();
-			if (GLenum err = glGetError()) throw VideoOutRenderException("GL_QUADS", err);
-		}
-	}
-	CHECK_ERROR(glDisable(GL_TEXTURE_2D));
-	CHECK_ERROR(glPopMatrix());
-
-	glEndList();
-
-	// Create the textures outside of the display list as there's no need to
-	// remake them on every frame
-	for (int i = 0; i < textureCount; ++i) {
-		LOG_I("video/out/gl") << "Using texture size: " << textureSizes[i].first << "x" << textureSizes[i].second;
-		CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, textureIdList[i]));
-		CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, textureSizes[i].first, textureSizes[i].second, 0, format, GL_UNSIGNED_BYTE, nullptr));
-		CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
-		CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
-		CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
-		CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
-	}
+void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, bool flipped)
+{
+    using namespace std;
+
+    // Do nothing if the frame size and format are unchanged
+    if (width == frameWidth && height == frameHeight && format == frameFormat && flipped == frameFlipped) return;
+    frameWidth  = width;
+    frameHeight = height;
+    frameFormat = format;
+    frameFlipped = flipped;
+    LOG_I("video/out/gl") << "Video size: " << width << "x" << height;
+
+    DetectOpenGLCapabilities();
+
+    // Clean up old textures
+    if (textureIdList.size() > 0) {
+        CHECK_INIT_ERROR(glDeleteTextures(textureIdList.size(), &textureIdList[0]));
+        textureIdList.clear();
+        textureList.clear();
+    }
+
+    // Create the textures
+    int textureArea = maxTextureSize - 2;
+    textureRows  = (int)ceil(double(height) / textureArea);
+    textureCols  = (int)ceil(double(width) / textureArea);
+    textureCount = textureRows * textureCols;
+    textureIdList.resize(textureCount);
+    textureList.resize(textureCount);
+    CHECK_INIT_ERROR(glGenTextures(textureIdList.size(), &textureIdList[0]));
+    vector<pair<int, int>> textureSizes;
+    textureSizes.reserve(textureCount);
+
+    /* Unfortunately, we can't simply use one of the two standard ways to do
+     * tiled textures to work around texture size limits in OpenGL, due to our
+     * need to support Microsoft's OpenGL emulation for RDP/VPC/video card
+     * drivers that don't support OpenGL (such as the ones which Windows
+     * Update pushes for ATI cards in Windows 7). GL_CLAMP_TO_EDGE requires
+     * OpenGL 1.2, but the emulation only supports 1.1. GL_CLAMP + borders has
+     * correct results, but takes several seconds to render each frame. As a
+     * result, the code below essentially manually reimplements borders, by
+     * just not using the edge when mapping the texture onto a quad. The one
+     * exception to this is the texture edges which are also frame edges, as
+     * there does not appear to be a trivial way to mirror the edges, and the
+     * nontrivial ways are more complex that is worth to avoid a single row of
+     * slightly discolored pixels along the edges at zooms over 100%.
+     *
+     * Given a 64x64 maximum texture size:
+     *     Quads touching the top of the frame are 63 pixels tall
+     *     Quads touching the bottom of the frame are up to 63 pixels tall
+     *     All other quads are 62 pixels tall
+     *     Quads not on the top skip the first row of the texture
+     *     Quads not on the bottom skip the last row of the texture
+     *     Width behaves in the same way with respect to left/right edges
+     */
+
+    // Set up the display list
+    CHECK_ERROR(dl = glGenLists(1));
+    CHECK_ERROR(glNewList(dl, GL_COMPILE));
+
+    CHECK_ERROR(glClearColor(0, 0, 0, 0));
+    CHECK_ERROR(glClearStencil(0));
+    CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
+
+    CHECK_ERROR(glShadeModel(GL_FLAT));
+    CHECK_ERROR(glDisable(GL_BLEND));
+
+    // Switch to video coordinates
+    CHECK_ERROR(glMatrixMode(GL_PROJECTION));
+    CHECK_ERROR(glLoadIdentity());
+    CHECK_ERROR(glPushMatrix());
+    if (frameFlipped) {
+        CHECK_ERROR(glOrtho(0.0f, frameWidth, 0.0f, frameHeight, -1000.0f, 1000.0f));
+    }
+    else {
+        CHECK_ERROR(glOrtho(0.0f, frameWidth, frameHeight, 0.0f, -1000.0f, 1000.0f));
+    }
+
+    CHECK_ERROR(glEnable(GL_TEXTURE_2D));
+
+    // Calculate the position information for each texture
+    int lastRow = textureRows - 1;
+    int lastCol = textureCols - 1;
+    for (int row = 0; row < textureRows; ++row) {
+        for (int col = 0; col < textureCols; ++col) {
+            TextureInfo &ti = textureList[row * textureCols + col];
+
+            // Width and height of the area read from the frame data
+            int sourceX = col * textureArea;
+            int sourceY = row * textureArea;
+            ti.sourceW  = std::min(frameWidth  - sourceX, maxTextureSize);
+            ti.sourceH  = std::min(frameHeight - sourceY, maxTextureSize);
+
+            // Used instead of GL_PACK_SKIP_ROWS/GL_PACK_SKIP_PIXELS due to
+            // performance issues with the emulation
+            ti.dataOffset = sourceY * frameWidth * bpp + sourceX * bpp;
+
+            int textureHeight = SmallestPowerOf2(ti.sourceH);
+            int textureWidth  = SmallestPowerOf2(ti.sourceW);
+            if (!supportsRectangularTextures) {
+                textureWidth = textureHeight = std::max(textureWidth, textureHeight);
+            }
+
+            // Location where this texture is placed
+            // X2/Y2 will be offscreen unless the video frame happens to
+            // exactly use all of the texture
+            float x1 = sourceX + (col != 0);
+            float y1 = sourceY + (row != 0);
+            float x2 = sourceX + textureWidth - (col != lastCol);
+            float y2 = sourceY + textureHeight - (row != lastRow);
+
+            // Portion of the texture actually used
+            float top    = row == 0 ? 0 : 1.0f / textureHeight;
+            float left   = col == 0 ? 0 : 1.0f / textureWidth;
+            float bottom = row == lastRow ? 1.0f : 1.0f - 1.0f / textureHeight;
+            float right  = col == lastCol ? 1.0f : 1.0f - 1.0f / textureWidth;
+
+            // Store the stuff needed later
+            ti.textureID = textureIdList[row * textureCols + col];
+            textureSizes.push_back(make_pair(textureWidth, textureHeight));
+
+            CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
+            CHECK_ERROR(glColor4f(1.0f, 1.0f, 1.0f, 1.0f));
+
+            // Place the texture
+            glBegin(GL_QUADS);
+            glTexCoord2f(left,  top);     glVertex2f(x1, y1);
+            glTexCoord2f(right, top);     glVertex2f(x2, y1);
+            glTexCoord2f(right, bottom);  glVertex2f(x2, y2);
+            glTexCoord2f(left,  bottom);  glVertex2f(x1, y2);
+            glEnd();
+            if (GLenum err = glGetError()) throw VideoOutRenderException("GL_QUADS", err);
+        }
+    }
+    CHECK_ERROR(glDisable(GL_TEXTURE_2D));
+    CHECK_ERROR(glPopMatrix());
+
+    glEndList();
+
+    // Create the textures outside of the display list as there's no need to
+    // remake them on every frame
+    for (int i = 0; i < textureCount; ++i) {
+        LOG_I("video/out/gl") << "Using texture size: " << textureSizes[i].first << "x" << textureSizes[i].second;
+        CHECK_INIT_ERROR(glBindTexture(GL_TEXTURE_2D, textureIdList[i]));
+        CHECK_INIT_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, textureSizes[i].first, textureSizes[i].second, 0, format, GL_UNSIGNED_BYTE, nullptr));
+        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP));
+        CHECK_INIT_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP));
+    }
 }
 
-void VideoOutGL::UploadFrameData(VideoFrame const& frame) {
-	if (frame.height == 0 || frame.width == 0) return;
+void VideoOutGL::UploadFrameData(VideoFrame const &frame)
+{
+    if (frame.height == 0 || frame.width == 0) return;
 
-	InitTextures(frame.width, frame.height, GL_BGRA_EXT, 4, frame.flipped);
+    InitTextures(frame.width, frame.height, GL_BGRA_EXT, 4, frame.flipped);
 
-	// Set the row length, needed to be able to upload partial rows
-	CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.pitch / 4));
+    // Set the row length, needed to be able to upload partial rows
+    CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.pitch / 4));
 
-	for (auto& ti : textureList) {
-		CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
-		CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
-			ti.sourceH, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &frame.data[ti.dataOffset]));
-	}
+    for (auto &ti : textureList) {
+        CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
+        CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
+                                    ti.sourceH, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &frame.data[ti.dataOffset]));
+    }
 
-	CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
+    CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
 }
 
-void VideoOutGL::Render(int dx1, int dy1, int dx2, int dy2) {
-	CHECK_ERROR(glViewport(dx1, dy1, dx2, dy2));
-	CHECK_ERROR(glCallList(dl));
-	CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
-	CHECK_ERROR(glLoadIdentity());
+void VideoOutGL::Render(int dx1, int dy1, int dx2, int dy2)
+{
+    CHECK_ERROR(glViewport(dx1, dy1, dx2, dy2));
+    CHECK_ERROR(glCallList(dl));
+    CHECK_ERROR(glMatrixMode(GL_MODELVIEW));
+    CHECK_ERROR(glLoadIdentity());
 
 }
 
-VideoOutGL::~VideoOutGL() {
-	if (textureIdList.size() > 0) {
-		glDeleteTextures(textureIdList.size(), &textureIdList[0]);
-		glDeleteLists(dl, 1);
-	}
+VideoOutGL::~VideoOutGL()
+{
+    if (textureIdList.size() > 0) {
+        glDeleteTextures(textureIdList.size(), &textureIdList[0]);
+        glDeleteLists(dl, 1);
+    }
 }
diff --git a/src/video_out_gl.h b/src/video_out_gl.h
index 7d12f5dfd9bc9805ab5fa9102a6a345f05125631..9da2142811f4c165c6b015553407cb2d760a884e 100644
--- a/src/video_out_gl.h
+++ b/src/video_out_gl.h
@@ -28,55 +28,55 @@ struct VideoFrame;
 /// @class VideoOutGL
 /// @brief OpenGL based video renderer
 class VideoOutGL {
-	struct TextureInfo;
+    struct TextureInfo;
 
-	/// The maximum texture size supported by the user's graphics card
-	int maxTextureSize = 0;
-	/// Whether rectangular textures are supported by the user's graphics card
-	bool supportsRectangularTextures = false;
-	/// The internalformat to use
-	int internalFormat = 0;
+    /// The maximum texture size supported by the user's graphics card
+    int maxTextureSize = 0;
+    /// Whether rectangular textures are supported by the user's graphics card
+    bool supportsRectangularTextures = false;
+    /// The internalformat to use
+    int internalFormat = 0;
 
-	/// The frame height which the texture grid has been set up for
-	int frameWidth = 0;
-	/// The frame width which the texture grid has been set up for
-	int frameHeight = 0;
-	/// The frame format which the texture grid has been set up for
-	GLenum frameFormat = 0;
-	/// Whether the grid is set up for flipped video
-	bool frameFlipped = false;
-	/// List of OpenGL texture ids used in the grid
-	std::vector<GLuint> textureIdList;
-	/// List of precalculated texture display information
-	std::vector<TextureInfo> textureList;
-	/// OpenGL display list which draws the frames
-	GLuint dl = 0;
-	/// The total texture count
-	int textureCount = 0;
-	/// The number of rows of textures
-	int textureRows = 0;
-	/// The number of columns of textures
-	int textureCols = 0;
+    /// The frame height which the texture grid has been set up for
+    int frameWidth = 0;
+    /// The frame width which the texture grid has been set up for
+    int frameHeight = 0;
+    /// The frame format which the texture grid has been set up for
+    GLenum frameFormat = 0;
+    /// Whether the grid is set up for flipped video
+    bool frameFlipped = false;
+    /// List of OpenGL texture ids used in the grid
+    std::vector<GLuint> textureIdList;
+    /// List of precalculated texture display information
+    std::vector<TextureInfo> textureList;
+    /// OpenGL display list which draws the frames
+    GLuint dl = 0;
+    /// The total texture count
+    int textureCount = 0;
+    /// The number of rows of textures
+    int textureRows = 0;
+    /// The number of columns of textures
+    int textureCols = 0;
 
-	void DetectOpenGLCapabilities();
-	void InitTextures(int width, int height, GLenum format, int bpp, bool flipped);
+    void DetectOpenGLCapabilities();
+    void InitTextures(int width, int height, GLenum format, int bpp, bool flipped);
 
-	VideoOutGL(const VideoOutGL &) = delete;
-	VideoOutGL& operator=(const VideoOutGL&) = delete;
+    VideoOutGL(const VideoOutGL &) = delete;
+    VideoOutGL &operator=(const VideoOutGL &) = delete;
 public:
-	/// @brief Set the frame to be displayed when Render() is called
-	/// @param frame The frame to be displayed
-	void UploadFrameData(VideoFrame const& frame);
+    /// @brief Set the frame to be displayed when Render() is called
+    /// @param frame The frame to be displayed
+    void UploadFrameData(VideoFrame const &frame);
 
-	/// @brief Render a frame
-	/// @param x Bottom left x coordinate
-	/// @param y Bottom left y coordinate
-	/// @param width Width in pixels of viewport
-	/// @param height Height in pixels of viewport
-	void Render(int x, int y, int width, int height);
+    /// @brief Render a frame
+    /// @param x Bottom left x coordinate
+    /// @param y Bottom left y coordinate
+    /// @param width Width in pixels of viewport
+    /// @param height Height in pixels of viewport
+    void Render(int x, int y, int width, int height);
 
-	VideoOutGL();
-	~VideoOutGL();
+    VideoOutGL();
+    ~VideoOutGL();
 };
 
 /// Base class for all exceptions thrown by VideoOutGL
@@ -85,16 +85,16 @@ DEFINE_EXCEPTION(VideoOutException, agi::Exception);
 /// An OpenGL error occurred while uploading or displaying a frame
 class VideoOutRenderException final : public VideoOutException {
 public:
-	VideoOutRenderException(const char *func, int err)
-	: VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
-	{ }
+    VideoOutRenderException(const char *func, int err)
+        : VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
+    { }
 };
 
 /// An OpenGL error occurred while setting up the video display
 class VideoOutInitException final : public VideoOutException {
 public:
-	VideoOutInitException(const char *func, int err)
-	: VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
-	{ }
-	VideoOutInitException(const char *err) : VideoOutException(err) { }
+    VideoOutInitException(const char *func, int err)
+        : VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
+    { }
+    VideoOutInitException(const char *err) : VideoOutException(err) { }
 };
diff --git a/src/video_provider_avs.cpp b/src/video_provider_avs.cpp
index dabcdaa68d3668065f331826d8cac5d3f825061a..98993a6aad9e66c9614b5a7366849689b7b64db8 100644
--- a/src/video_provider_avs.cpp
+++ b/src/video_provider_avs.cpp
@@ -54,275 +54,281 @@
 
 namespace {
 class AvisynthVideoProvider: public VideoProvider {
-	AviSynthWrapper avs;
-	std::string decoder_name;
-	agi::vfr::Framerate fps;
-	std::vector<int> keyframes;
-	std::string warning;
-	std::string colorspace;
-	std::string real_colorspace;
-	bool has_audio = false;
-
-	AVSValue source_clip;
-	PClip RGB32Video;
-	VideoInfo vi;
-
-	AVSValue Open(agi::fs::path const& filename);
-	void Init(std::string const& matrix);
+    AviSynthWrapper avs;
+    std::string decoder_name;
+    agi::vfr::Framerate fps;
+    std::vector<int> keyframes;
+    std::string warning;
+    std::string colorspace;
+    std::string real_colorspace;
+    bool has_audio = false;
+
+    AVSValue source_clip;
+    PClip RGB32Video;
+    VideoInfo vi;
+
+    AVSValue Open(agi::fs::path const &filename);
+    void Init(std::string const &matrix);
 
 public:
-	AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
-
-	void GetFrame(int n, VideoFrame &frame) override;
-
-	void SetColorSpace(std::string const& matrix) override {
-		// Can't really do anything if this fails
-		try { Init(matrix); } catch (AvisynthError const&) { }
-	}
-
-	int GetFrameCount() const override             { return vi.num_frames; }
-	agi::vfr::Framerate GetFPS() const override    { return fps; }
-	int GetWidth() const override                  { return vi.width; }
-	int GetHeight() const override                 { return vi.height; }
-	double GetDAR() const override                 { return 0; }
-	std::vector<int> GetKeyFrames() const override { return keyframes; }
-	std::string GetWarning() const override        { return warning; }
-	std::string GetDecoderName() const override    { return decoder_name; }
-	std::string GetColorSpace() const override     { return colorspace; }
-	std::string GetRealColorSpace() const override { return real_colorspace; }
-	bool HasAudio() const override                 { return has_audio; }
+    AvisynthVideoProvider(agi::fs::path const &filename, std::string const &colormatrix);
+
+    void GetFrame(int n, VideoFrame &frame) override;
+
+    void SetColorSpace(std::string const &matrix) override {
+        // Can't really do anything if this fails
+        try { Init(matrix); }
+        catch (AvisynthError const &) { }
+    }
+
+    int GetFrameCount() const override             { return vi.num_frames; }
+    agi::vfr::Framerate GetFPS() const override    { return fps; }
+    int GetWidth() const override                  { return vi.width; }
+    int GetHeight() const override                 { return vi.height; }
+    double GetDAR() const override                 { return 0; }
+    std::vector<int> GetKeyFrames() const override { return keyframes; }
+    std::string GetWarning() const override        { return warning; }
+    std::string GetDecoderName() const override    { return decoder_name; }
+    std::string GetColorSpace() const override     { return colorspace; }
+    std::string GetRealColorSpace() const override { return real_colorspace; }
+    bool HasAudio() const override                 { return has_audio; }
 };
 
-AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) {
-	agi::acs::CheckFileRead(filename);
+AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const &filename, std::string const &colormatrix)
+{
+    agi::acs::CheckFileRead(filename);
 
-	std::lock_guard<std::mutex> lock(avs.GetMutex());
+    std::lock_guard<std::mutex> lock(avs.GetMutex());
 
 #ifdef _WIN32
-	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.c_str(), OF_SHARE_DENY_WRITE, 0);
-		if (hr) {
-			warning = "Unable to open AVI file for reading keyframes:\n";
-			switch (hr) {
-				case AVIERR_BADFORMAT:
-					warning += "The file is corrupted, incomplete or has an otherwise bad format.";
-					break;
-				case AVIERR_MEMORY:
-					warning += "The file could not be opened because of insufficient memory.";
-					break;
-				case AVIERR_FILEREAD:
-					warning += "An error occurred reading the file. There might be a problem with the storage media.";
-					break;
-				case AVIERR_FILEOPEN:
-					warning += "The file could not be opened. It might be in use by another application, or you do not have permission to access it.";
-					break;
-				case REGDB_E_CLASSNOTREG:
-					warning += "There is no handler installed for the file extension. This might indicate a fundamental problem in your Video for Windows installation, and can be caused by extremely stripped Windows installations.";
-					break;
-				default:
-					warning += "Unknown error.";
-					break;
-			}
-			goto file_exit;
-		}
-
-		PAVISTREAM ppavi;
-		if (hr = AVIFileGetStream(pfile, &ppavi, streamtypeVIDEO, 0)) {
-			warning = "Unable to open AVI video stream for reading keyframes:\n";
-			switch (hr) {
-				case AVIERR_NODATA:
-					warning += "The file does not contain a usable video stream.";
-					break;
-				case AVIERR_MEMORY:
-					warning += "Not enough memory.";
-					break;
-				default:
-					warning += "Unknown error.";
-					break;
-			}
-			goto file_release;
-		}
-
-		AVISTREAMINFO avis;
-		if (FAILED(AVIStreamInfo(ppavi,&avis,sizeof(avis)))) {
-			warning = "Unable to read keyframes from AVI file:\nCould not get stream information.";
-			goto stream_release;
-		}
-
-		for (size_t i = 0; i < avis.dwLength; i++) {
-			if (AVIStreamIsKeyFrame(ppavi, i))
-				keyframes.push_back(i);
-		}
-
-		// If every frame is a keyframe then just discard the keyframe data as it's useless
-		if (keyframes.size() == (size_t)avis.dwLength)
-			keyframes.clear();
-
-		// Clean up
+    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.c_str(), OF_SHARE_DENY_WRITE, 0);
+        if (hr) {
+            warning = "Unable to open AVI file for reading keyframes:\n";
+            switch (hr) {
+            case AVIERR_BADFORMAT:
+                warning += "The file is corrupted, incomplete or has an otherwise bad format.";
+                break;
+            case AVIERR_MEMORY:
+                warning += "The file could not be opened because of insufficient memory.";
+                break;
+            case AVIERR_FILEREAD:
+                warning += "An error occurred reading the file. There might be a problem with the storage media.";
+                break;
+            case AVIERR_FILEOPEN:
+                warning += "The file could not be opened. It might be in use by another application, or you do not have permission to access it.";
+                break;
+            case REGDB_E_CLASSNOTREG:
+                warning += "There is no handler installed for the file extension. This might indicate a fundamental problem in your Video for Windows installation, and can be caused by extremely stripped Windows installations.";
+                break;
+            default:
+                warning += "Unknown error.";
+                break;
+            }
+            goto file_exit;
+        }
+
+        PAVISTREAM ppavi;
+        if (hr = AVIFileGetStream(pfile, &ppavi, streamtypeVIDEO, 0)) {
+            warning = "Unable to open AVI video stream for reading keyframes:\n";
+            switch (hr) {
+            case AVIERR_NODATA:
+                warning += "The file does not contain a usable video stream.";
+                break;
+            case AVIERR_MEMORY:
+                warning += "Not enough memory.";
+                break;
+            default:
+                warning += "Unknown error.";
+                break;
+            }
+            goto file_release;
+        }
+
+        AVISTREAMINFO avis;
+        if (FAILED(AVIStreamInfo(ppavi, &avis, sizeof(avis)))) {
+            warning = "Unable to read keyframes from AVI file:\nCould not get stream information.";
+            goto stream_release;
+        }
+
+        for (size_t i = 0; i < avis.dwLength; i++) {
+            if (AVIStreamIsKeyFrame(ppavi, i))
+                keyframes.push_back(i);
+        }
+
+        // If every frame is a keyframe then just discard the keyframe data as it's useless
+        if (keyframes.size() == (size_t)avis.dwLength)
+            keyframes.clear();
+
+        // Clean up
 stream_release:
-		AVIStreamRelease(ppavi);
+        AVIStreamRelease(ppavi);
 file_release:
-		AVIFileRelease(pfile);
+        AVIFileRelease(pfile);
 file_exit:
-		AVIFileExit();
-	}
+        AVIFileExit();
+    }
 #endif
 
-	try {
-		source_clip = Open(filename);
-		Init(colormatrix);
-	}
-	catch (AvisynthError const& err) {
-		throw VideoOpenError("Avisynth error: " + std::string(err.msg));
-	}
+    try {
+        source_clip = Open(filename);
+        Init(colormatrix);
+    }
+    catch (AvisynthError const &err) {
+        throw VideoOpenError("Avisynth error: " + std::string(err.msg));
+    }
 }
 
-void AvisynthVideoProvider::Init(std::string const& colormatrix) {
-	auto script = source_clip;
-	vi = script.AsClip()->GetVideoInfo();
-	has_audio = vi.HasAudio();
-	if (vi.IsRGB())
-		real_colorspace = colorspace = "None";
-	else {
-		/// @todo maybe read ColorMatrix hints for d2v files?
-		AVSValue args[2] = { script, "Rec709" };
-		bool bt709 = vi.width > 1024 || vi.height >= 600;
-		if (colormatrix == "TV.601") {
-			args[1] = "Rec601";
-			colorspace = "TV.601";
-		}
-		else {
-			colorspace = "TV.709";
-		}
-		real_colorspace = bt709 ? "TV.709" : "TV.601";
-		const char *argnames[2] = { 0, "matrix" };
-		script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames);
-	}
-
-	RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
-	vi = RGB32Video->GetVideoInfo();
-	fps = (double)vi.fps_numerator / vi.fps_denominator;
+void AvisynthVideoProvider::Init(std::string const &colormatrix)
+{
+    auto script = source_clip;
+    vi = script.AsClip()->GetVideoInfo();
+    has_audio = vi.HasAudio();
+    if (vi.IsRGB())
+        real_colorspace = colorspace = "None";
+    else {
+        /// @todo maybe read ColorMatrix hints for d2v files?
+        AVSValue args[2] = { script, "Rec709" };
+        bool bt709 = vi.width > 1024 || vi.height >= 600;
+        if (colormatrix == "TV.601") {
+            args[1] = "Rec601";
+            colorspace = "TV.601";
+        }
+        else {
+            colorspace = "TV.709";
+        }
+        real_colorspace = bt709 ? "TV.709" : "TV.601";
+        const char *argnames[2] = { 0, "matrix" };
+        script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames);
+    }
+
+    RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
+    vi = RGB32Video->GetVideoInfo();
+    fps = (double)vi.fps_numerator / vi.fps_denominator;
 }
 
-AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
-	IScriptEnvironment *env = avs.GetEnv();
-	char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
-
-	// Avisynth file, just import it
-	if (agi::fs::HasExtension(filename, "avs")) {
-		LOG_I("avisynth/video") << "Opening .avs file with Import";
-		decoder_name = "Avisynth/Import";
-		return env->Invoke("Import", videoFilename);
-	}
-
-	// Open avi file with AviSource
-	if (agi::fs::HasExtension(filename, "avi")) {
-		LOG_I("avisynth/video") << "Opening .avi file with AviSource";
-		try {
-			const char *argnames[2] = { 0, "audio" };
-			AVSValue args[2] = { videoFilename, false };
-			decoder_name = "Avisynth/AviSource";
-			return env->Invoke("AviSource", AVSValue(args,2), argnames);
-		}
-		// On Failure, fallback to DSS
-		catch (AvisynthError &err) {
-			LOG_E("avisynth/video") << err.msg;
-			LOG_I("avisynth/video") << "Failed to open .avi file with AviSource, trying DirectShowSource";
-		}
-	}
-
-	// Open d2v with mpeg2dec3
-	if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) {
-		LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
-		auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename);
-		decoder_name = "Avisynth/Mpeg2Dec3_Mpeg2Source";
-
-		//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
-		if (env->FunctionExists("SetPlanarLegacyAlignment")) {
-			AVSValue args[2] = { script, true };
-			script = env->Invoke("SetPlanarLegacyAlignment", AVSValue(args,2));
-		}
-		return script;
-	}
-
-	// If that fails, try opening it with DGDecode
-	if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) {
-		LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source";
-		decoder_name = "DGDecode_Mpeg2Source";
-		return env->Invoke("Avisynth/Mpeg2Source", videoFilename);
-
-		//note that DGDecode will also have issues like if the version is too
-		// ancient but no sane person would use that anyway
-	}
-
-	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);
-		decoder_name = "Avisynth/Mpeg2Source";
-
-		//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
-		if (env->FunctionExists("SetPlanarLegacyAlignment"))
-			script = env->Invoke("SetPlanarLegacyAlignment", script);
-
-		return script;
-	}
-
-	// Try loading DirectShowSource2
-	if (!env->FunctionExists("dss2")) {
-		auto dss2path(config::path->Decode("?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
-	if (env->FunctionExists("dss2")) {
-		LOG_I("avisynth/video") << "Opening file with DSS2";
-		decoder_name = "Avisynth/DSS2";
-		return env->Invoke("DSS2", videoFilename);
-	}
-
-	// Try DirectShowSource
-	// Load DirectShowSource.dll from app dir if it exists
-	auto dsspath(config::path->Decode("?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")) {
-		const char *argnames[3] = { 0, "video", "audio" };
-		AVSValue args[3] = { videoFilename, true, false };
-		decoder_name = "Avisynth/DirectShowSource";
-		warning = "Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!";
-		LOG_I("avisynth/video") << "Opening file with DirectShowSource";
-		return env->Invoke("DirectShowSource", AVSValue(args,3), argnames);
-	}
-
-	// Failed to find a suitable function
-	LOG_E("avisynth/video") << "DSS function not found";
-	throw VideoNotSupported("No function suitable for opening the video found");
+AVSValue AvisynthVideoProvider::Open(agi::fs::path const &filename)
+{
+    IScriptEnvironment *env = avs.GetEnv();
+    char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
+
+    // Avisynth file, just import it
+    if (agi::fs::HasExtension(filename, "avs")) {
+        LOG_I("avisynth/video") << "Opening .avs file with Import";
+        decoder_name = "Avisynth/Import";
+        return env->Invoke("Import", videoFilename);
+    }
+
+    // Open avi file with AviSource
+    if (agi::fs::HasExtension(filename, "avi")) {
+        LOG_I("avisynth/video") << "Opening .avi file with AviSource";
+        try {
+            const char *argnames[2] = { 0, "audio" };
+            AVSValue args[2] = { videoFilename, false };
+            decoder_name = "Avisynth/AviSource";
+            return env->Invoke("AviSource", AVSValue(args, 2), argnames);
+        }
+        // On Failure, fallback to DSS
+        catch (AvisynthError &err) {
+            LOG_E("avisynth/video") << err.msg;
+            LOG_I("avisynth/video") << "Failed to open .avi file with AviSource, trying DirectShowSource";
+        }
+    }
+
+    // Open d2v with mpeg2dec3
+    if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) {
+        LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
+        auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename);
+        decoder_name = "Avisynth/Mpeg2Dec3_Mpeg2Source";
+
+        //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
+        if (env->FunctionExists("SetPlanarLegacyAlignment")) {
+            AVSValue args[2] = { script, true };
+            script = env->Invoke("SetPlanarLegacyAlignment", AVSValue(args, 2));
+        }
+        return script;
+    }
+
+    // If that fails, try opening it with DGDecode
+    if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) {
+        LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source";
+        decoder_name = "DGDecode_Mpeg2Source";
+        return env->Invoke("Avisynth/Mpeg2Source", videoFilename);
+
+        //note that DGDecode will also have issues like if the version is too
+        // ancient but no sane person would use that anyway
+    }
+
+    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);
+        decoder_name = "Avisynth/Mpeg2Source";
+
+        //if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
+        if (env->FunctionExists("SetPlanarLegacyAlignment"))
+            script = env->Invoke("SetPlanarLegacyAlignment", script);
+
+        return script;
+    }
+
+    // Try loading DirectShowSource2
+    if (!env->FunctionExists("dss2")) {
+        auto dss2path(config::path->Decode("?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
+    if (env->FunctionExists("dss2")) {
+        LOG_I("avisynth/video") << "Opening file with DSS2";
+        decoder_name = "Avisynth/DSS2";
+        return env->Invoke("DSS2", videoFilename);
+    }
+
+    // Try DirectShowSource
+    // Load DirectShowSource.dll from app dir if it exists
+    auto dsspath(config::path->Decode("?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")) {
+        const char *argnames[3] = { 0, "video", "audio" };
+        AVSValue args[3] = { videoFilename, true, false };
+        decoder_name = "Avisynth/DirectShowSource";
+        warning = "Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!";
+        LOG_I("avisynth/video") << "Opening file with DirectShowSource";
+        return env->Invoke("DirectShowSource", AVSValue(args, 3), argnames);
+    }
+
+    // Failed to find a suitable function
+    LOG_E("avisynth/video") << "DSS function not found";
+    throw VideoNotSupported("No function suitable for opening the video found");
 }
 
-void AvisynthVideoProvider::GetFrame(int n, VideoFrame &out) {
-	std::lock_guard<std::mutex> lock(avs.GetMutex());
-
-	auto frame = RGB32Video->GetFrame(n, avs.GetEnv());
-	auto ptr = frame->GetReadPtr();
-	out.data.assign(ptr, ptr + frame->GetPitch() * frame->GetHeight());
-	out.flipped = true;
-	out.height = frame->GetHeight();
-	out.width = frame->GetRowSize() / 4;
-	out.pitch = frame->GetPitch();
+void AvisynthVideoProvider::GetFrame(int n, VideoFrame &out)
+{
+    std::lock_guard<std::mutex> lock(avs.GetMutex());
+
+    auto frame = RGB32Video->GetFrame(n, avs.GetEnv());
+    auto ptr = frame->GetReadPtr();
+    out.data.assign(ptr, ptr + frame->GetPitch() * frame->GetHeight());
+    out.flipped = true;
+    out.height = frame->GetHeight();
+    out.width = frame->GetRowSize() / 4;
+    out.pitch = frame->GetPitch();
 }
 }
 
 namespace agi { class BackgroundRunner; }
-std::unique_ptr<VideoProvider> CreateAvisynthVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *) {
-	return agi::make_unique<AvisynthVideoProvider>(path, colormatrix);
+std::unique_ptr<VideoProvider> CreateAvisynthVideoProvider(agi::fs::path const &path, std::string const &colormatrix, agi::BackgroundRunner *)
+{
+    return agi::make_unique<AvisynthVideoProvider>(path, colormatrix);
 }
 #endif // HAVE_AVISYNTH
diff --git a/src/video_provider_cache.cpp b/src/video_provider_cache.cpp
index 4271279e2124a6f064ce63e67aa06c95d5f3a22a..c7839618dedfcf057b0c704549dfe79548d94092 100644
--- a/src/video_provider_cache.cpp
+++ b/src/video_provider_cache.cpp
@@ -26,79 +26,81 @@
 namespace {
 /// A video frame and its frame number
 struct CachedFrame {
-	VideoFrame frame;
-	int frame_number;
+    VideoFrame frame;
+    int frame_number;
 
-	CachedFrame(VideoFrame const& frame, int frame_number)
-	: frame(frame), frame_number(frame_number) { }
+    CachedFrame(VideoFrame const &frame, int frame_number)
+        : frame(frame), frame_number(frame_number) { }
 
-	CachedFrame(CachedFrame const&) = delete;
+    CachedFrame(CachedFrame const &) = delete;
 };
 
 /// @class VideoProviderCache
 /// @brief A wrapper around a video provider which provides LRU caching
 class VideoProviderCache final : public VideoProvider {
-	/// The source provider to get frames from
-	std::unique_ptr<VideoProvider> master;
+    /// The source provider to get frames from
+    std::unique_ptr<VideoProvider> master;
 
-	/// @brief Maximum size of the cache in bytes
-	///
-	/// Note that this is a soft limit. The cache stops allocating new frames
-	/// once it has exceeded the limit, but it never tries to shrink
-	const size_t max_cache_size = OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20; // convert MB to bytes
+    /// @brief Maximum size of the cache in bytes
+    ///
+    /// Note that this is a soft limit. The cache stops allocating new frames
+    /// once it has exceeded the limit, but it never tries to shrink
+    const size_t max_cache_size = OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20; // convert MB to bytes
 
-	/// Cache of video frames with the most recently used ones at the front
-	std::list<CachedFrame> cache;
+    /// Cache of video frames with the most recently used ones at the front
+    std::list<CachedFrame> cache;
 
 public:
-	VideoProviderCache(std::unique_ptr<VideoProvider> master) : master(std::move(master)) { }
-
-	void GetFrame(int n, VideoFrame &frame) override;
-
-	void SetColorSpace(std::string const& m) override {
-		cache.clear();
-		return master->SetColorSpace(m);
-	}
-
-	int GetFrameCount() const override             { return master->GetFrameCount(); }
-	int GetWidth() const override                  { return master->GetWidth(); }
-	int GetHeight() const override                 { return master->GetHeight(); }
-	double GetDAR() const override                 { return master->GetDAR(); }
-	agi::vfr::Framerate GetFPS() const override    { return master->GetFPS(); }
-	std::vector<int> GetKeyFrames() const override { return master->GetKeyFrames(); }
-	std::string GetWarning() const override        { return master->GetWarning(); }
-	std::string GetDecoderName() const override    { return master->GetDecoderName(); }
-	std::string GetColorSpace() const override     { return master->GetColorSpace(); }
-	std::string GetRealColorSpace() const override { return master->GetRealColorSpace(); }
-	bool ShouldSetVideoProperties() const override { return master->ShouldSetVideoProperties(); }
-	bool HasAudio() const override                 { return master->HasAudio(); }
+    VideoProviderCache(std::unique_ptr<VideoProvider> master) : master(std::move(master)) { }
+
+    void GetFrame(int n, VideoFrame &frame) override;
+
+    void SetColorSpace(std::string const &m) override {
+        cache.clear();
+        return master->SetColorSpace(m);
+    }
+
+    int GetFrameCount() const override             { return master->GetFrameCount(); }
+    int GetWidth() const override                  { return master->GetWidth(); }
+    int GetHeight() const override                 { return master->GetHeight(); }
+    double GetDAR() const override                 { return master->GetDAR(); }
+    agi::vfr::Framerate GetFPS() const override    { return master->GetFPS(); }
+    std::vector<int> GetKeyFrames() const override { return master->GetKeyFrames(); }
+    std::string GetWarning() const override        { return master->GetWarning(); }
+    std::string GetDecoderName() const override    { return master->GetDecoderName(); }
+    std::string GetColorSpace() const override     { return master->GetColorSpace(); }
+    std::string GetRealColorSpace() const override { return master->GetRealColorSpace(); }
+    bool ShouldSetVideoProperties() const override { return master->ShouldSetVideoProperties(); }
+    bool HasAudio() const override                 { return master->HasAudio(); }
 };
 
-void VideoProviderCache::GetFrame(int n, VideoFrame &out) {
-	size_t total_size = 0;
-
-	for (auto cur = cache.begin(); cur != cache.end(); ++cur) {
-		if (cur->frame_number == n) {
-			cache.splice(cache.begin(), cache, cur); // Move to front
-			out = cache.front().frame;
-			return;
-		}
-
-		total_size += cur->frame.data.size();
-	}
-
-	master->GetFrame(n, out);
-
-	if (total_size >= max_cache_size) {
-		cache.splice(cache.begin(), cache, --cache.end()); // Move last to front
-		cache.front().frame_number = n;
-		cache.front().frame = out;
-	}
-	else
-		cache.emplace_front(out, n);
+void VideoProviderCache::GetFrame(int n, VideoFrame &out)
+{
+    size_t total_size = 0;
+
+    for (auto cur = cache.begin(); cur != cache.end(); ++cur) {
+        if (cur->frame_number == n) {
+            cache.splice(cache.begin(), cache, cur); // Move to front
+            out = cache.front().frame;
+            return;
+        }
+
+        total_size += cur->frame.data.size();
+    }
+
+    master->GetFrame(n, out);
+
+    if (total_size >= max_cache_size) {
+        cache.splice(cache.begin(), cache, --cache.end()); // Move last to front
+        cache.front().frame_number = n;
+        cache.front().frame = out;
+    }
+    else
+        cache.emplace_front(out, n);
 }
 }
 
-std::unique_ptr<VideoProvider> CreateCacheVideoProvider(std::unique_ptr<VideoProvider> parent) {
-	return agi::make_unique<VideoProviderCache>(std::move(parent));
+std::unique_ptr<VideoProvider> CreateCacheVideoProvider(std::unique_ptr<VideoProvider> parent)
+{
+    return agi::make_unique<VideoProviderCache>(std::move(parent));
 }
diff --git a/src/video_provider_dummy.cpp b/src/video_provider_dummy.cpp
index da3b4a0a48835fb5de95f6159c3255a016e9f9ed..12d71ca744faa792dabc6572f73e5497ad1f173f 100644
--- a/src/video_provider_dummy.cpp
+++ b/src/video_provider_dummy.cpp
@@ -52,82 +52,85 @@
 #endif
 
 DummyVideoProvider::DummyVideoProvider(double fps, int frames, int width, int height, agi::Color colour, bool pattern)
-: framecount(frames)
-, fps(fps)
-, width(width)
-, height(height)
+    : framecount(frames)
+    , fps(fps)
+    , width(width)
+    , height(height)
 {
-	data.resize(width * height * 4);
-
-	auto red = colour.r;
-	auto green = colour.g;
-	auto blue = colour.b;
-
-	using namespace boost::gil;
-	auto dst = interleaved_view(width, height, (bgra8_pixel_t*)data.data(), 4 * width);
-
-	bgra8_pixel_t colors[2] = {
-		bgra8_pixel_t(blue, green, red, 0),
-		bgra8_pixel_t(blue, green, red, 0)
-	};
-
-	if (pattern) {
-		// Generate light version
-		unsigned char h, s, l;
-		rgb_to_hsl(red, blue, green, &h, &s, &l);
-		l += 24;
-		if (l < 24) l -= 48;
-		hsl_to_rgb(h, s, l, &red, &blue, &green);
-		colors[1] = bgra8_pixel_t(blue, green, red, 0);
-
-		// Divide into a 8x8 grid and use light colours when row % 2 != col % 2
-		auto out = dst.begin();
-		for (int y = 0; y < height; ++y)
-			for (int x = 0; x < width; ++x)
-				*out++ = colors[((y / 8) & 1) != ((x / 8) & 1)];
-	}
-	else {
-		fill_pixels(dst, colors[0]);
-	}
+    data.resize(width * height * 4);
+
+    auto red = colour.r;
+    auto green = colour.g;
+    auto blue = colour.b;
+
+    using namespace boost::gil;
+    auto dst = interleaved_view(width, height, (bgra8_pixel_t *)data.data(), 4 * width);
+
+    bgra8_pixel_t colors[2] = {
+        bgra8_pixel_t(blue, green, red, 0),
+        bgra8_pixel_t(blue, green, red, 0)
+    };
+
+    if (pattern) {
+        // Generate light version
+        unsigned char h, s, l;
+        rgb_to_hsl(red, blue, green, &h, &s, &l);
+        l += 24;
+        if (l < 24) l -= 48;
+        hsl_to_rgb(h, s, l, &red, &blue, &green);
+        colors[1] = bgra8_pixel_t(blue, green, red, 0);
+
+        // Divide into a 8x8 grid and use light colours when row % 2 != col % 2
+        auto out = dst.begin();
+        for (int y = 0; y < height; ++y)
+            for (int x = 0; x < width; ++x)
+                *out++ = colors[((y / 8) & 1) != ((x / 8) & 1)];
+    }
+    else {
+        fill_pixels(dst, colors[0]);
+    }
 }
 
-std::string DummyVideoProvider::MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern) {
-	return agi::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" : ""));
+std::string DummyVideoProvider::MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern)
+{
+    return agi::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" : ""));
 }
 
-void DummyVideoProvider::GetFrame(int, VideoFrame &frame) {
-	frame.data    = data;
-	frame.width   = width;
-	frame.height  = height;
-	frame.pitch   = width * 4;
-	frame.flipped = false;
+void DummyVideoProvider::GetFrame(int, VideoFrame &frame)
+{
+    frame.data    = data;
+    frame.width   = width;
+    frame.height  = height;
+    frame.pitch   = width * 4;
+    frame.flipped = false;
 }
 
 namespace agi { class BackgroundRunner; }
-std::unique_ptr<VideoProvider> CreateDummyVideoProvider(agi::fs::path const& filename, std::string const&, agi::BackgroundRunner *) {
-	if (!boost::starts_with(filename.string(), "?dummy"))
-		return {};
-
-	std::vector<std::string> toks;
-	auto const& fields = filename.string().substr(7);
-	agi::Split(toks, fields, ':');
-	if (toks.size() != 8)
-		throw VideoOpenError("Too few fields in dummy video parameter list");
-
-	size_t i = 0;
-	double fps;
-	int frames, width, height, red, green, blue;
-
-	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 = toks[i] == "c";
-
-	return agi::make_unique<DummyVideoProvider>(fps, frames, width, height, agi::Color(red, green, blue), pattern);
+std::unique_ptr<VideoProvider> CreateDummyVideoProvider(agi::fs::path const &filename, std::string const &, agi::BackgroundRunner *)
+{
+    if (!boost::starts_with(filename.string(), "?dummy"))
+        return {};
+
+    std::vector<std::string> toks;
+    auto const &fields = filename.string().substr(7);
+    agi::Split(toks, fields, ':');
+    if (toks.size() != 8)
+        throw VideoOpenError("Too few fields in dummy video parameter list");
+
+    size_t i = 0;
+    double fps;
+    int frames, width, height, red, green, blue;
+
+    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 = toks[i] == "c";
+
+    return agi::make_unique<DummyVideoProvider>(fps, frames, width, height, agi::Color(red, green, blue), pattern);
 }
diff --git a/src/video_provider_dummy.h b/src/video_provider_dummy.h
index bf4841e79c3ff15b30f71d677a2ed7a8bc9eeca2..5a4696e8a61eb80c4da6bb9a89082ea67037afd7 100644
--- a/src/video_provider_dummy.h
+++ b/src/video_provider_dummy.h
@@ -42,38 +42,38 @@ namespace agi { struct Color; }
 /// This simply returns a single constant frame, which can either be a flat
 /// color or a checkerboard pattern
 class DummyVideoProvider final : public VideoProvider {
-	int framecount;          ///< Length of the dummy video in frames
-	agi::vfr::Framerate fps; ///< Frame rate to use
-	int width;               ///< Width in pixels
-	int height;              ///< Height in pixels
+    int framecount;          ///< Length of the dummy video in frames
+    agi::vfr::Framerate fps; ///< Frame rate to use
+    int width;               ///< Width in pixels
+    int height;              ///< Height in pixels
 
-	/// The data for the image returned for all frames
-	std::vector<unsigned char> data;
+    /// The data for the image returned for all frames
+    std::vector<unsigned char> data;
 
 public:
-	/// Create a dummy video from separate parameters
-	/// @param fps Frame rate of the dummy video
-	/// @param frames Length in frames of the dummy video
-	/// @param width Width in pixels of the dummy video
-	/// @param height Height in pixels of the dummy video
-	/// @param colour Primary colour of the dummy video
-	/// @param pattern Use a checkerboard pattern rather than a solid colour
-	DummyVideoProvider(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
+    /// Create a dummy video from separate parameters
+    /// @param fps Frame rate of the dummy video
+    /// @param frames Length in frames of the dummy video
+    /// @param width Width in pixels of the dummy video
+    /// @param height Height in pixels of the dummy video
+    /// @param colour Primary colour of the dummy video
+    /// @param pattern Use a checkerboard pattern rather than a solid colour
+    DummyVideoProvider(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
 
-	/// Make a fake filename which when passed to the constructor taking a
-	/// string will result in a video with the given parameters
-	static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
+    /// Make a fake filename which when passed to the constructor taking a
+    /// string will result in a video with the given parameters
+    static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
 
-	void GetFrame(int n, VideoFrame &frame) override;
-	void SetColorSpace(std::string const&) override { }
+    void GetFrame(int n, VideoFrame &frame) override;
+    void SetColorSpace(std::string const &) override { }
 
-	int GetFrameCount()             const override { return framecount; }
-	int GetWidth()                  const override { return width; }
-	int GetHeight()                 const override { return height; }
-	double GetDAR()                 const override { return 0; }
-	agi::vfr::Framerate GetFPS()    const override { return fps; }
-	std::vector<int> GetKeyFrames() const override { return {}; }
-	std::string GetColorSpace()     const override { return "None"; }
-	std::string GetDecoderName()    const override { return "Dummy Video Provider"; }
-	bool ShouldSetVideoProperties() const override { return false; }
+    int GetFrameCount()             const override { return framecount; }
+    int GetWidth()                  const override { return width; }
+    int GetHeight()                 const override { return height; }
+    double GetDAR()                 const override { return 0; }
+    agi::vfr::Framerate GetFPS()    const override { return fps; }
+    std::vector<int> GetKeyFrames() const override { return {}; }
+    std::string GetColorSpace()     const override { return "None"; }
+    std::string GetDecoderName()    const override { return "Dummy Video Provider"; }
+    bool ShouldSetVideoProperties() const override { return false; }
 };
diff --git a/src/video_provider_ffmpegsource.cpp b/src/video_provider_ffmpegsource.cpp
index 0b4853c216e6d0d382f507f5b1a9b5630a1ad7f6..2193c788d1c75e6c6f30aad62215db64d5dc3b0d 100644
--- a/src/video_provider_ffmpegsource.cpp
+++ b/src/video_provider_ffmpegsource.cpp
@@ -45,276 +45,281 @@
 
 namespace {
 typedef enum AGI_ColorSpaces {
-	AGI_CS_RGB = 0,
-	AGI_CS_BT709 = 1,
-	AGI_CS_UNSPECIFIED = 2,
-	AGI_CS_FCC = 4,
-	AGI_CS_BT470BG = 5,
-	AGI_CS_SMPTE170M = 6,
-	AGI_CS_SMPTE240M = 7,
-	AGI_CS_YCOCG = 8,
-	AGI_CS_BT2020_NCL = 9,
-	AGI_CS_BT2020_CL = 10,
-	AGI_CS_SMPTE2085 = 11,
-	AGI_CS_CHROMATICITY_DERIVED_NCL = 12,
-	AGI_CS_CHROMATICITY_DERIVED_CL = 13,
-	AGI_CS_ICTCP = 14
+    AGI_CS_RGB = 0,
+    AGI_CS_BT709 = 1,
+    AGI_CS_UNSPECIFIED = 2,
+    AGI_CS_FCC = 4,
+    AGI_CS_BT470BG = 5,
+    AGI_CS_SMPTE170M = 6,
+    AGI_CS_SMPTE240M = 7,
+    AGI_CS_YCOCG = 8,
+    AGI_CS_BT2020_NCL = 9,
+    AGI_CS_BT2020_CL = 10,
+    AGI_CS_SMPTE2085 = 11,
+    AGI_CS_CHROMATICITY_DERIVED_NCL = 12,
+    AGI_CS_CHROMATICITY_DERIVED_CL = 13,
+    AGI_CS_ICTCP = 14
 } AGI_ColorSpaces;
 
 /// @class FFmpegSourceVideoProvider
 /// @brief Implements video loading through the FFMS library.
 class FFmpegSourceVideoProvider final : public VideoProvider, FFmpegSourceProvider {
-	/// video source object
-	agi::scoped_holder<FFMS_VideoSource*, void (FFMS_CC*)(FFMS_VideoSource*)> VideoSource;
-	const FFMS_VideoProperties *VideoInfo = nullptr; ///< video properties
-
-	int Width = -1;                 ///< width in pixels
-	int Height = -1;                ///< height in pixels
-	int CS = -1;                    ///< Reported colorspace of first frame
-	int CR = -1;                    ///< Reported colorrange of first frame
-	double DAR;                     ///< display aspect ratio
-	std::vector<int> KeyFramesList; ///< list of keyframes
-	agi::vfr::Framerate Timecodes;  ///< vfr object
-	std::string ColorSpace;         ///< Colorspace name
-	std::string RealColorSpace;     ///< Colorspace name
-
-	char FFMSErrMsg[1024];          ///< FFMS error message
-	FFMS_ErrorInfo ErrInfo;         ///< FFMS error codes/messages
-	bool has_audio = false;
-
-	void LoadVideo(agi::fs::path const& filename, std::string const& colormatrix);
+    /// video source object
+    agi::scoped_holder<FFMS_VideoSource *, void (FFMS_CC *)(FFMS_VideoSource *)> VideoSource;
+    const FFMS_VideoProperties *VideoInfo = nullptr; ///< video properties
+
+    int Width = -1;                 ///< width in pixels
+    int Height = -1;                ///< height in pixels
+    int CS = -1;                    ///< Reported colorspace of first frame
+    int CR = -1;                    ///< Reported colorrange of first frame
+    double DAR;                     ///< display aspect ratio
+    std::vector<int> KeyFramesList; ///< list of keyframes
+    agi::vfr::Framerate Timecodes;  ///< vfr object
+    std::string ColorSpace;         ///< Colorspace name
+    std::string RealColorSpace;     ///< Colorspace name
+
+    char FFMSErrMsg[1024];          ///< FFMS error message
+    FFMS_ErrorInfo ErrInfo;         ///< FFMS error codes/messages
+    bool has_audio = false;
+
+    void LoadVideo(agi::fs::path const &filename, std::string const &colormatrix);
 
 public:
-	FFmpegSourceVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br);
-
-	void GetFrame(int n, VideoFrame &out) override;
-
-	void SetColorSpace(std::string const& matrix) override {
-		if (matrix == ColorSpace) return;
-		if (matrix == RealColorSpace)
-			FFMS_SetInputFormatV(VideoSource, CS, CR, FFMS_GetPixFmt(""), nullptr);
-		else if (matrix == "TV.601")
-			FFMS_SetInputFormatV(VideoSource, AGI_CS_BT470BG, CR, FFMS_GetPixFmt(""), nullptr);
-		else
-			return;
-		ColorSpace = matrix;
-	}
-
-	int GetFrameCount() const override             { return VideoInfo->NumFrames; }
-	int GetWidth() const override                  { return Width; }
-	int GetHeight() const override                 { return Height; }
-	double GetDAR() const override                 { return DAR; }
-	agi::vfr::Framerate GetFPS() const override    { return Timecodes; }
-	std::string GetColorSpace() const override     { return ColorSpace; }
-	std::string GetRealColorSpace() const override { return RealColorSpace; }
-	std::vector<int> GetKeyFrames() const override { return KeyFramesList; };
-	std::string GetDecoderName() const override    { return "FFmpegSource"; }
-	bool WantsCaching() const override             { return true; }
-	bool HasAudio() const override                 { return has_audio; }
+    FFmpegSourceVideoProvider(agi::fs::path const &filename, std::string const &colormatrix, agi::BackgroundRunner *br);
+
+    void GetFrame(int n, VideoFrame &out) override;
+
+    void SetColorSpace(std::string const &matrix) override {
+        if (matrix == ColorSpace) return;
+        if (matrix == RealColorSpace)
+            FFMS_SetInputFormatV(VideoSource, CS, CR, FFMS_GetPixFmt(""), nullptr);
+        else if (matrix == "TV.601")
+            FFMS_SetInputFormatV(VideoSource, AGI_CS_BT470BG, CR, FFMS_GetPixFmt(""), nullptr);
+        else
+            return;
+        ColorSpace = matrix;
+    }
+
+    int GetFrameCount() const override             { return VideoInfo->NumFrames; }
+    int GetWidth() const override                  { return Width; }
+    int GetHeight() const override                 { return Height; }
+    double GetDAR() const override                 { return DAR; }
+    agi::vfr::Framerate GetFPS() const override    { return Timecodes; }
+    std::string GetColorSpace() const override     { return ColorSpace; }
+    std::string GetRealColorSpace() const override { return RealColorSpace; }
+    std::vector<int> GetKeyFrames() const override { return KeyFramesList; };
+    std::string GetDecoderName() const override    { return "FFmpegSource"; }
+    bool WantsCaching() const override             { return true; }
+    bool HasAudio() const override                 { return has_audio; }
 };
 
-std::string colormatrix_description(int cs, int cr) {
-	// Assuming TV for unspecified
-	std::string str = cr == FFMS_CR_JPEG ? "PC" : "TV";
-
-	switch (cs) {
-		case AGI_CS_RGB:
-			return "None";
-		case AGI_CS_BT709:
-			return str + ".709";
-		case AGI_CS_FCC:
-			return str + ".FCC";
-		case AGI_CS_BT470BG:
-		case AGI_CS_SMPTE170M:
-			return str + ".601";
-		case AGI_CS_SMPTE240M:
-			return str + ".240M";
-		default:
-			throw VideoOpenError("Unknown video color space");
-	}
+std::string colormatrix_description(int cs, int cr)
+{
+    // Assuming TV for unspecified
+    std::string str = cr == FFMS_CR_JPEG ? "PC" : "TV";
+
+    switch (cs) {
+    case AGI_CS_RGB:
+        return "None";
+    case AGI_CS_BT709:
+        return str + ".709";
+    case AGI_CS_FCC:
+        return str + ".FCC";
+    case AGI_CS_BT470BG:
+    case AGI_CS_SMPTE170M:
+        return str + ".601";
+    case AGI_CS_SMPTE240M:
+        return str + ".240M";
+    default:
+        throw VideoOpenError("Unknown video color space");
+    }
 }
 
-FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) try
+FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const &filename, std::string const &colormatrix, agi::BackgroundRunner *br) try
 : FFmpegSourceProvider(br)
-, VideoSource(nullptr, FFMS_DestroyVideoSource)
+    , VideoSource(nullptr, FFMS_DestroyVideoSource)
 {
-	ErrInfo.Buffer		= FFMSErrMsg;
-	ErrInfo.BufferSize	= sizeof(FFMSErrMsg);
-	ErrInfo.ErrorType	= FFMS_ERROR_SUCCESS;
-	ErrInfo.SubType		= FFMS_ERROR_SUCCESS;
+    ErrInfo.Buffer		= FFMSErrMsg;
+    ErrInfo.BufferSize	= sizeof(FFMSErrMsg);
+    ErrInfo.ErrorType	= FFMS_ERROR_SUCCESS;
+    ErrInfo.SubType		= FFMS_ERROR_SUCCESS;
 
-	SetLogLevel();
+    SetLogLevel();
 
-	LoadVideo(filename, colormatrix);
+    LoadVideo(filename, colormatrix);
 }
-catch (agi::EnvironmentError const& err) {
-	throw VideoOpenError(err.GetMessage());
+catch (agi::EnvironmentError const &err)
+{
+    throw VideoOpenError(err.GetMessage());
 }
 
-void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::string const& colormatrix) {
-	FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
-	if (!Indexer) {
-		if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
-			throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer));
-		else
-			throw VideoNotSupported(ErrInfo.Buffer);
-	}
-
-	std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO);
-	if (TrackList.size() <= 0)
-		throw VideoNotSupported("no video tracks found");
-
-	int TrackNumber = -1;
-	if (TrackList.size() > 1) {
-		auto Selection = AskForTrackSelection(TrackList, FFMS_TYPE_VIDEO);
-		if (Selection == TrackSelection::None)
-			throw agi::UserCancelException("video loading cancelled by user");
-		TrackNumber = static_cast<int>(Selection);
-	}
-
-	// generate a name for the cache file
-	auto CacheName = GetCacheFilename(filename);
-
-	// try to read index
-	agi::scoped_holder<FFMS_Index*, void (FFMS_CC*)(FFMS_Index*)>
-		Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
-
-	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
-	// technically this isn't really needed since all video tracks should always be indexed,
-	// but a bit of sanity checking never hurt anyone
-	if (Index && TrackNumber >= 0) {
-		FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
-		if (FFMS_GetNumFrames(TempTrackData) <= 0)
-			Index = nullptr;
-	}
-
-	// moment of truth
-	if (!Index) {
-		auto TrackMask = TrackSelection::None;
-		if (OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool() || OPT_GET("Video/Open Audio")->GetBool())
-			TrackMask = TrackSelection::All;
-		Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode());
-	}
-	else {
-		FFMS_CancelIndexing(Indexer);
-	}
-
-	// update access time of index file so it won't get cleaned away
-	agi::fs::Touch(CacheName);
-
-	// we have now read the index and may proceed with cleaning the index cache
-	CleanCache();
-
-	// track number still not set?
-	if (TrackNumber < 0) {
-		// just grab the first track
-		TrackNumber = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, &ErrInfo);
-		if (TrackNumber < 0)
-			throw VideoNotSupported(std::string("Couldn't find any video tracks: ") + ErrInfo.Buffer);
-	}
-
-	// Check if there's an audio track
-	has_audio = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, nullptr) != -1;
-
-	// set thread count
-	int Threads = OPT_GET("Provider/Video/FFmpegSource/Decoding Threads")->GetInt();
-
-	// set seekmode
-	// TODO: give this its own option?
-	int SeekMode;
-	if (OPT_GET("Provider/Video/FFmpegSource/Unsafe Seeking")->GetBool())
-		SeekMode = FFMS_SEEK_UNSAFE;
-	else
-		SeekMode = FFMS_SEEK_NORMAL;
-
-	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);
-
-	// load video properties
-	VideoInfo = FFMS_GetVideoProperties(VideoSource);
-
-	const FFMS_Frame *TempFrame = FFMS_GetFrame(VideoSource, 0, &ErrInfo);
-	if (!TempFrame)
-		throw VideoOpenError(std::string("Failed to decode first frame: ") + ErrInfo.Buffer);
-
-	Width  = TempFrame->EncodedWidth;
-	Height = TempFrame->EncodedHeight;
-	if (VideoInfo->SARDen > 0 && VideoInfo->SARNum > 0)
-		DAR = double(Width) * VideoInfo->SARNum / ((double)Height * VideoInfo->SARDen);
-	else
-		DAR = double(Width) / Height;
-
-	int VideoCS = CS = TempFrame->ColorSpace;
-	CR = TempFrame->ColorRange;
-
-	if (CS == AGI_CS_UNSPECIFIED)
-		CS = Width > 1024 || Height >= 600 ? AGI_CS_BT709 : AGI_CS_BT470BG;
-	RealColorSpace = ColorSpace = colormatrix_description(CS, CR);
-
-	if (CS != AGI_CS_RGB && CS != AGI_CS_BT470BG && ColorSpace != colormatrix && colormatrix == "TV.601") {
-		CS = AGI_CS_BT470BG;
-		ColorSpace = colormatrix_description(CS, CR);
-	}
-
-	if (CS != VideoCS) {
-		if (FFMS_SetInputFormatV(VideoSource, CS, CR, FFMS_GetPixFmt(""), &ErrInfo))
-			throw VideoOpenError(std::string("Failed to set input format: ") + ErrInfo.Buffer);
-	}
-
-	const int TargetFormat[] = { FFMS_GetPixFmt("bgra"), -1 };
-	if (FFMS_SetOutputFormatV2(VideoSource, TargetFormat, Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo))
-		throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer);
-
-	// get frame info data
-	FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
-	if (FrameData == nullptr)
-		throw VideoOpenError("failed to get frame data");
-	const FFMS_TrackTimeBase *TimeBase = FFMS_GetTimeBase(FrameData);
-	if (TimeBase == nullptr)
-		throw VideoOpenError("failed to get track time base");
-
-	// build list of keyframes and timecodes
-	std::vector<int> TimecodesVector;
-	for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; 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)
-			KeyFramesList.push_back(CurFrameNum);
-
-		// calculate timestamp and add to timecodes vector
-		int Timestamp = (int)((CurFrameData->PTS * TimeBase->Num) / TimeBase->Den);
-		TimecodesVector.push_back(Timestamp);
-	}
-	if (TimecodesVector.size() < 2)
-		Timecodes = 25.0;
-	else
-		Timecodes = agi::vfr::Framerate(TimecodesVector);
+void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const &filename, std::string const &colormatrix)
+{
+    FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
+    if (!Indexer) {
+        if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
+            throw agi::fs::FileNotFound(std::string(ErrInfo.Buffer));
+        else
+            throw VideoNotSupported(ErrInfo.Buffer);
+    }
+
+    std::map<int, std::string> TrackList = GetTracksOfType(Indexer, FFMS_TYPE_VIDEO);
+    if (TrackList.size() <= 0)
+        throw VideoNotSupported("no video tracks found");
+
+    int TrackNumber = -1;
+    if (TrackList.size() > 1) {
+        auto Selection = AskForTrackSelection(TrackList, FFMS_TYPE_VIDEO);
+        if (Selection == TrackSelection::None)
+            throw agi::UserCancelException("video loading cancelled by user");
+        TrackNumber = static_cast<int>(Selection);
+    }
+
+    // generate a name for the cache file
+    auto CacheName = GetCacheFilename(filename);
+
+    // try to read index
+    agi::scoped_holder<FFMS_Index *, void (FFMS_CC *)(FFMS_Index *)>
+    Index(FFMS_ReadIndex(CacheName.string().c_str(), &ErrInfo), FFMS_DestroyIndex);
+
+    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
+    // technically this isn't really needed since all video tracks should always be indexed,
+    // but a bit of sanity checking never hurt anyone
+    if (Index && TrackNumber >= 0) {
+        FFMS_Track *TempTrackData = FFMS_GetTrackFromIndex(Index, TrackNumber);
+        if (FFMS_GetNumFrames(TempTrackData) <= 0)
+            Index = nullptr;
+    }
+
+    // moment of truth
+    if (!Index) {
+        auto TrackMask = TrackSelection::None;
+        if (OPT_GET("Provider/FFmpegSource/Index All Tracks")->GetBool() || OPT_GET("Video/Open Audio")->GetBool())
+            TrackMask = TrackSelection::All;
+        Index = DoIndexing(Indexer, CacheName, TrackMask, GetErrorHandlingMode());
+    }
+    else {
+        FFMS_CancelIndexing(Indexer);
+    }
+
+    // update access time of index file so it won't get cleaned away
+    agi::fs::Touch(CacheName);
+
+    // we have now read the index and may proceed with cleaning the index cache
+    CleanCache();
+
+    // track number still not set?
+    if (TrackNumber < 0) {
+        // just grab the first track
+        TrackNumber = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, &ErrInfo);
+        if (TrackNumber < 0)
+            throw VideoNotSupported(std::string("Couldn't find any video tracks: ") + ErrInfo.Buffer);
+    }
+
+    // Check if there's an audio track
+    has_audio = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, nullptr) != -1;
+
+    // set thread count
+    int Threads = OPT_GET("Provider/Video/FFmpegSource/Decoding Threads")->GetInt();
+
+    // set seekmode
+    // TODO: give this its own option?
+    int SeekMode;
+    if (OPT_GET("Provider/Video/FFmpegSource/Unsafe Seeking")->GetBool())
+        SeekMode = FFMS_SEEK_UNSAFE;
+    else
+        SeekMode = FFMS_SEEK_NORMAL;
+
+    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);
+
+    // load video properties
+    VideoInfo = FFMS_GetVideoProperties(VideoSource);
+
+    const FFMS_Frame *TempFrame = FFMS_GetFrame(VideoSource, 0, &ErrInfo);
+    if (!TempFrame)
+        throw VideoOpenError(std::string("Failed to decode first frame: ") + ErrInfo.Buffer);
+
+    Width  = TempFrame->EncodedWidth;
+    Height = TempFrame->EncodedHeight;
+    if (VideoInfo->SARDen > 0 && VideoInfo->SARNum > 0)
+        DAR = double(Width) * VideoInfo->SARNum / ((double)Height * VideoInfo->SARDen);
+    else
+        DAR = double(Width) / Height;
+
+    int VideoCS = CS = TempFrame->ColorSpace;
+    CR = TempFrame->ColorRange;
+
+    if (CS == AGI_CS_UNSPECIFIED)
+        CS = Width > 1024 || Height >= 600 ? AGI_CS_BT709 : AGI_CS_BT470BG;
+    RealColorSpace = ColorSpace = colormatrix_description(CS, CR);
+
+    if (CS != AGI_CS_RGB && CS != AGI_CS_BT470BG && ColorSpace != colormatrix && colormatrix == "TV.601") {
+        CS = AGI_CS_BT470BG;
+        ColorSpace = colormatrix_description(CS, CR);
+    }
+
+    if (CS != VideoCS) {
+        if (FFMS_SetInputFormatV(VideoSource, CS, CR, FFMS_GetPixFmt(""), &ErrInfo))
+            throw VideoOpenError(std::string("Failed to set input format: ") + ErrInfo.Buffer);
+    }
+
+    const int TargetFormat[] = { FFMS_GetPixFmt("bgra"), -1 };
+    if (FFMS_SetOutputFormatV2(VideoSource, TargetFormat, Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo))
+        throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer);
+
+    // get frame info data
+    FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
+    if (FrameData == nullptr)
+        throw VideoOpenError("failed to get frame data");
+    const FFMS_TrackTimeBase *TimeBase = FFMS_GetTimeBase(FrameData);
+    if (TimeBase == nullptr)
+        throw VideoOpenError("failed to get track time base");
+
+    // build list of keyframes and timecodes
+    std::vector<int> TimecodesVector;
+    for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; 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)
+            KeyFramesList.push_back(CurFrameNum);
+
+        // calculate timestamp and add to timecodes vector
+        int Timestamp = (int)((CurFrameData->PTS * TimeBase->Num) / TimeBase->Den);
+        TimecodesVector.push_back(Timestamp);
+    }
+    if (TimecodesVector.size() < 2)
+        Timecodes = 25.0;
+    else
+        Timecodes = agi::vfr::Framerate(TimecodesVector);
 }
 
-void FFmpegSourceVideoProvider::GetFrame(int n, VideoFrame &out) {
-	n = mid(0, n, GetFrameCount() - 1);
+void FFmpegSourceVideoProvider::GetFrame(int n, VideoFrame &out)
+{
+    n = mid(0, n, GetFrameCount() - 1);
 
-	auto frame = FFMS_GetFrame(VideoSource, n, &ErrInfo);
-	if (!frame)
-		throw VideoDecodeError(std::string("Failed to retrieve frame: ") +  ErrInfo.Buffer);
+    auto frame = FFMS_GetFrame(VideoSource, n, &ErrInfo);
+    if (!frame)
+        throw VideoDecodeError(std::string("Failed to retrieve frame: ") +  ErrInfo.Buffer);
 
-	out.data.assign(frame->Data[0], frame->Data[0] + frame->Linesize[0] * Height);
-	out.flipped = false;
-	out.width = Width;
-	out.height = Height;
-	out.pitch = frame->Linesize[0];
+    out.data.assign(frame->Data[0], frame->Data[0] + frame->Linesize[0] * Height);
+    out.flipped = false;
+    out.width = Width;
+    out.height = Height;
+    out.pitch = frame->Linesize[0];
 }
 }
 
-std::unique_ptr<VideoProvider> CreateFFmpegSourceVideoProvider(agi::fs::path const& path, std::string const& colormatrix, agi::BackgroundRunner *br) {
-	return agi::make_unique<FFmpegSourceVideoProvider>(path, colormatrix, br);
+std::unique_ptr<VideoProvider> CreateFFmpegSourceVideoProvider(agi::fs::path const &path, std::string const &colormatrix, agi::BackgroundRunner *br)
+{
+    return agi::make_unique<FFmpegSourceVideoProvider>(path, colormatrix, br);
 }
 
 #endif /* WITH_FFMS2 */
diff --git a/src/video_provider_manager.cpp b/src/video_provider_manager.cpp
index 3739a7293df7963232b295f2020655992b0a2a73..bbba57458d83bcd08c405c2a6a5f071a798bc7cc 100644
--- a/src/video_provider_manager.cpp
+++ b/src/video_provider_manager.cpp
@@ -25,80 +25,82 @@
 
 #include <boost/range/iterator_range.hpp>
 
-std::unique_ptr<VideoProvider> CreateDummyVideoProvider(agi::fs::path const&, std::string const&, agi::BackgroundRunner *);
-std::unique_ptr<VideoProvider> CreateYUV4MPEGVideoProvider(agi::fs::path const&, std::string const&, agi::BackgroundRunner *);
-std::unique_ptr<VideoProvider> CreateFFmpegSourceVideoProvider(agi::fs::path const&, std::string const&, agi::BackgroundRunner *);
-std::unique_ptr<VideoProvider> CreateAvisynthVideoProvider(agi::fs::path const&, std::string const&, agi::BackgroundRunner *);
+std::unique_ptr<VideoProvider> CreateDummyVideoProvider(agi::fs::path const &, std::string const &, agi::BackgroundRunner *);
+std::unique_ptr<VideoProvider> CreateYUV4MPEGVideoProvider(agi::fs::path const &, std::string const &, agi::BackgroundRunner *);
+std::unique_ptr<VideoProvider> CreateFFmpegSourceVideoProvider(agi::fs::path const &, std::string const &, agi::BackgroundRunner *);
+std::unique_ptr<VideoProvider> CreateAvisynthVideoProvider(agi::fs::path const &, std::string const &, agi::BackgroundRunner *);
 
 std::unique_ptr<VideoProvider> CreateCacheVideoProvider(std::unique_ptr<VideoProvider>);
 
 namespace {
-	struct factory {
-		const char *name;
-		std::unique_ptr<VideoProvider> (*create)(agi::fs::path const&, std::string const&, agi::BackgroundRunner *);
-		bool hidden;
-	};
+struct factory {
+    const char *name;
+    std::unique_ptr<VideoProvider> (*create)(agi::fs::path const &, std::string const &, agi::BackgroundRunner *);
+    bool hidden;
+};
 
-	const factory providers[] = {
-		{"Dummy", CreateDummyVideoProvider, true},
-		{"YUV4MPEG", CreateYUV4MPEGVideoProvider, true},
+const factory providers[] = {
+    {"Dummy", CreateDummyVideoProvider, true},
+    {"YUV4MPEG", CreateYUV4MPEGVideoProvider, true},
 #ifdef WITH_FFMS2
-		{"FFmpegSource", CreateFFmpegSourceVideoProvider, false},
+    {"FFmpegSource", CreateFFmpegSourceVideoProvider, false},
 #endif
 #ifdef WITH_AVISYNTH
-		{"Avisynth", CreateAvisynthVideoProvider, false},
+    {"Avisynth", CreateAvisynthVideoProvider, false},
 #endif
-	};
+};
 }
 
-std::vector<std::string> VideoProviderFactory::GetClasses() {
-	return ::GetClasses(boost::make_iterator_range(std::begin(providers), std::end(providers)));
+std::vector<std::string> VideoProviderFactory::GetClasses()
+{
+    return ::GetClasses(boost::make_iterator_range(std::begin(providers), std::end(providers)));
 }
 
-std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const& filename, std::string const& colormatrix, agi::BackgroundRunner *br) {
-	auto preferred = OPT_GET("Video/Provider")->GetString();
-	auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
+std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const &filename, std::string const &colormatrix, agi::BackgroundRunner *br)
+{
+    auto preferred = OPT_GET("Video/Provider")->GetString();
+    auto sorted = GetSorted(boost::make_iterator_range(std::begin(providers), std::end(providers)), preferred);
 
-	bool found = false;
-	bool supported = false;
-	std::string errors;
-	errors.reserve(1024);
+    bool found = false;
+    bool supported = false;
+    std::string errors;
+    errors.reserve(1024);
 
-	for (auto factory : sorted) {
-		std::string err;
-		try {
-			auto provider = factory->create(filename, colormatrix, br);
-			if (!provider) continue;
-			LOG_I("manager/video/provider") << factory->name << ": opened " << filename;
-			return provider->WantsCaching() ? CreateCacheVideoProvider(std::move(provider)) : std::move(provider);
-		}
-		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&) {
-			found = true;
-			err = "video is not in a supported format.";
-		}
-		catch (VideoOpenError const& ex) {
-			supported = true;
-			err = ex.GetMessage();
-		}
-		catch (agi::vfr::Error const& ex) {
-			supported = true;
-			err = ex.GetMessage();
-		}
+    for (auto factory : sorted) {
+        std::string err;
+        try {
+            auto provider = factory->create(filename, colormatrix, br);
+            if (!provider) continue;
+            LOG_I("manager/video/provider") << factory->name << ": opened " << filename;
+            return provider->WantsCaching() ? CreateCacheVideoProvider(std::move(provider)) : std::move(provider);
+        }
+        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 &) {
+            found = true;
+            err = "video is not in a supported format.";
+        }
+        catch (VideoOpenError const &ex) {
+            supported = true;
+            err = ex.GetMessage();
+        }
+        catch (agi::vfr::Error const &ex) {
+            supported = true;
+            err = ex.GetMessage();
+        }
 
-		errors += std::string(factory->name) + ": " + err + "\n";
-		LOG_D("manager/video/provider") << factory->name << ": " << err;
-	}
+        errors += std::string(factory->name) + ": " + err + "\n";
+        LOG_D("manager/video/provider") << factory->name << ": " << err;
+    }
 
-	// No provider could open the file
-	LOG_E("manager/video/provider") << "Could not open " << filename;
-	std::string msg = "Could not open " + filename.string() + ":\n" + errors;
+    // No provider could open the file
+    LOG_E("manager/video/provider") << "Could not open " << filename;
+    std::string msg = "Could not open " + filename.string() + ":\n" + errors;
 
-	if (!found) throw agi::fs::FileNotFound(filename.string());
-	if (!supported) throw VideoNotSupported(msg);
-	throw VideoOpenError(msg);
+    if (!found) throw agi::fs::FileNotFound(filename.string());
+    if (!supported) throw VideoNotSupported(msg);
+    throw VideoOpenError(msg);
 }
diff --git a/src/video_provider_manager.h b/src/video_provider_manager.h
index 0e4ddfcb301a5ea58d0379987761966a48b06689..270ca9c298ab5fffcab289940d088ca962907202 100644
--- a/src/video_provider_manager.h
+++ b/src/video_provider_manager.h
@@ -24,6 +24,6 @@ class VideoProvider;
 namespace agi { class BackgroundRunner; }
 
 struct VideoProviderFactory {
-	static std::vector<std::string> GetClasses();
-	static std::unique_ptr<VideoProvider> GetProvider(agi::fs::path const& video_file, std::string const& colormatrix, agi::BackgroundRunner *br);
+    static std::vector<std::string> GetClasses();
+    static std::unique_ptr<VideoProvider> GetProvider(agi::fs::path const &video_file, std::string const &colormatrix, agi::BackgroundRunner *br);
 };
diff --git a/src/video_provider_yuv4mpeg.cpp b/src/video_provider_yuv4mpeg.cpp
index 583cebcecf8f5af5128ffb2cd0fef1dc9da333af..1a4127762099d472f0e3aa4adca1c54573fe34a3 100644
--- a/src/video_provider_yuv4mpeg.cpp
+++ b/src/video_provider_yuv4mpeg.cpp
@@ -55,299 +55,302 @@ namespace {
 /// @class YUV4MPEGVideoProvider
 /// @brief Implements reading of YUV4MPEG uncompressed video files
 class YUV4MPEGVideoProvider final : public VideoProvider {
-	/// Pixel formats
-	enum Y4M_PixelFormat {
-		Y4M_PIXFMT_NONE		= -1,	/// not set/unknown
-
-		/// 4:2:0 sampling variants.
-		/// afaict the only difference between these three
-		/// is the chroma sample location, and nobody cares about that.
-		Y4M_PIXFMT_420JPEG,		/// 4:2:0, H/V centered, for JPEG/MPEG-1
-		Y4M_PIXFMT_420MPEG2,	/// 4:2:0, H cosited, for MPEG-2
-		Y4M_PIXFMT_420PALDV,	/// 4:2:0, alternating Cb/Cr, for PAL-DV
-
-		Y4M_PIXFMT_411,			/// 4:1:1, H cosited
-		Y4M_PIXFMT_422,			/// 4:2:2, H cosited
-		Y4M_PIXFMT_444,			/// 4:4:4, i.e. no chroma subsampling
-		Y4M_PIXFMT_444ALPHA,	/// 4:4:4 plus alpha channel
-
-		Y4M_PIXFMT_MONO		/// luma only (grayscale)
-	};
-
-	/// Interlacing mode for an entire stream
-	enum Y4M_InterlacingMode {
-		Y4M_ILACE_NOTSET = -1,	/// undefined
-		Y4M_ILACE_PROGRESSIVE,	/// progressive (no interlacing)
-
-		Y4M_ILACE_TFF,			/// interlaced, top field first
-		Y4M_ILACE_BFF,			/// interlaced, bottom field first
-
-		Y4M_ILACE_MIXED,		/// mixed interlaced/progressive, possibly with RFF flags
-		Y4M_ILACE_UNKNOWN		/// unknown interlacing mode (not the same as undefined)
-	};
-
-	/// Frame information flags
-	enum Y4M_FrameFlags {
-		Y4M_FFLAG_NOTSET	= -1,		/// undefined
-		Y4M_FFLAG_NONE		= 0x0000,	/// no flags set
-
-		/// field order/repeat field flags
-		Y4M_FFLAG_R_TFF		= 0x0001,	/// top field first
-		Y4M_FFLAG_R_TFF_R	= 0x0002,	/// top field first, and repeat that field
-		Y4M_FFLAG_R_BFF		= 0x0004,	/// bottom field first
-		Y4M_FFLAG_R_BFF_R	= 0x0008,	/// bottom field first, and repeat that field
-		Y4M_FFLAG_R_P		= 0x0010,	/// progressive
-		Y4M_FFLAG_R_P_R		= 0x0020,	/// progressive, and repeat frame once
-		Y4M_FFLAG_R_P_RR	= 0x0040,	/// progressive, and repeat frame twice
-
-		/// temporal sampling flags
-		Y4M_FFLAG_T_P		= 0x0080,	/// progressive (fields sampled at the same time)
-		Y4M_FFLAG_T_I		= 0x0100,	/// interlaced (fields sampled at different times)
-
-		/// chroma subsampling flags
-		Y4M_FFLAG_C_P		= 0x0200,	/// progressive (whole frame subsampled)
-		Y4M_FFLAG_C_I		= 0x0400,	/// interlaced (fields subsampled independently)
-		Y4M_FFLAG_C_UNKNOWN = 0x0800	/// unknown (only allowed for non-4:2:0 sampling)
-	};
-
-	agi::read_file_mapping file;
-	bool inited = false;	/// initialization state
-
-	int w = 0, h = 0;	/// frame width/height
-	int num_frames = -1; /// length of file in frames
-	int frame_sz;	/// size of each frame in bytes
-	int luma_sz;	/// size of the luma plane of each frame, in bytes
-	int chroma_sz;	/// size of one of the two chroma planes of each frame, in bytes
-
-	Y4M_PixelFormat pixfmt = Y4M_PIXFMT_NONE;		/// colorspace/pixel format
-	Y4M_InterlacingMode imode = Y4M_ILACE_NOTSET;	/// interlacing mode (for the entire stream)
-	struct {
-		int num = -1;	/// numerator
-		int den = 1;	/// denominator
-	} fps_rat;          /// framerate
-
-	agi::vfr::Framerate fps;
-
-	agi::ycbcr_converter conv{agi::ycbcr_matrix::bt601, agi::ycbcr_range::tv};
-
-	/// a list of byte positions detailing where in the file
-	/// each frame header can be found
-	std::vector<uint64_t> seek_table;
-
-	void ParseFileHeader(const std::vector<std::string>& tags);
-	Y4M_FrameFlags ParseFrameHeader(const std::vector<std::string>& tags);
-	std::vector<std::string> ReadHeader(uint64_t &startpos);
-	int IndexFile(uint64_t pos);
+    /// Pixel formats
+    enum Y4M_PixelFormat {
+        Y4M_PIXFMT_NONE		= -1,	/// not set/unknown
+
+        /// 4:2:0 sampling variants.
+        /// afaict the only difference between these three
+        /// is the chroma sample location, and nobody cares about that.
+        Y4M_PIXFMT_420JPEG,		/// 4:2:0, H/V centered, for JPEG/MPEG-1
+        Y4M_PIXFMT_420MPEG2,	/// 4:2:0, H cosited, for MPEG-2
+        Y4M_PIXFMT_420PALDV,	/// 4:2:0, alternating Cb/Cr, for PAL-DV
+
+        Y4M_PIXFMT_411,			/// 4:1:1, H cosited
+        Y4M_PIXFMT_422,			/// 4:2:2, H cosited
+        Y4M_PIXFMT_444,			/// 4:4:4, i.e. no chroma subsampling
+        Y4M_PIXFMT_444ALPHA,	/// 4:4:4 plus alpha channel
+
+        Y4M_PIXFMT_MONO		/// luma only (grayscale)
+    };
+
+    /// Interlacing mode for an entire stream
+    enum Y4M_InterlacingMode {
+        Y4M_ILACE_NOTSET = -1,	/// undefined
+        Y4M_ILACE_PROGRESSIVE,	/// progressive (no interlacing)
+
+        Y4M_ILACE_TFF,			/// interlaced, top field first
+        Y4M_ILACE_BFF,			/// interlaced, bottom field first
+
+        Y4M_ILACE_MIXED,		/// mixed interlaced/progressive, possibly with RFF flags
+        Y4M_ILACE_UNKNOWN		/// unknown interlacing mode (not the same as undefined)
+    };
+
+    /// Frame information flags
+    enum Y4M_FrameFlags {
+        Y4M_FFLAG_NOTSET	= -1,		/// undefined
+        Y4M_FFLAG_NONE		= 0x0000,	/// no flags set
+
+        /// field order/repeat field flags
+        Y4M_FFLAG_R_TFF		= 0x0001,	/// top field first
+        Y4M_FFLAG_R_TFF_R	= 0x0002,	/// top field first, and repeat that field
+        Y4M_FFLAG_R_BFF		= 0x0004,	/// bottom field first
+        Y4M_FFLAG_R_BFF_R	= 0x0008,	/// bottom field first, and repeat that field
+        Y4M_FFLAG_R_P		= 0x0010,	/// progressive
+        Y4M_FFLAG_R_P_R		= 0x0020,	/// progressive, and repeat frame once
+        Y4M_FFLAG_R_P_RR	= 0x0040,	/// progressive, and repeat frame twice
+
+        /// temporal sampling flags
+        Y4M_FFLAG_T_P		= 0x0080,	/// progressive (fields sampled at the same time)
+        Y4M_FFLAG_T_I		= 0x0100,	/// interlaced (fields sampled at different times)
+
+        /// chroma subsampling flags
+        Y4M_FFLAG_C_P		= 0x0200,	/// progressive (whole frame subsampled)
+        Y4M_FFLAG_C_I		= 0x0400,	/// interlaced (fields subsampled independently)
+        Y4M_FFLAG_C_UNKNOWN = 0x0800	/// unknown (only allowed for non-4:2:0 sampling)
+    };
+
+    agi::read_file_mapping file;
+    bool inited = false;	/// initialization state
+
+    int w = 0, h = 0;	/// frame width/height
+    int num_frames = -1; /// length of file in frames
+    int frame_sz;	/// size of each frame in bytes
+    int luma_sz;	/// size of the luma plane of each frame, in bytes
+    int chroma_sz;	/// size of one of the two chroma planes of each frame, in bytes
+
+    Y4M_PixelFormat pixfmt = Y4M_PIXFMT_NONE;		/// colorspace/pixel format
+    Y4M_InterlacingMode imode = Y4M_ILACE_NOTSET;	/// interlacing mode (for the entire stream)
+    struct {
+        int num = -1;	/// numerator
+        int den = 1;	/// denominator
+    } fps_rat;          /// framerate
+
+    agi::vfr::Framerate fps;
+
+    agi::ycbcr_converter conv{agi::ycbcr_matrix::bt601, agi::ycbcr_range::tv};
+
+    /// a list of byte positions detailing where in the file
+    /// each frame header can be found
+    std::vector<uint64_t> seek_table;
+
+    void ParseFileHeader(const std::vector<std::string> &tags);
+    Y4M_FrameFlags ParseFrameHeader(const std::vector<std::string> &tags);
+    std::vector<std::string> ReadHeader(uint64_t &startpos);
+    int IndexFile(uint64_t pos);
 
 public:
-	YUV4MPEGVideoProvider(agi::fs::path const& filename);
-
-	void GetFrame(int n, VideoFrame &frame) override;
-	void SetColorSpace(std::string const&) override { }
-
-	int GetFrameCount() const override             { return num_frames; }
-	int GetWidth() const override                  { return w; }
-	int GetHeight() const override                 { return h; }
-	double GetDAR() const override                 { return 0; }
-	agi::vfr::Framerate GetFPS() const override    { return fps; }
-	std::vector<int> GetKeyFrames() const override { return {}; }
-	std::string GetColorSpace() const override     { return "TV.601"; }
-	std::string GetDecoderName() const override    { return "YU4MPEG"; }
-	bool WantsCaching() const override             { return true; }
+    YUV4MPEGVideoProvider(agi::fs::path const &filename);
+
+    void GetFrame(int n, VideoFrame &frame) override;
+    void SetColorSpace(std::string const &) override { }
+
+    int GetFrameCount() const override             { return num_frames; }
+    int GetWidth() const override                  { return w; }
+    int GetHeight() const override                 { return h; }
+    double GetDAR() const override                 { return 0; }
+    agi::vfr::Framerate GetFPS() const override    { return fps; }
+    std::vector<int> GetKeyFrames() const override { return {}; }
+    std::string GetColorSpace() const override     { return "TV.601"; }
+    std::string GetDecoderName() const override    { return "YU4MPEG"; }
+    bool WantsCaching() const override             { return true; }
 };
 
 /// @brief Constructor
 /// @param filename The filename to open
-YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
-: file(filename)
+YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const &filename)
+    : file(filename)
 {
-	if (file.size() < 10)
-		throw VideoNotSupported("File is not a YUV4MPEG file (too small)");
-	if (strncmp("YUV4MPEG2 ", file.read(0, 10), 10))
-		throw VideoNotSupported("File is not a YUV4MPEG file (bad magic)");
-
-	uint64_t pos = 0;
-	ParseFileHeader(ReadHeader(pos));
-
-	if (w <= 0 || h <= 0)
-		throw VideoOpenError("Invalid resolution");
-	if (fps_rat.num <= 0 || fps_rat.den <= 0) {
-		fps_rat.num = 25;
-		fps_rat.den = 1;
-		LOG_D("provider/video/yuv4mpeg") << "framerate info unavailable, assuming 25fps";
-	}
-	if (pixfmt == Y4M_PIXFMT_NONE)
-		pixfmt = Y4M_PIXFMT_420JPEG;
-	if (imode == Y4M_ILACE_NOTSET)
-		imode = Y4M_ILACE_UNKNOWN;
-
-	luma_sz = w * h;
-	switch (pixfmt) {
-	case Y4M_PIXFMT_420JPEG:
-	case Y4M_PIXFMT_420MPEG2:
-	case Y4M_PIXFMT_420PALDV:
-		chroma_sz	= (w * h) >> 2; break;
-	default:
-		/// @todo add support for more pixel formats
-		throw VideoOpenError("Unsupported pixel format");
-	}
-	frame_sz	= luma_sz + chroma_sz*2;
-
-	num_frames = IndexFile(pos);
-	if (num_frames <= 0 || seek_table.empty())
-		throw VideoOpenError("Unable to determine file length");
+    if (file.size() < 10)
+        throw VideoNotSupported("File is not a YUV4MPEG file (too small)");
+    if (strncmp("YUV4MPEG2 ", file.read(0, 10), 10))
+        throw VideoNotSupported("File is not a YUV4MPEG file (bad magic)");
+
+    uint64_t pos = 0;
+    ParseFileHeader(ReadHeader(pos));
+
+    if (w <= 0 || h <= 0)
+        throw VideoOpenError("Invalid resolution");
+    if (fps_rat.num <= 0 || fps_rat.den <= 0) {
+        fps_rat.num = 25;
+        fps_rat.den = 1;
+        LOG_D("provider/video/yuv4mpeg") << "framerate info unavailable, assuming 25fps";
+    }
+    if (pixfmt == Y4M_PIXFMT_NONE)
+        pixfmt = Y4M_PIXFMT_420JPEG;
+    if (imode == Y4M_ILACE_NOTSET)
+        imode = Y4M_ILACE_UNKNOWN;
+
+    luma_sz = w * h;
+    switch (pixfmt) {
+    case Y4M_PIXFMT_420JPEG:
+    case Y4M_PIXFMT_420MPEG2:
+    case Y4M_PIXFMT_420PALDV:
+        chroma_sz	= (w * h) >> 2; break;
+    default:
+        /// @todo add support for more pixel formats
+        throw VideoOpenError("Unsupported pixel format");
+    }
+    frame_sz	= luma_sz + chroma_sz * 2;
+
+    num_frames = IndexFile(pos);
+    if (num_frames <= 0 || seek_table.empty())
+        throw VideoOpenError("Unable to determine file length");
 }
 
 /// @brief Read a frame or file header at a given file position
 /// @param startpos		The byte offset at where to start reading
 /// @return				A list of parameters
-std::vector<std::string> YUV4MPEGVideoProvider::ReadHeader(uint64_t &pos) {
-	std::vector<std::string> tags;
-	if (pos >= file.size())
-		return tags;
-
-	auto len = std::min<uint64_t>(YUV4MPEG_HEADER_MAXLEN, file.size() - pos);
-	auto buff = file.read(pos, len);
-
-	// read header until terminating newline (0x0A) is found
-	auto curtag = buff;
-	auto end = buff + len;
-	for (; buff < end && *buff != 0x0A; ++buff, ++pos) {
-		if (*buff == 0)
-			throw VideoOpenError("ReadHeader: Malformed header (unexpected NUL)");
-
-		if (*buff == 0x20) {
-			if (curtag != buff)
-				tags.emplace_back(curtag, buff);
-			curtag = buff + 1;
-		}
-	}
-
-	if (buff == end)
-		throw VideoOpenError("ReadHeader: Malformed header (no terminating newline found)");
-
-	// if only one tag with no trailing space was found (possible in the
-	// FRAME header case), make sure we get it
-	if (curtag != buff)
-		tags.emplace_back(curtag, buff);
-
-	pos += 1; // Move past newline
-
-	return tags;
+std::vector<std::string> YUV4MPEGVideoProvider::ReadHeader(uint64_t &pos)
+{
+    std::vector<std::string> tags;
+    if (pos >= file.size())
+        return tags;
+
+    auto len = std::min<uint64_t>(YUV4MPEG_HEADER_MAXLEN, file.size() - pos);
+    auto buff = file.read(pos, len);
+
+    // read header until terminating newline (0x0A) is found
+    auto curtag = buff;
+    auto end = buff + len;
+    for (; buff < end && *buff != 0x0A; ++buff, ++pos) {
+        if (*buff == 0)
+            throw VideoOpenError("ReadHeader: Malformed header (unexpected NUL)");
+
+        if (*buff == 0x20) {
+            if (curtag != buff)
+                tags.emplace_back(curtag, buff);
+            curtag = buff + 1;
+        }
+    }
+
+    if (buff == end)
+        throw VideoOpenError("ReadHeader: Malformed header (no terminating newline found)");
+
+    // if only one tag with no trailing space was found (possible in the
+    // FRAME header case), make sure we get it
+    if (curtag != buff)
+        tags.emplace_back(curtag, buff);
+
+    pos += 1; // Move past newline
+
+    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<std::string>& tags) {
-	if (tags.size() <= 1)
-		throw VideoOpenError("ParseFileHeader: contentless header");
-	if (tags.front() != "YUV4MPEG2")
-		throw VideoOpenError("ParseFileHeader: malformed header (bad magic)");
-
-	// temporary stuff
-	int t_w			= -1;
-	int t_h			= -1;
-	int t_fps_num	= -1;
-	int t_fps_den	= -1;
-	Y4M_InterlacingMode t_imode	= Y4M_ILACE_NOTSET;
-	Y4M_PixelFormat t_pixfmt	= Y4M_PIXFMT_NONE;
-
-	for (unsigned i = 1; i < tags.size(); i++) {
-		char type = tags[i][0];
-		std::string tag = tags[i].substr(1);
-
-		const char *err = nullptr;
-		if (type == 'W') {
-			if (!agi::util::try_parse(tag, &t_w))
-				err = "invalid width";
-		}
-		else if (type == 'H') {
-			if (!agi::util::try_parse(tag, &t_h))
-				err = "invalid height";
-		}
-		else if (type == 'F') {
-			size_t pos = tag.find(':');
-			if (pos == tag.npos)
-				err = "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))
-				err = "invalid framerate";
-		}
-		else if (type == 'C') {
-			// technically this should probably be case sensitive,
-			// but being liberal in what you accept doesn't hurt
-			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;
-			else if (tag == "420paldv")	t_pixfmt = Y4M_PIXFMT_420PALDV;
-			else if (tag == "411")		t_pixfmt = Y4M_PIXFMT_411;
-			else if (tag == "422")		t_pixfmt = Y4M_PIXFMT_422;
-			else if (tag == "444")		t_pixfmt = Y4M_PIXFMT_444;
-			else if (tag == "444alpha")	t_pixfmt = Y4M_PIXFMT_444ALPHA;
-			else if (tag == "mono")		t_pixfmt = Y4M_PIXFMT_MONO;
-			else
-				err = "invalid or unknown colorspace";
-		}
-		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;
-			else if (tag == "m")	t_imode = Y4M_ILACE_MIXED;
-			else if (tag == "?")	t_imode = Y4M_ILACE_UNKNOWN;
-			else
-				err = "invalid or unknown interlacing mode";
-		}
-		else
-			LOG_D("provider/video/yuv4mpeg") << "Unparsed tag: " << tags[i];
-
-		if (err)
-			throw VideoOpenError(err);
-	}
-
-	// The point of all this is to allow multiple YUV4MPEG2 headers in a single file
-	// (can happen if you concat several files) as long as they have identical
-	// header flags. The spec doesn't explicitly say you have to allow this,
-	// but the "reference implementation" (mjpegtools) does, so I'm doing it too.
-	if (inited) {
-		const char *err = nullptr;
-		if (t_w > 0 && t_w != w)
-			err = "illegal width change";
-		if (t_h > 0 && t_h != h)
-			err = "illegal height change";
-		if ((t_fps_num > 0 && t_fps_den > 0) && (t_fps_num != fps_rat.num || t_fps_den != fps_rat.den))
-			err = "illegal framerate change";
-		if (t_pixfmt != Y4M_PIXFMT_NONE && t_pixfmt != pixfmt)
-			err = "illegal colorspace change";
-		if (t_imode != Y4M_ILACE_NOTSET && t_imode != imode)
-			err = "illegal interlacing mode change";
-		if (err)
-			throw VideoOpenError(err);
-	}
-	else {
-		w = t_w;
-		h = t_h;
-		fps_rat.num = t_fps_num;
-		fps_rat.den = t_fps_den;
-		pixfmt		= t_pixfmt	!= Y4M_PIXFMT_NONE	? t_pixfmt	: Y4M_PIXFMT_420JPEG;
-		imode		= t_imode	!= Y4M_ILACE_NOTSET	? t_imode	: Y4M_ILACE_UNKNOWN;
-		fps = double(fps_rat.num) / fps_rat.den;
-		inited = true;
-	}
+void YUV4MPEGVideoProvider::ParseFileHeader(const std::vector<std::string> &tags)
+{
+    if (tags.size() <= 1)
+        throw VideoOpenError("ParseFileHeader: contentless header");
+    if (tags.front() != "YUV4MPEG2")
+        throw VideoOpenError("ParseFileHeader: malformed header (bad magic)");
+
+    // temporary stuff
+    int t_w			= -1;
+    int t_h			= -1;
+    int t_fps_num	= -1;
+    int t_fps_den	= -1;
+    Y4M_InterlacingMode t_imode	= Y4M_ILACE_NOTSET;
+    Y4M_PixelFormat t_pixfmt	= Y4M_PIXFMT_NONE;
+
+    for (unsigned i = 1; i < tags.size(); i++) {
+        char type = tags[i][0];
+        std::string tag = tags[i].substr(1);
+
+        const char *err = nullptr;
+        if (type == 'W') {
+            if (!agi::util::try_parse(tag, &t_w))
+                err = "invalid width";
+        }
+        else if (type == 'H') {
+            if (!agi::util::try_parse(tag, &t_h))
+                err = "invalid height";
+        }
+        else if (type == 'F') {
+            size_t pos = tag.find(':');
+            if (pos == tag.npos)
+                err = "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))
+                err = "invalid framerate";
+        }
+        else if (type == 'C') {
+            // technically this should probably be case sensitive,
+            // but being liberal in what you accept doesn't hurt
+            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;
+            else if (tag == "420paldv")	t_pixfmt = Y4M_PIXFMT_420PALDV;
+            else if (tag == "411")		t_pixfmt = Y4M_PIXFMT_411;
+            else if (tag == "422")		t_pixfmt = Y4M_PIXFMT_422;
+            else if (tag == "444")		t_pixfmt = Y4M_PIXFMT_444;
+            else if (tag == "444alpha")	t_pixfmt = Y4M_PIXFMT_444ALPHA;
+            else if (tag == "mono")		t_pixfmt = Y4M_PIXFMT_MONO;
+            else
+                err = "invalid or unknown colorspace";
+        }
+        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;
+            else if (tag == "m")	t_imode = Y4M_ILACE_MIXED;
+            else if (tag == "?")	t_imode = Y4M_ILACE_UNKNOWN;
+            else
+                err = "invalid or unknown interlacing mode";
+        }
+        else
+            LOG_D("provider/video/yuv4mpeg") << "Unparsed tag: " << tags[i];
+
+        if (err)
+            throw VideoOpenError(err);
+    }
+
+    // The point of all this is to allow multiple YUV4MPEG2 headers in a single file
+    // (can happen if you concat several files) as long as they have identical
+    // header flags. The spec doesn't explicitly say you have to allow this,
+    // but the "reference implementation" (mjpegtools) does, so I'm doing it too.
+    if (inited) {
+        const char *err = nullptr;
+        if (t_w > 0 && t_w != w)
+            err = "illegal width change";
+        if (t_h > 0 && t_h != h)
+            err = "illegal height change";
+        if ((t_fps_num > 0 && t_fps_den > 0) && (t_fps_num != fps_rat.num || t_fps_den != fps_rat.den))
+            err = "illegal framerate change";
+        if (t_pixfmt != Y4M_PIXFMT_NONE && t_pixfmt != pixfmt)
+            err = "illegal colorspace change";
+        if (t_imode != Y4M_ILACE_NOTSET && t_imode != imode)
+            err = "illegal interlacing mode change";
+        if (err)
+            throw VideoOpenError(err);
+    }
+    else {
+        w = t_w;
+        h = t_h;
+        fps_rat.num = t_fps_num;
+        fps_rat.den = t_fps_den;
+        pixfmt		= t_pixfmt	!= Y4M_PIXFMT_NONE	? t_pixfmt	: Y4M_PIXFMT_420JPEG;
+        imode		= t_imode	!= Y4M_ILACE_NOTSET	? t_imode	: Y4M_ILACE_UNKNOWN;
+        fps = double(fps_rat.num) / fps_rat.den;
+        inited = true;
+    }
 }
 
 /// @brief Parses a frame header
 /// @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<std::string>& tags) {
-	if (tags.front() == "FRAME")
-		return Y4M_FFLAG_NONE;
+YUV4MPEGVideoProvider::Y4M_FrameFlags YUV4MPEGVideoProvider::ParseFrameHeader(const std::vector<std::string> &tags)
+{
+    if (tags.front() == "FRAME")
+        return Y4M_FFLAG_NONE;
 
-	/// @todo implement parsing of frame flags
-	throw VideoOpenError("ParseFrameHeader: malformed frame header (bad magic)");
+    /// @todo implement parsing of frame flags
+    throw VideoOpenError("ParseFrameHeader: malformed frame header (bad magic)");
 }
 
 /// @brief Indexes the file
@@ -355,78 +358,81 @@ YUV4MPEGVideoProvider::Y4M_FrameFlags YUV4MPEGVideoProvider::ParseFrameHeader(co
 /// This function goes through the file, finds and parses all file and frame headers,
 /// and creates a seek table that lists the byte positions of all frames so seeking
 /// can easily be done.
-int YUV4MPEGVideoProvider::IndexFile(uint64_t pos) {
-	int framecount = 0;
-
-	// the ParseFileHeader() call in LoadVideo() will already have read
-	// the file header for us and set the seek position correctly
-	while (true) {
-		// continue reading headers until no more are found
-		std::vector<std::string> tags = ReadHeader(pos);
-		if (tags.empty())
-			break; // no more headers
-
-		Y4M_FrameFlags flags = Y4M_FFLAG_NOTSET;
-		if (tags.front() == "YUV4MPEG2") {
-			ParseFileHeader(tags);
-			continue;
-		}
-		else if (tags.front() == "FRAME")
-			flags = ParseFrameHeader(tags);
-
-		if (flags == Y4M_FFLAG_NONE) {
-			framecount++;
-			seek_table.push_back(pos);
-			pos += frame_sz;
-		}
-		else {
-			/// @todo implement rff flags etc
-		}
-	}
-
-	return framecount;
+int YUV4MPEGVideoProvider::IndexFile(uint64_t pos)
+{
+    int framecount = 0;
+
+    // the ParseFileHeader() call in LoadVideo() will already have read
+    // the file header for us and set the seek position correctly
+    while (true) {
+        // continue reading headers until no more are found
+        std::vector<std::string> tags = ReadHeader(pos);
+        if (tags.empty())
+            break; // no more headers
+
+        Y4M_FrameFlags flags = Y4M_FFLAG_NOTSET;
+        if (tags.front() == "YUV4MPEG2") {
+            ParseFileHeader(tags);
+            continue;
+        }
+        else if (tags.front() == "FRAME")
+            flags = ParseFrameHeader(tags);
+
+        if (flags == Y4M_FFLAG_NONE) {
+            framecount++;
+            seek_table.push_back(pos);
+            pos += frame_sz;
+        }
+        else {
+            /// @todo implement rff flags etc
+        }
+    }
+
+    return framecount;
 }
 
-void YUV4MPEGVideoProvider::GetFrame(int n, VideoFrame &frame) {
-	n = mid(0, n, num_frames - 1);
-
-	int uv_width = w / 2;
-
-	auto src_y = reinterpret_cast<const unsigned char *>(file.read(seek_table[n], luma_sz + chroma_sz * 2));
-	auto src_u = src_y + luma_sz;
-	auto src_v = src_u + chroma_sz;
-	frame.data.resize(w * h * 4);
-	unsigned char *dst = &frame.data[0];
-
-	for (int py = 0; py < h; ++py) {
-		for (int px = 0; px < w / 2; ++px) {
-			const uint8_t u = *src_u++;
-			const uint8_t v = *src_v++;
-			for (unsigned int i = 0; i < 2; ++i) {
-				const uint8_t y = *src_y++;
-				auto rgb = conv.ycbcr_to_rgb({{y, u, v}});
-				*dst++ = rgb[2];
-				*dst++ = rgb[1];
-				*dst++ = rgb[0];
-				*dst++ = 0;
-			}
-		}
-
-		// Roll back u/v on even lines
-		if (!(py & 1)) {
-			src_u -= uv_width;
-			src_v -= uv_width;
-		}
-	}
-
-	frame.flipped = false;
-	frame.width = w;
-	frame.height = h;
-	frame.pitch = w * 4;
+void YUV4MPEGVideoProvider::GetFrame(int n, VideoFrame &frame)
+{
+    n = mid(0, n, num_frames - 1);
+
+    int uv_width = w / 2;
+
+    auto src_y = reinterpret_cast<const unsigned char *>(file.read(seek_table[n], luma_sz + chroma_sz * 2));
+    auto src_u = src_y + luma_sz;
+    auto src_v = src_u + chroma_sz;
+    frame.data.resize(w * h * 4);
+    unsigned char *dst = &frame.data[0];
+
+    for (int py = 0; py < h; ++py) {
+        for (int px = 0; px < w / 2; ++px) {
+            const uint8_t u = *src_u++;
+            const uint8_t v = *src_v++;
+            for (unsigned int i = 0; i < 2; ++i) {
+                const uint8_t y = *src_y++;
+                auto rgb = conv.ycbcr_to_rgb({{y, u, v}});
+                *dst++ = rgb[2];
+                *dst++ = rgb[1];
+                *dst++ = rgb[0];
+                *dst++ = 0;
+            }
+        }
+
+        // Roll back u/v on even lines
+        if (!(py & 1)) {
+            src_u -= uv_width;
+            src_v -= uv_width;
+        }
+    }
+
+    frame.flipped = false;
+    frame.width = w;
+    frame.height = h;
+    frame.pitch = w * 4;
 }
 }
 
 namespace agi { class BackgroundRunner; }
-std::unique_ptr<VideoProvider> CreateYUV4MPEGVideoProvider(agi::fs::path const& path, std::string const&, agi::BackgroundRunner *) {
-	return agi::make_unique<YUV4MPEGVideoProvider>(path);
+std::unique_ptr<VideoProvider> CreateYUV4MPEGVideoProvider(agi::fs::path const &path, std::string const &, agi::BackgroundRunner *)
+{
+    return agi::make_unique<YUV4MPEGVideoProvider>(path);
 }
diff --git a/src/video_slider.cpp b/src/video_slider.cpp
index 705def983fab6ea7fd8b05a7b2a2bb37ebb80107..58cc2a5d84cbb16c36034e1f4d5e55e8e501b575 100644
--- a/src/video_slider.cpp
+++ b/src/video_slider.cpp
@@ -47,220 +47,230 @@
 #include <wx/dcbuffer.h>
 #include <wx/settings.h>
 
-VideoSlider::VideoSlider (wxWindow* parent, agi::Context *c)
-: wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE)
-, c(c)
-, connections(agi::signal::make_vector({
-	OPT_SUB("Video/Slider/Show Keyframes", [=] { Refresh(false); }),
-	c->videoController->AddSeekListener(&VideoSlider::SetValue, this),
-	c->project->AddVideoProviderListener(&VideoSlider::VideoOpened, this),
-	c->project->AddKeyframesListener(&VideoSlider::KeyframesChanged, this),
+VideoSlider::VideoSlider (wxWindow *parent, agi::Context *c)
+    : wxWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE)
+    , c(c)
+    , connections(agi::signal::make_vector({
+    OPT_SUB("Video/Slider/Show Keyframes", [ = ] { Refresh(false); }),
+    c->videoController->AddSeekListener(&VideoSlider::SetValue, this),
+    c->project->AddVideoProviderListener(&VideoSlider::VideoOpened, this),
+    c->project->AddKeyframesListener(&VideoSlider::KeyframesChanged, this),
 }))
 {
-	SetClientSize(20,25);
-	SetMinSize(wxSize(20, 25));
-	SetBackgroundStyle(wxBG_STYLE_PAINT);
+    SetClientSize(20, 25);
+    SetMinSize(wxSize(20, 25));
+    SetBackgroundStyle(wxBG_STYLE_PAINT);
 
-	c->videoSlider = this;
-	VideoOpened(c->project->VideoProvider());
+    c->videoSlider = this;
+    VideoOpened(c->project->VideoProvider());
 }
 
-void VideoSlider::SetValue(int value) {
-	if (val == value) return;
-	value = mid(0, value, max);
-	if (GetXAtValue(val) != GetXAtValue(value))
-		Refresh(false);
-	val = value;
+void VideoSlider::SetValue(int value)
+{
+    if (val == value) return;
+    value = mid(0, value, max);
+    if (GetXAtValue(val) != GetXAtValue(value))
+        Refresh(false);
+    val = value;
 }
 
-void VideoSlider::VideoOpened(AsyncVideoProvider *provider) {
-	if (provider) {
-		max = provider->GetFrameCount() - 1;
-		Refresh(false);
-	}
+void VideoSlider::VideoOpened(AsyncVideoProvider *provider)
+{
+    if (provider) {
+        max = provider->GetFrameCount() - 1;
+        Refresh(false);
+    }
 }
 
-void VideoSlider::KeyframesChanged(std::vector<int> const& newKeyframes) {
-	keyframes = newKeyframes;
-	Refresh(false);
+void VideoSlider::KeyframesChanged(std::vector<int> const &newKeyframes)
+{
+    keyframes = newKeyframes;
+    Refresh(false);
 }
 
-int VideoSlider::GetValueAtX(int x) {
-	int w = GetClientSize().GetWidth();
-	// Special case
-	if (w <= 10) return 0;
+int VideoSlider::GetValueAtX(int x)
+{
+    int w = GetClientSize().GetWidth();
+    // Special case
+    if (w <= 10) return 0;
 
-	return (int64_t)(x-5)*(int64_t)max/(int64_t)(w-10);
+    return (int64_t)(x - 5) * (int64_t)max / (int64_t)(w - 10);
 }
 
-int VideoSlider::GetXAtValue(int value) {
-	if (max <= 0) return 0;
+int VideoSlider::GetXAtValue(int value)
+{
+    if (max <= 0) return 0;
 
-	int w = GetClientSize().GetWidth();
-	return (int64_t)value*(int64_t)(w-10)/(int64_t)max+5;
+    int w = GetClientSize().GetWidth();
+    return (int64_t)value * (int64_t)(w - 10) / (int64_t)max + 5;
 }
 
 BEGIN_EVENT_TABLE(VideoSlider, wxWindow)
-	EVT_MOUSE_EVENTS(VideoSlider::OnMouse)
-	EVT_KEY_DOWN(VideoSlider::OnKeyDown)
-	EVT_CHAR_HOOK(VideoSlider::OnCharHook)
-	EVT_PAINT(VideoSlider::OnPaint)
-	EVT_SET_FOCUS(VideoSlider::OnFocus)
-	EVT_KILL_FOCUS(VideoSlider::OnFocus)
+    EVT_MOUSE_EVENTS(VideoSlider::OnMouse)
+    EVT_KEY_DOWN(VideoSlider::OnKeyDown)
+    EVT_CHAR_HOOK(VideoSlider::OnCharHook)
+    EVT_PAINT(VideoSlider::OnPaint)
+    EVT_SET_FOCUS(VideoSlider::OnFocus)
+    EVT_KILL_FOCUS(VideoSlider::OnFocus)
 END_EVENT_TABLE()
 
-void VideoSlider::OnMouse(wxMouseEvent &event) {
-	bool had_focus = HasFocus();
-	if (event.ButtonDown())
-		SetFocus();
-
-	if (event.LeftIsDown()) {
-		int x = event.GetX();
-
-		// If the slider didn't already have focus, don't seek if the user
-		// clicked very close to the current location as they were probably
-		// just trying to focus the slider
-		if (!had_focus && abs(x - GetXAtValue(val)) < 4)
-			return;
-
-		// Shift click to snap to keyframe
-		if (event.ShiftDown() && keyframes.size()) {
-			int clickedFrame = GetValueAtX(x);
-			auto pos = lower_bound(keyframes.begin(), keyframes.end(), clickedFrame);
-			if (pos == keyframes.end())
-				--pos;
-			else if (pos + 1 != keyframes.end() && clickedFrame - *pos > (*pos + 1) - clickedFrame)
-				++pos;
-
-			if (*pos == val) return;
-			SetValue(*pos);
-		}
-		// Normal click
-		else {
-			int go = GetValueAtX(x);
-			if (go == val) return;
-			SetValue(go);
-		}
-
-		c->videoController->JumpToFrame(val);
-	}
-	else if (event.GetWheelRotation() != 0 && ForwardMouseWheelEvent(this, event)) {
-		// If mouse is over the slider, use wheel to step by frames or keyframes (when Shift is held)
-		if (event.ShiftDown())
-			if (event.GetWheelRotation() < 0)
-				cmd::call("video/frame/next/keyframe", c);
-			else
-				cmd::call("video/frame/prev/keyframe", c);
-		else {
-			SetValue(val + (event.GetWheelRotation() > 0 ? -1 : 1));
-			c->videoController->JumpToFrame(val);
-		}
-	}
+void VideoSlider::OnMouse(wxMouseEvent &event)
+{
+    bool had_focus = HasFocus();
+    if (event.ButtonDown())
+        SetFocus();
+
+    if (event.LeftIsDown()) {
+        int x = event.GetX();
+
+        // If the slider didn't already have focus, don't seek if the user
+        // clicked very close to the current location as they were probably
+        // just trying to focus the slider
+        if (!had_focus && abs(x - GetXAtValue(val)) < 4)
+            return;
+
+        // Shift click to snap to keyframe
+        if (event.ShiftDown() && keyframes.size()) {
+            int clickedFrame = GetValueAtX(x);
+            auto pos = lower_bound(keyframes.begin(), keyframes.end(), clickedFrame);
+            if (pos == keyframes.end())
+                --pos;
+            else if (pos + 1 != keyframes.end() && clickedFrame - *pos > (*pos + 1) - clickedFrame)
+                ++pos;
+
+            if (*pos == val) return;
+            SetValue(*pos);
+        }
+        // Normal click
+        else {
+            int go = GetValueAtX(x);
+            if (go == val) return;
+            SetValue(go);
+        }
+
+        c->videoController->JumpToFrame(val);
+    }
+    else if (event.GetWheelRotation() != 0 && ForwardMouseWheelEvent(this, event)) {
+        // If mouse is over the slider, use wheel to step by frames or keyframes (when Shift is held)
+        if (event.ShiftDown())
+            if (event.GetWheelRotation() < 0)
+                cmd::call("video/frame/next/keyframe", c);
+            else
+                cmd::call("video/frame/prev/keyframe", c);
+        else {
+            SetValue(val + (event.GetWheelRotation() > 0 ? -1 : 1));
+            c->videoController->JumpToFrame(val);
+        }
+    }
 }
 
-void VideoSlider::OnCharHook(wxKeyEvent &event) {
-	hotkey::check("Video", c, event);
+void VideoSlider::OnCharHook(wxKeyEvent &event)
+{
+    hotkey::check("Video", c, event);
 }
 
-void VideoSlider::OnKeyDown(wxKeyEvent &event) {
-	// Forward up/down/pgup/pgdn/home/end to grid as those aren't yet handled by commands
-	switch (event.GetKeyCode()) {
-		case WXK_UP:
-		case WXK_DOWN:
-		case WXK_PAGEUP:
-		case WXK_PAGEDOWN:
-		case WXK_HOME:
-		case WXK_END:
-			c->subsGrid->GetEventHandler()->ProcessEvent(event);
-			break;
-		default:
-			event.Skip();
-	}
+void VideoSlider::OnKeyDown(wxKeyEvent &event)
+{
+    // Forward up/down/pgup/pgdn/home/end to grid as those aren't yet handled by commands
+    switch (event.GetKeyCode()) {
+    case WXK_UP:
+    case WXK_DOWN:
+    case WXK_PAGEUP:
+    case WXK_PAGEDOWN:
+    case WXK_HOME:
+    case WXK_END:
+        c->subsGrid->GetEventHandler()->ProcessEvent(event);
+        break;
+    default:
+        event.Skip();
+    }
 }
 
-void VideoSlider::OnPaint(wxPaintEvent &) {
-	wxAutoBufferedPaintDC dc(this);
-	int w,h;
-	GetClientSize(&w, &h);
-
-	// Colors
-	wxColour shad = wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW);
-	wxColour high = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
-	wxColour face = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
-	wxColour sel(123,251,232);
-	wxColour notSel(sel.Red()*2/5,sel.Green()*2/5,sel.Blue()*2/5);
-	wxColour bord(0,0,0);
-	int x1,x2,y1,y2;
-
-	// Background
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.SetBrush(face);
-	dc.DrawRectangle(0,0,w,h);
-
-	// Selection border
-	if (HasFocus()) {
-		dc.SetBrush(*wxTRANSPARENT_BRUSH);
-		dc.SetPen(wxPen(shad, 1, wxPENSTYLE_DOT));
-		dc.DrawRectangle(0,0,w,h);
-	}
-
-	// Draw slider
-	x1 = 5;
-	x2 = w-5;
-	y1 = 8;
-	y2 = h-8;
-	dc.SetPen(wxPen(shad));
-	dc.DrawLine(x1,y1,x2,y1);
-	dc.DrawLine(x1,y1,x1,y2);
-	dc.SetPen(wxPen(high));
-	dc.DrawLine(x1,y2,x2,y2);
-	dc.DrawLine(x2,y1,x2,y2);
-
-	// Draw keyframes
-	int curX;
-	if (OPT_GET("Video/Slider/Show Keyframes")->GetBool()) {
-		dc.SetPen(wxPen(shad));
-		for (int frame : keyframes) {
-			curX = GetXAtValue(frame);
-			dc.DrawLine(curX,2,curX,8);
-		}
-	}
-
-	// Draw cursor
-	curX = GetXAtValue(val);
-
-	// Fill bg
-	dc.SetBrush(wxBrush(face));
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.DrawRectangle(curX-2,y1-1,4,y2-y1+5);
-	dc.SetBrush(wxNullBrush);
-
-	// Draw cursor highlights
-	dc.SetPen(wxPen(high));
-	dc.DrawLine(curX,y1-2,curX-4,y1+2);
-	dc.DrawLine(curX-3,y1+2,curX-3,y2+5);
-
-	// Draw cursor shades
-	dc.SetPen(wxPen(shad));
-	dc.DrawLine(curX+1,y1-1,curX+4,y1+2);
-	dc.DrawLine(curX+3,y1+2,curX+3,y2+5);
-	dc.DrawLine(curX-3,y2+4,curX+3,y2+4);
-
-	// Draw cursor outline
-	dc.SetPen(wxPen(bord));
-	dc.DrawLine(curX,y1-3,curX-4,y1+1);
-	dc.DrawLine(curX,y1-3,curX+4,y1+1);
-	dc.DrawLine(curX-4,y1+1,curX-4,y2+5);
-	dc.DrawLine(curX+4,y1+1,curX+4,y2+5);
-	dc.DrawLine(curX-3,y2+5,curX+4,y2+5);
-	dc.DrawLine(curX-3,y2,curX+4,y2);
-
-	// Draw selection
-	dc.SetPen(*wxTRANSPARENT_PEN);
-	dc.SetBrush(HasFocus() ? wxBrush(sel) : wxBrush(notSel));
-	dc.DrawRectangle(curX-3,y2+1,7,4);
+void VideoSlider::OnPaint(wxPaintEvent &)
+{
+    wxAutoBufferedPaintDC dc(this);
+    int w, h;
+    GetClientSize(&w, &h);
+
+    // Colors
+    wxColour shad = wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW);
+    wxColour high = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
+    wxColour face = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
+    wxColour sel(123, 251, 232);
+    wxColour notSel(sel.Red() * 2 / 5, sel.Green() * 2 / 5, sel.Blue() * 2 / 5);
+    wxColour bord(0, 0, 0);
+    int x1, x2, y1, y2;
+
+    // Background
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.SetBrush(face);
+    dc.DrawRectangle(0, 0, w, h);
+
+    // Selection border
+    if (HasFocus()) {
+        dc.SetBrush(*wxTRANSPARENT_BRUSH);
+        dc.SetPen(wxPen(shad, 1, wxPENSTYLE_DOT));
+        dc.DrawRectangle(0, 0, w, h);
+    }
+
+    // Draw slider
+    x1 = 5;
+    x2 = w - 5;
+    y1 = 8;
+    y2 = h - 8;
+    dc.SetPen(wxPen(shad));
+    dc.DrawLine(x1, y1, x2, y1);
+    dc.DrawLine(x1, y1, x1, y2);
+    dc.SetPen(wxPen(high));
+    dc.DrawLine(x1, y2, x2, y2);
+    dc.DrawLine(x2, y1, x2, y2);
+
+    // Draw keyframes
+    int curX;
+    if (OPT_GET("Video/Slider/Show Keyframes")->GetBool()) {
+        dc.SetPen(wxPen(shad));
+        for (int frame : keyframes) {
+            curX = GetXAtValue(frame);
+            dc.DrawLine(curX, 2, curX, 8);
+        }
+    }
+
+    // Draw cursor
+    curX = GetXAtValue(val);
+
+    // Fill bg
+    dc.SetBrush(wxBrush(face));
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.DrawRectangle(curX - 2, y1 - 1, 4, y2 - y1 + 5);
+    dc.SetBrush(wxNullBrush);
+
+    // Draw cursor highlights
+    dc.SetPen(wxPen(high));
+    dc.DrawLine(curX, y1 - 2, curX - 4, y1 + 2);
+    dc.DrawLine(curX - 3, y1 + 2, curX - 3, y2 + 5);
+
+    // Draw cursor shades
+    dc.SetPen(wxPen(shad));
+    dc.DrawLine(curX + 1, y1 - 1, curX + 4, y1 + 2);
+    dc.DrawLine(curX + 3, y1 + 2, curX + 3, y2 + 5);
+    dc.DrawLine(curX - 3, y2 + 4, curX + 3, y2 + 4);
+
+    // Draw cursor outline
+    dc.SetPen(wxPen(bord));
+    dc.DrawLine(curX, y1 - 3, curX - 4, y1 + 1);
+    dc.DrawLine(curX, y1 - 3, curX + 4, y1 + 1);
+    dc.DrawLine(curX - 4, y1 + 1, curX - 4, y2 + 5);
+    dc.DrawLine(curX + 4, y1 + 1, curX + 4, y2 + 5);
+    dc.DrawLine(curX - 3, y2 + 5, curX + 4, y2 + 5);
+    dc.DrawLine(curX - 3, y2, curX + 4, y2);
+
+    // Draw selection
+    dc.SetPen(*wxTRANSPARENT_PEN);
+    dc.SetBrush(HasFocus() ? wxBrush(sel) : wxBrush(notSel));
+    dc.DrawRectangle(curX - 3, y2 + 1, 7, 4);
 }
 
-void VideoSlider::OnFocus(wxFocusEvent &) {
-	Refresh(false);
+void VideoSlider::OnFocus(wxFocusEvent &)
+{
+    Refresh(false);
 }
diff --git a/src/video_slider.h b/src/video_slider.h
index 0c0546d524351e91971ee9e197a8d13966e84895..3888e2cec669d6a5eca77dbdab7a05739f1ed54c 100644
--- a/src/video_slider.h
+++ b/src/video_slider.h
@@ -40,33 +40,33 @@ class AsyncVideoProvider;
 /// @class VideoSlider
 /// @brief Slider for displaying and adjusting the video position
 class VideoSlider: public wxWindow {
-	agi::Context *c; ///< Associated project context
-	std::vector<int> keyframes; ///< Currently loaded keyframes
-	std::vector<agi::signal::Connection> connections;
+    agi::Context *c; ///< Associated project context
+    std::vector<int> keyframes; ///< Currently loaded keyframes
+    std::vector<agi::signal::Connection> connections;
 
-	int val = 0; ///< Current frame number
-	int max = 1; ///< Last frame number
+    int val = 0; ///< Current frame number
+    int max = 1; ///< Last frame number
 
-	/// Get the frame number for the given x coordinate
-	int GetValueAtX(int x);
-	/// Get the x-coordinate for a frame number
-	int GetXAtValue(int value);
-	/// Set the position of the slider
-	void SetValue(int value);
+    /// Get the frame number for the given x coordinate
+    int GetValueAtX(int x);
+    /// Get the x-coordinate for a frame number
+    int GetXAtValue(int value);
+    /// Set the position of the slider
+    void SetValue(int value);
 
-	/// Video open event handler
-	void VideoOpened(AsyncVideoProvider *new_provider);
-	/// Keyframe open even handler
-	void KeyframesChanged(std::vector<int> const& newKeyframes);
+    /// Video open event handler
+    void VideoOpened(AsyncVideoProvider *new_provider);
+    /// Keyframe open even handler
+    void KeyframesChanged(std::vector<int> const &newKeyframes);
 
-	void OnMouse(wxMouseEvent &event);
-	void OnKeyDown(wxKeyEvent &event);
-	void OnCharHook(wxKeyEvent &event);
-	void OnPaint(wxPaintEvent &);
-	void OnFocus(wxFocusEvent &);
+    void OnMouse(wxMouseEvent &event);
+    void OnKeyDown(wxKeyEvent &event);
+    void OnCharHook(wxKeyEvent &event);
+    void OnPaint(wxPaintEvent &);
+    void OnFocus(wxFocusEvent &);
 
 public:
-	VideoSlider(wxWindow* parent, agi::Context *c);
+    VideoSlider(wxWindow *parent, agi::Context *c);
 
-	DECLARE_EVENT_TABLE()
+    DECLARE_EVENT_TABLE()
 };
diff --git a/src/visual_feature.cpp b/src/visual_feature.cpp
index 21c97abf6514cc90f9c0fc1a0b9ad6225de4173d..2e39d5ace7d5b321e8d7bfb9980176745d63bc80 100644
--- a/src/visual_feature.cpp
+++ b/src/visual_feature.cpp
@@ -35,81 +35,86 @@
 #include "gl_wrap.h"
 #include "visual_feature.h"
 
-bool VisualDraggableFeature::IsMouseOver(Vector2D mouse_pos) const {
-	if (!pos) return false;
+bool VisualDraggableFeature::IsMouseOver(Vector2D mouse_pos) const
+{
+    if (!pos) return false;
 
-	Vector2D delta = mouse_pos - pos;
+    Vector2D delta = mouse_pos - pos;
 
-	switch (type) {
-		case DRAG_BIG_SQUARE:
-			return fabs(delta.X()) < 6 && fabs(delta.Y()) < 6;
+    switch (type) {
+    case DRAG_BIG_SQUARE:
+        return fabs(delta.X()) < 6 && fabs(delta.Y()) < 6;
 
-		case DRAG_BIG_CIRCLE:
-			return delta.SquareLen() < 36;
+    case DRAG_BIG_CIRCLE:
+        return delta.SquareLen() < 36;
 
-		case DRAG_BIG_TRIANGLE: {
-			if (delta.Y() < -10 || delta.Y() > 6) return false;
-			float dy = delta.Y() - 6;
-			return 16 * delta.X() + 9 * dy < 0 && 16 * delta.X() - 9 * dy > 0;
-		}
+    case DRAG_BIG_TRIANGLE: {
+        if (delta.Y() < -10 || delta.Y() > 6) return false;
+        float dy = delta.Y() - 6;
+        return 16 * delta.X() + 9 * dy < 0 && 16 * delta.X() - 9 * dy > 0;
+    }
 
-		case DRAG_SMALL_SQUARE:
-			return fabs(delta.X()) < 3 && fabs(delta.Y()) < 3;
+    case DRAG_SMALL_SQUARE:
+        return fabs(delta.X()) < 3 && fabs(delta.Y()) < 3;
 
-		case DRAG_SMALL_CIRCLE:
-			return delta.SquareLen() < 9;
+    case DRAG_SMALL_CIRCLE:
+        return delta.SquareLen() < 9;
 
-		default:
-			return false;
-	}
+    default:
+        return false;
+    }
 }
 
-void VisualDraggableFeature::Draw(OpenGLWrapper const& gl) const {
-	if (!pos) return;
-
-	switch (type) {
-		case DRAG_BIG_SQUARE:
-			gl.DrawRectangle(pos - 6, pos + 6);
-			gl.DrawLine(pos - Vector2D(0, 12), pos + Vector2D(0, 12));
-			gl.DrawLine(pos - Vector2D(12, 0), pos + Vector2D(12, 0));
-			break;
-
-		case DRAG_BIG_CIRCLE:
-			gl.DrawCircle(pos, 6);
-			gl.DrawLine(pos - Vector2D(0, 12), pos + Vector2D(0, 12));
-			gl.DrawLine(pos - Vector2D(12, 0), pos + Vector2D(12, 0));
-			break;
-
-		case DRAG_BIG_TRIANGLE:
-			gl.DrawTriangle(pos - Vector2D(9, 6), pos + Vector2D(9, -6), pos + Vector2D(0, 10));
-			gl.DrawLine(pos, pos + Vector2D(0, -16));
-			gl.DrawLine(pos, pos + Vector2D(-14, 8));
-			gl.DrawLine(pos, pos + Vector2D(14, 8));
-			break;
-
-		case DRAG_SMALL_SQUARE:
-			gl.DrawRectangle(pos - 3, pos + 3);
-			break;
-
-		case DRAG_SMALL_CIRCLE:
-			gl.DrawCircle(pos, 3);
-			break;
-		default:
-			break;
-	}
+void VisualDraggableFeature::Draw(OpenGLWrapper const &gl) const
+{
+    if (!pos) return;
+
+    switch (type) {
+    case DRAG_BIG_SQUARE:
+        gl.DrawRectangle(pos - 6, pos + 6);
+        gl.DrawLine(pos - Vector2D(0, 12), pos + Vector2D(0, 12));
+        gl.DrawLine(pos - Vector2D(12, 0), pos + Vector2D(12, 0));
+        break;
+
+    case DRAG_BIG_CIRCLE:
+        gl.DrawCircle(pos, 6);
+        gl.DrawLine(pos - Vector2D(0, 12), pos + Vector2D(0, 12));
+        gl.DrawLine(pos - Vector2D(12, 0), pos + Vector2D(12, 0));
+        break;
+
+    case DRAG_BIG_TRIANGLE:
+        gl.DrawTriangle(pos - Vector2D(9, 6), pos + Vector2D(9, -6), pos + Vector2D(0, 10));
+        gl.DrawLine(pos, pos + Vector2D(0, -16));
+        gl.DrawLine(pos, pos + Vector2D(-14, 8));
+        gl.DrawLine(pos, pos + Vector2D(14, 8));
+        break;
+
+    case DRAG_SMALL_SQUARE:
+        gl.DrawRectangle(pos - 3, pos + 3);
+        break;
+
+    case DRAG_SMALL_CIRCLE:
+        gl.DrawCircle(pos, 3);
+        break;
+    default:
+        break;
+    }
 }
 
-void VisualDraggableFeature::StartDrag() {
-	start = pos;
+void VisualDraggableFeature::StartDrag()
+{
+    start = pos;
 }
 
-void VisualDraggableFeature::UpdateDrag(Vector2D d, bool single_axis) {
-	if (single_axis)
-		d = d.SingleAxis();
+void VisualDraggableFeature::UpdateDrag(Vector2D d, bool single_axis)
+{
+    if (single_axis)
+        d = d.SingleAxis();
 
-	pos = start + d;
+    pos = start + d;
 }
 
-bool VisualDraggableFeature::HasMoved() const {
-	return pos != start;
+bool VisualDraggableFeature::HasMoved() const
+{
+    return pos != start;
 }
diff --git a/src/visual_feature.h b/src/visual_feature.h
index 4b3fb6ed00cf8a02b31f566fffc41974d953c1eb..a0651c2b8bc576a28181104a7f37b8aa695e653e 100644
--- a/src/visual_feature.h
+++ b/src/visual_feature.h
@@ -43,12 +43,12 @@ class AssDialogue;
 
 /// VisualDraggableFeature display types
 enum DraggableFeatureType {
-	DRAG_NONE,
-	DRAG_BIG_SQUARE,
-	DRAG_BIG_CIRCLE,
-	DRAG_BIG_TRIANGLE,
-	DRAG_SMALL_SQUARE,
-	DRAG_SMALL_CIRCLE
+    DRAG_NONE,
+    DRAG_BIG_SQUARE,
+    DRAG_BIG_CIRCLE,
+    DRAG_BIG_TRIANGLE,
+    DRAG_SMALL_SQUARE,
+    DRAG_SMALL_CIRCLE
 };
 
 /// @class VisualDraggableFeature
@@ -57,30 +57,30 @@ enum DraggableFeatureType {
 /// By itself this class doesn't do much. It mostly just draws itself at a
 /// specified position and performs hit-testing.
 class VisualDraggableFeature : public boost::intrusive::make_list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>::type {
-	Vector2D start; ///< position before the last drag operation began
+    Vector2D start; ///< position before the last drag operation began
 
 public:
-	DraggableFeatureType type = DRAG_NONE; ///< Shape of feature
-	Vector2D pos;                          ///< Position of this feature
-	int layer = 0;                         ///< Layer; Higher = above
-	AssDialogue* line = nullptr;           ///< The dialogue line this feature is for; may be nullptr
+    DraggableFeatureType type = DRAG_NONE; ///< Shape of feature
+    Vector2D pos;                          ///< Position of this feature
+    int layer = 0;                         ///< Layer; Higher = above
+    AssDialogue *line = nullptr;           ///< The dialogue line this feature is for; may be nullptr
 
-	/// @brief Is the given point over this feature?
-	/// @param mouse_pos Position of the mouse
-	bool IsMouseOver(Vector2D mouse_pos) const;
+    /// @brief Is the given point over this feature?
+    /// @param mouse_pos Position of the mouse
+    bool IsMouseOver(Vector2D mouse_pos) const;
 
-	/// @brief Draw this feature
-	/// @param gl OpenGLWrapper to use
-	void Draw(OpenGLWrapper const& gl) const;
+    /// @brief Draw this feature
+    /// @param gl OpenGLWrapper to use
+    void Draw(OpenGLWrapper const &gl) const;
 
-	/// Start a drag
-	void StartDrag();
+    /// Start a drag
+    void StartDrag();
 
-	/// Update the position of the feature during a drag
-	/// @param d New position of the feature
-	/// @param single_axis Only apply the larger of the two changes to the position
-	void UpdateDrag(Vector2D d, bool single_axis);
+    /// Update the position of the feature during a drag
+    /// @param d New position of the feature
+    /// @param single_axis Only apply the larger of the two changes to the position
+    void UpdateDrag(Vector2D d, bool single_axis);
 
-	/// Has this feature actually moved since a drag was last started?
-	bool HasMoved() const;
+    /// Has this feature actually moved since a drag was last started?
+    bool HasMoved() const;
 };
diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp
index 61d3207226a038169ac54a24891fe06d3cc0107c..1c3a4e4ab429e731e95eabc6aac3a55c562cebd0 100644
--- a/src/visual_tool.cpp
+++ b/src/visual_tool.cpp
@@ -40,520 +40,545 @@
 #include <algorithm>
 
 VisualToolBase::VisualToolBase(VideoDisplay *parent, agi::Context *context)
-: c(context)
-, parent(parent)
-, frame_number(c->videoController->GetFrameN())
-, highlight_color_primary_opt(OPT_GET("Colour/Visual Tools/Highlight Primary"))
-, highlight_color_secondary_opt(OPT_GET("Colour/Visual Tools/Highlight Secondary"))
-, line_color_primary_opt(OPT_GET("Colour/Visual Tools/Lines Primary"))
-, line_color_secondary_opt(OPT_GET("Colour/Visual Tools/Lines Secondary"))
-, shaded_area_alpha_opt(OPT_GET("Colour/Visual Tools/Shaded Area Alpha"))
-, file_changed_connection(c->ass->AddCommitListener(&VisualToolBase::OnCommit, this))
+    : c(context)
+    , parent(parent)
+    , frame_number(c->videoController->GetFrameN())
+    , highlight_color_primary_opt(OPT_GET("Colour/Visual Tools/Highlight Primary"))
+    , highlight_color_secondary_opt(OPT_GET("Colour/Visual Tools/Highlight Secondary"))
+    , line_color_primary_opt(OPT_GET("Colour/Visual Tools/Lines Primary"))
+    , line_color_secondary_opt(OPT_GET("Colour/Visual Tools/Lines Secondary"))
+    , shaded_area_alpha_opt(OPT_GET("Colour/Visual Tools/Shaded Area Alpha"))
+    , file_changed_connection(c->ass->AddCommitListener(&VisualToolBase::OnCommit, this))
 {
-	int script_w, script_h;
-	c->ass->GetResolution(script_w, script_h);
-	script_res = Vector2D(script_w, script_h);
-	active_line = GetActiveDialogueLine();
-	connections.push_back(c->selectionController->AddActiveLineListener(&VisualToolBase::OnActiveLineChanged, this));
-	connections.push_back(c->videoController->AddSeekListener(&VisualToolBase::OnSeek, this));
-	parent->Bind(wxEVT_MOUSE_CAPTURE_LOST, &VisualToolBase::OnMouseCaptureLost, this);
+    int script_w, script_h;
+    c->ass->GetResolution(script_w, script_h);
+    script_res = Vector2D(script_w, script_h);
+    active_line = GetActiveDialogueLine();
+    connections.push_back(c->selectionController->AddActiveLineListener(&VisualToolBase::OnActiveLineChanged, this));
+    connections.push_back(c->videoController->AddSeekListener(&VisualToolBase::OnSeek, this));
+    parent->Bind(wxEVT_MOUSE_CAPTURE_LOST, &VisualToolBase::OnMouseCaptureLost, this);
 }
 
-void VisualToolBase::OnCommit(int type) {
-	holding = false;
-	dragging = false;
-
-	if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_SCRIPTINFO) {
-		int script_w, script_h;
-		c->ass->GetResolution(script_w, script_h);
-		script_res = Vector2D(script_w, script_h);
-		OnCoordinateSystemsChanged();
-	}
-
-	if (type & AssFile::COMMIT_DIAG_FULL || type & AssFile::COMMIT_DIAG_ADDREM) {
-		active_line = GetActiveDialogueLine();
-		OnFileChanged();
-	}
+void VisualToolBase::OnCommit(int type)
+{
+    holding = false;
+    dragging = false;
+
+    if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_SCRIPTINFO) {
+        int script_w, script_h;
+        c->ass->GetResolution(script_w, script_h);
+        script_res = Vector2D(script_w, script_h);
+        OnCoordinateSystemsChanged();
+    }
+
+    if (type & AssFile::COMMIT_DIAG_FULL || type & AssFile::COMMIT_DIAG_ADDREM) {
+        active_line = GetActiveDialogueLine();
+        OnFileChanged();
+    }
 }
 
-void VisualToolBase::OnSeek(int new_frame) {
-	if (frame_number == new_frame) return;
+void VisualToolBase::OnSeek(int new_frame)
+{
+    if (frame_number == new_frame) return;
 
-	frame_number = new_frame;
-	OnFrameChanged();
+    frame_number = new_frame;
+    OnFrameChanged();
 
-	AssDialogue *new_line = GetActiveDialogueLine();
-	if (new_line != active_line) {
-		dragging = false;
-		active_line = new_line;
-		OnLineChanged();
-	}
+    AssDialogue *new_line = GetActiveDialogueLine();
+    if (new_line != active_line) {
+        dragging = false;
+        active_line = new_line;
+        OnLineChanged();
+    }
 }
 
-void VisualToolBase::OnMouseCaptureLost(wxMouseCaptureLostEvent &) {
-	holding = false;
-	dragging = false;
+void VisualToolBase::OnMouseCaptureLost(wxMouseCaptureLostEvent &)
+{
+    holding = false;
+    dragging = false;
 }
 
-void VisualToolBase::OnActiveLineChanged(AssDialogue *new_line) {
-	if (!IsDisplayed(new_line))
-		new_line = nullptr;
-
-	holding = false;
-	dragging = false;
-	if (new_line != active_line) {
-		active_line = new_line;
-		OnLineChanged();
-		parent->Render();
-	}
+void VisualToolBase::OnActiveLineChanged(AssDialogue *new_line)
+{
+    if (!IsDisplayed(new_line))
+        new_line = nullptr;
+
+    holding = false;
+    dragging = false;
+    if (new_line != active_line) {
+        active_line = new_line;
+        OnLineChanged();
+        parent->Render();
+    }
 }
 
-bool VisualToolBase::IsDisplayed(AssDialogue *line) const {
-	int frame = c->videoController->GetFrameN();
-	return line
-		&& !line->Comment
-		&& c->videoController->FrameAtTime(line->Start, agi::vfr::START) <= frame
-		&& c->videoController->FrameAtTime(line->End, agi::vfr::END) >= frame;
+bool VisualToolBase::IsDisplayed(AssDialogue *line) const
+{
+    int frame = c->videoController->GetFrameN();
+    return line
+           && !line->Comment
+           && c->videoController->FrameAtTime(line->Start, agi::vfr::START) <= frame
+           && c->videoController->FrameAtTime(line->End, agi::vfr::END) >= frame;
 }
 
-void VisualToolBase::Commit(wxString message) {
-	file_changed_connection.Block();
-	if (message.empty())
-		message = _("visual typesetting");
+void VisualToolBase::Commit(wxString message)
+{
+    file_changed_connection.Block();
+    if (message.empty())
+        message = _("visual typesetting");
 
-	commit_id = c->ass->Commit(message, AssFile::COMMIT_DIAG_TEXT, commit_id);
-	file_changed_connection.Unblock();
+    commit_id = c->ass->Commit(message, AssFile::COMMIT_DIAG_TEXT, commit_id);
+    file_changed_connection.Unblock();
 }
 
-AssDialogue* VisualToolBase::GetActiveDialogueLine() {
-	AssDialogue *diag = c->selectionController->GetActiveLine();
-	if (IsDisplayed(diag))
-		return diag;
-	return nullptr;
+AssDialogue *VisualToolBase::GetActiveDialogueLine()
+{
+    AssDialogue *diag = c->selectionController->GetActiveLine();
+    if (IsDisplayed(diag))
+        return diag;
+    return nullptr;
 }
 
-void VisualToolBase::SetDisplayArea(int x, int y, int w, int h) {
-	if (x == video_pos.X() && y == video_pos.Y() && w == video_res.X() && h == video_res.Y()) return;
+void VisualToolBase::SetDisplayArea(int x, int y, int w, int h)
+{
+    if (x == video_pos.X() && y == video_pos.Y() && w == video_res.X() && h == video_res.Y()) return;
 
-	video_pos = Vector2D(x, y);
-	video_res = Vector2D(w, h);
+    video_pos = Vector2D(x, y);
+    video_res = Vector2D(w, h);
 
-	holding = false;
-	dragging = false;
-	if (parent->HasCapture())
-		parent->ReleaseMouse();
-	OnCoordinateSystemsChanged();
+    holding = false;
+    dragging = false;
+    if (parent->HasCapture())
+        parent->ReleaseMouse();
+    OnCoordinateSystemsChanged();
 }
 
-Vector2D VisualToolBase::ToScriptCoords(Vector2D point) const {
-	return (point - video_pos) * script_res / video_res;
+Vector2D VisualToolBase::ToScriptCoords(Vector2D point) const
+{
+    return (point - video_pos) * script_res / video_res;
 }
 
-Vector2D VisualToolBase::FromScriptCoords(Vector2D point) const {
-	return (point * video_res / script_res) + video_pos;
+Vector2D VisualToolBase::FromScriptCoords(Vector2D point) const
+{
+    return (point * video_res / script_res) + video_pos;
 }
 
 template<class FeatureType>
 VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context)
-: VisualToolBase(parent, context)
+    : VisualToolBase(parent, context)
 {
 }
 
 template<class FeatureType>
-void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
-	bool left_click = event.LeftDown();
-	bool left_double = event.LeftDClick();
-	shift_down = event.ShiftDown();
-	ctrl_down = event.CmdDown();
-	alt_down = event.AltDown();
-
-	mouse_pos = event.GetPosition();
-
-	if (event.Leaving()) {
-		mouse_pos = Vector2D();
-		parent->Render();
-		return;
-	}
-
-	if (!dragging) {
-		int max_layer = INT_MIN;
-		active_feature = nullptr;
-		for (auto& feature : features) {
-			if (feature.IsMouseOver(mouse_pos) && feature.layer >= max_layer) {
-				active_feature = &feature;
-				max_layer = feature.layer;
-			}
-		}
-	}
-
-	if (dragging) {
-		// continue drag
-		if (event.LeftIsDown()) {
-			for (auto sel : sel_features)
-				sel->UpdateDrag(mouse_pos - drag_start, shift_down);
-			for (auto sel : sel_features)
-				UpdateDrag(sel);
-			Commit();
-		}
-		// end drag
-		else {
-			dragging = false;
-
-			// mouse didn't move, fiddle with selection
-			if (active_feature && !active_feature->HasMoved()) {
-				// Don't deselect stuff that was selected in this click's mousedown event
-				if (!sel_changed) {
-					if (ctrl_down)
-						RemoveSelection(active_feature);
-					else
-						SetSelection(active_feature, true);
-				}
-			}
-
-			active_feature = nullptr;
-			parent->ReleaseMouse();
-			parent->SetFocus();
-		}
-	}
-	else if (holding) {
-		if (!event.LeftIsDown()) {
-			holding = false;
-
-			parent->ReleaseMouse();
-			parent->SetFocus();
-		}
-
-		UpdateHold();
-		Commit();
-
-	}
-	else if (left_click) {
-		drag_start = mouse_pos;
-
-		// start drag
-		if (active_feature) {
-			if (!sel_features.count(active_feature)) {
-				sel_changed = true;
-				SetSelection(active_feature, !ctrl_down);
-			}
-			else
-				sel_changed = false;
-
-			if (active_feature->line)
-				c->selectionController->SetActiveLine(active_feature->line);
-
-			if (InitializeDrag(active_feature)) {
-				for (auto sel : sel_features) sel->StartDrag();
-				dragging = true;
-				parent->CaptureMouse();
-			}
-		}
-		// start hold
-		else {
-			if (!alt_down && features.size() > 1) {
-				sel_features.clear();
-				c->selectionController->SetSelectedSet({ c->selectionController->GetActiveLine() });
-			}
-			if (active_line && InitializeHold()) {
-				holding = true;
-				parent->CaptureMouse();
-			}
-		}
-	}
-
-	if (active_line && left_double)
-		OnDoubleClick();
-
-	parent->Render();
-
-	// Only coalesce the changes made in a single drag
-	if (!event.LeftIsDown())
-		commit_id = -1;
+void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event)
+{
+    bool left_click = event.LeftDown();
+    bool left_double = event.LeftDClick();
+    shift_down = event.ShiftDown();
+    ctrl_down = event.CmdDown();
+    alt_down = event.AltDown();
+
+    mouse_pos = event.GetPosition();
+
+    if (event.Leaving()) {
+        mouse_pos = Vector2D();
+        parent->Render();
+        return;
+    }
+
+    if (!dragging) {
+        int max_layer = INT_MIN;
+        active_feature = nullptr;
+        for (auto &feature : features) {
+            if (feature.IsMouseOver(mouse_pos) && feature.layer >= max_layer) {
+                active_feature = &feature;
+                max_layer = feature.layer;
+            }
+        }
+    }
+
+    if (dragging) {
+        // continue drag
+        if (event.LeftIsDown()) {
+            for (auto sel : sel_features)
+                sel->UpdateDrag(mouse_pos - drag_start, shift_down);
+            for (auto sel : sel_features)
+                UpdateDrag(sel);
+            Commit();
+        }
+        // end drag
+        else {
+            dragging = false;
+
+            // mouse didn't move, fiddle with selection
+            if (active_feature && !active_feature->HasMoved()) {
+                // Don't deselect stuff that was selected in this click's mousedown event
+                if (!sel_changed) {
+                    if (ctrl_down)
+                        RemoveSelection(active_feature);
+                    else
+                        SetSelection(active_feature, true);
+                }
+            }
+
+            active_feature = nullptr;
+            parent->ReleaseMouse();
+            parent->SetFocus();
+        }
+    }
+    else if (holding) {
+        if (!event.LeftIsDown()) {
+            holding = false;
+
+            parent->ReleaseMouse();
+            parent->SetFocus();
+        }
+
+        UpdateHold();
+        Commit();
+
+    }
+    else if (left_click) {
+        drag_start = mouse_pos;
+
+        // start drag
+        if (active_feature) {
+            if (!sel_features.count(active_feature)) {
+                sel_changed = true;
+                SetSelection(active_feature, !ctrl_down);
+            }
+            else
+                sel_changed = false;
+
+            if (active_feature->line)
+                c->selectionController->SetActiveLine(active_feature->line);
+
+            if (InitializeDrag(active_feature)) {
+                for (auto sel : sel_features) sel->StartDrag();
+                dragging = true;
+                parent->CaptureMouse();
+            }
+        }
+        // start hold
+        else {
+            if (!alt_down && features.size() > 1) {
+                sel_features.clear();
+                c->selectionController->SetSelectedSet({ c->selectionController->GetActiveLine() });
+            }
+            if (active_line && InitializeHold()) {
+                holding = true;
+                parent->CaptureMouse();
+            }
+        }
+    }
+
+    if (active_line && left_double)
+        OnDoubleClick();
+
+    parent->Render();
+
+    // Only coalesce the changes made in a single drag
+    if (!event.LeftIsDown())
+        commit_id = -1;
 }
 
 template<class FeatureType>
-void VisualTool<FeatureType>::DrawAllFeatures() {
-	wxColour grid_color = to_wx(line_color_secondary_opt->GetColor());
-	gl.SetLineColour(grid_color, 1.0f, 1);
-	wxColour base_fill = to_wx(highlight_color_primary_opt->GetColor());
-	wxColour active_fill = to_wx(highlight_color_secondary_opt->GetColor());
-	wxColour alt_fill = to_wx(line_color_primary_opt->GetColor());
-	for (auto& feature : features) {
-		wxColour fill = base_fill;
-		if (&feature == active_feature)
-			fill = active_fill;
-		else if (sel_features.count(&feature))
-			fill = alt_fill;
-		gl.SetFillColour(fill, 0.3f);
-		feature.Draw(gl);
-	}
+void VisualTool<FeatureType>::DrawAllFeatures()
+{
+    wxColour grid_color = to_wx(line_color_secondary_opt->GetColor());
+    gl.SetLineColour(grid_color, 1.0f, 1);
+    wxColour base_fill = to_wx(highlight_color_primary_opt->GetColor());
+    wxColour active_fill = to_wx(highlight_color_secondary_opt->GetColor());
+    wxColour alt_fill = to_wx(line_color_primary_opt->GetColor());
+    for (auto &feature : features) {
+        wxColour fill = base_fill;
+        if (&feature == active_feature)
+            fill = active_fill;
+        else if (sel_features.count(&feature))
+            fill = alt_fill;
+        gl.SetFillColour(fill, 0.3f);
+        feature.Draw(gl);
+    }
 }
 
 template<class FeatureType>
-void VisualTool<FeatureType>::SetSelection(FeatureType *feat, bool clear) {
-	if (clear)
-		sel_features.clear();
-
-	if (sel_features.insert(feat).second && feat->line) {
-		Selection sel;
-		if (!clear)
-			sel = c->selectionController->GetSelectedSet();
-		if (sel.insert(feat->line).second)
-			c->selectionController->SetSelectedSet(std::move(sel));
-	}
+void VisualTool<FeatureType>::SetSelection(FeatureType *feat, bool clear)
+{
+    if (clear)
+        sel_features.clear();
+
+    if (sel_features.insert(feat).second && feat->line) {
+        Selection sel;
+        if (!clear)
+            sel = c->selectionController->GetSelectedSet();
+        if (sel.insert(feat->line).second)
+            c->selectionController->SetSelectedSet(std::move(sel));
+    }
 }
 
 template<class FeatureType>
-void VisualTool<FeatureType>::RemoveSelection(FeatureType *feat) {
-	if (!sel_features.erase(feat) || !feat->line) return;
-	for (auto sel : sel_features)
-		if (sel->line == feat->line) return;
+void VisualTool<FeatureType>::RemoveSelection(FeatureType *feat)
+{
+    if (!sel_features.erase(feat) || !feat->line) return;
+    for (auto sel : sel_features)
+        if (sel->line == feat->line) return;
 
-	auto sel = c->selectionController->GetSelectedSet();
+    auto sel = c->selectionController->GetSelectedSet();
 
-	// Don't deselect the only selected line
-	if (sel.size() <= 1) return;
+    // Don't deselect the only selected line
+    if (sel.size() <= 1) return;
 
-	sel.erase(feat->line);
+    sel.erase(feat->line);
 
-	// Set the active line to an arbitrary selected line if we just
-	// deselected the active line
-	AssDialogue *new_active = c->selectionController->GetActiveLine();
-	if (feat->line == new_active)
-		new_active = *sel.begin();
+    // Set the active line to an arbitrary selected line if we just
+    // deselected the active line
+    AssDialogue *new_active = c->selectionController->GetActiveLine();
+    if (feat->line == new_active)
+        new_active = *sel.begin();
 
-	c->selectionController->SetSelectionAndActive(std::move(sel), new_active);
+    c->selectionController->SetSelectionAndActive(std::move(sel), new_active);
 }
 
 //////// PARSERS
 
-typedef const std::vector<AssOverrideParameter> * param_vec;
+typedef const std::vector<AssOverrideParameter> *param_vec;
 
 // Find a tag's parameters in a line or return nullptr if it's not found
-static param_vec find_tag(std::vector<std::unique_ptr<AssDialogueBlock>>& blocks, std::string const& tag_name) {
-	for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
-		for (auto const& tag : ovr->Tags) {
-			if (tag.Name == tag_name)
-				return &tag.Params;
-		}
-	}
-
-	return nullptr;
+static param_vec find_tag(std::vector<std::unique_ptr<AssDialogueBlock>> &blocks, std::string const &tag_name)
+{
+    for (auto ovr : blocks | agi::of_type<AssDialogueBlockOverride>()) {
+        for (auto const &tag : ovr->Tags) {
+            if (tag.Name == tag_name)
+                return &tag.Params;
+        }
+    }
+
+    return nullptr;
 }
 
 // Get a Vector2D from the given tag parameters, or Vector2D::Bad() if they are not valid
-static Vector2D vec_or_bad(param_vec tag, size_t x_idx, size_t y_idx) {
-	if (!tag ||
-		tag->size() <= x_idx || tag->size() <= y_idx ||
-		(*tag)[x_idx].omitted || (*tag)[y_idx].omitted)
-	{
-		return Vector2D();
-	}
-	return Vector2D((*tag)[x_idx].Get<float>(), (*tag)[y_idx].Get<float>());
+static Vector2D vec_or_bad(param_vec tag, size_t x_idx, size_t y_idx)
+{
+    if (!tag ||
+        tag->size() <= x_idx || tag->size() <= y_idx ||
+        (*tag)[x_idx].omitted || (*tag)[y_idx].omitted) {
+        return Vector2D();
+    }
+    return Vector2D((*tag)[x_idx].Get<float>(), (*tag)[y_idx].Get<float>());
 }
 
-Vector2D VisualToolBase::GetLinePosition(AssDialogue *diag) {
-	auto blocks = diag->ParseTags();
-
-	if (Vector2D ret = vec_or_bad(find_tag(blocks, "\\pos"), 0, 1)) return ret;
-	if (Vector2D ret = vec_or_bad(find_tag(blocks, "\\move"), 0, 1)) return ret;
-
-	// Get default position
-	auto margin = diag->Margin;
-	int align = 2;
-
-	if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
-		align = style->alignment;
-		for (int i = 0; i < 3; i++) {
-			if (margin[i] == 0)
-				margin[i] = style->Margin[i];
-		}
-	}
-
-	param_vec align_tag;
-	int ovr_align = 0;
-	if ((align_tag = find_tag(blocks, "\\an")))
-		ovr_align = (*align_tag)[0].Get<int>(ovr_align);
-	else if ((align_tag = find_tag(blocks, "\\a")))
-		ovr_align = AssStyle::SsaToAss((*align_tag)[0].Get<int>(2));
-
-	if (ovr_align > 0 && ovr_align <= 9)
-		align = ovr_align;
-
-	// Alignment type
-	int hor = (align - 1) % 3;
-	int vert = (align - 1) / 3;
-
-	// Calculate positions
-	int x, y;
-	if (hor == 0)
-		x = margin[0];
-	else if (hor == 1)
-		x = (script_res.X() + margin[0] - margin[1]) / 2;
-	else
-		x = script_res.X() - margin[1];
-
-	if (vert == 0)
-		y = script_res.Y() - margin[2];
-	else if (vert == 1)
-		y = script_res.Y() / 2;
-	else
-		y = margin[2];
-
-	return Vector2D(x, y);
+Vector2D VisualToolBase::GetLinePosition(AssDialogue *diag)
+{
+    auto blocks = diag->ParseTags();
+
+    if (Vector2D ret = vec_or_bad(find_tag(blocks, "\\pos"), 0, 1)) return ret;
+    if (Vector2D ret = vec_or_bad(find_tag(blocks, "\\move"), 0, 1)) return ret;
+
+    // Get default position
+    auto margin = diag->Margin;
+    int align = 2;
+
+    if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
+        align = style->alignment;
+        for (int i = 0; i < 3; i++) {
+            if (margin[i] == 0)
+                margin[i] = style->Margin[i];
+        }
+    }
+
+    param_vec align_tag;
+    int ovr_align = 0;
+    if ((align_tag = find_tag(blocks, "\\an")))
+        ovr_align = (*align_tag)[0].Get<int>(ovr_align);
+    else if ((align_tag = find_tag(blocks, "\\a")))
+        ovr_align = AssStyle::SsaToAss((*align_tag)[0].Get<int>(2));
+
+    if (ovr_align > 0 && ovr_align <= 9)
+        align = ovr_align;
+
+    // Alignment type
+    int hor = (align - 1) % 3;
+    int vert = (align - 1) / 3;
+
+    // Calculate positions
+    int x, y;
+    if (hor == 0)
+        x = margin[0];
+    else if (hor == 1)
+        x = (script_res.X() + margin[0] - margin[1]) / 2;
+    else
+        x = script_res.X() - margin[1];
+
+    if (vert == 0)
+        y = script_res.Y() - margin[2];
+    else if (vert == 1)
+        y = script_res.Y() / 2;
+    else
+        y = margin[2];
+
+    return Vector2D(x, y);
 }
 
-Vector2D VisualToolBase::GetLineOrigin(AssDialogue *diag) {
-	auto blocks = diag->ParseTags();
-	return vec_or_bad(find_tag(blocks, "\\org"), 0, 1);
+Vector2D VisualToolBase::GetLineOrigin(AssDialogue *diag)
+{
+    auto blocks = diag->ParseTags();
+    return vec_or_bad(find_tag(blocks, "\\org"), 0, 1);
 }
 
-bool VisualToolBase::GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2) {
-	auto blocks = diag->ParseTags();
+bool VisualToolBase::GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2)
+{
+    auto blocks = diag->ParseTags();
 
-	param_vec tag = find_tag(blocks, "\\move");
-	if (!tag)
-		return false;
+    param_vec tag = find_tag(blocks, "\\move");
+    if (!tag)
+        return false;
 
-	p1 = vec_or_bad(tag, 0, 1);
-	p2 = vec_or_bad(tag, 2, 3);
-	// VSFilter actually defaults to -1, but it uses <= 0 to check for default and 0 seems less bug-prone
-	t1 = (*tag)[4].Get<int>(0);
-	t2 = (*tag)[5].Get<int>(0);
+    p1 = vec_or_bad(tag, 0, 1);
+    p2 = vec_or_bad(tag, 2, 3);
+    // VSFilter actually defaults to -1, but it uses <= 0 to check for default and 0 seems less bug-prone
+    t1 = (*tag)[4].Get<int>(0);
+    t2 = (*tag)[5].Get<int>(0);
 
-	return p1 && p2;
+    return p1 && p2;
 }
 
-void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz) {
-	rx = ry = rz = 0.f;
+void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz)
+{
+    rx = ry = rz = 0.f;
 
-	if (AssStyle *style = c->ass->GetStyle(diag->Style))
-		rz = style->angle;
+    if (AssStyle *style = c->ass->GetStyle(diag->Style))
+        rz = style->angle;
 
-	auto blocks = diag->ParseTags();
+    auto blocks = diag->ParseTags();
 
-	if (param_vec tag = find_tag(blocks, "\\frx"))
-		rx = tag->front().Get(rx);
-	if (param_vec tag = find_tag(blocks, "\\fry"))
-		ry = tag->front().Get(ry);
-	if (param_vec tag = find_tag(blocks, "\\frz"))
-		rz = tag->front().Get(rz);
-	else if ((tag = find_tag(blocks, "\\fr")))
-		rz = tag->front().Get(rz);
+    if (param_vec tag = find_tag(blocks, "\\frx"))
+        rx = tag->front().Get(rx);
+    if (param_vec tag = find_tag(blocks, "\\fry"))
+        ry = tag->front().Get(ry);
+    if (param_vec tag = find_tag(blocks, "\\frz"))
+        rz = tag->front().Get(rz);
+    else if ((tag = find_tag(blocks, "\\fr")))
+        rz = tag->front().Get(rz);
 }
 
-void VisualToolBase::GetLineShear(AssDialogue *diag, float& fax, float& fay) {
-	fax = fay = 0.f;
+void VisualToolBase::GetLineShear(AssDialogue *diag, float &fax, float &fay)
+{
+    fax = fay = 0.f;
 
-	auto blocks = diag->ParseTags();
+    auto blocks = diag->ParseTags();
 
-	if (param_vec tag = find_tag(blocks, "\\fax"))
-		fax = tag->front().Get(fax);
-	if (param_vec tag = find_tag(blocks, "\\fay"))
-		fay = tag->front().Get(fay);
+    if (param_vec tag = find_tag(blocks, "\\fax"))
+        fax = tag->front().Get(fax);
+    if (param_vec tag = find_tag(blocks, "\\fay"))
+        fay = tag->front().Get(fay);
 }
 
-void VisualToolBase::GetLineScale(AssDialogue *diag, Vector2D &scale) {
-	float x = 100.f, y = 100.f;
+void VisualToolBase::GetLineScale(AssDialogue *diag, Vector2D &scale)
+{
+    float x = 100.f, y = 100.f;
 
-	if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
-		x = style->scalex;
-		y = style->scaley;
-	}
+    if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
+        x = style->scalex;
+        y = style->scaley;
+    }
 
-	auto blocks = diag->ParseTags();
+    auto blocks = diag->ParseTags();
 
-	if (param_vec tag = find_tag(blocks, "\\fscx"))
-		x = tag->front().Get(x);
-	if (param_vec tag = find_tag(blocks, "\\fscy"))
-		y = tag->front().Get(y);
+    if (param_vec tag = find_tag(blocks, "\\fscx"))
+        x = tag->front().Get(x);
+    if (param_vec tag = find_tag(blocks, "\\fscy"))
+        y = tag->front().Get(y);
 
-	scale = Vector2D(x, y);
+    scale = Vector2D(x, y);
 }
 
-void VisualToolBase::GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse) {
-	inverse = false;
-
-	auto blocks = diag->ParseTags();
-	param_vec tag = find_tag(blocks, "\\iclip");
-	if (tag)
-		inverse = true;
-	else
-		tag = find_tag(blocks, "\\clip");
-
-	if (tag && tag->size() == 4) {
-		p1 = vec_or_bad(tag, 0, 1);
-		p2 = vec_or_bad(tag, 2, 3);
-	}
-	else {
-		p1 = Vector2D(0, 0);
-		p2 = script_res - 1;
-	}
+void VisualToolBase::GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse)
+{
+    inverse = false;
+
+    auto blocks = diag->ParseTags();
+    param_vec tag = find_tag(blocks, "\\iclip");
+    if (tag)
+        inverse = true;
+    else
+        tag = find_tag(blocks, "\\clip");
+
+    if (tag && tag->size() == 4) {
+        p1 = vec_or_bad(tag, 0, 1);
+        p2 = vec_or_bad(tag, 2, 3);
+    }
+    else {
+        p1 = Vector2D(0, 0);
+        p2 = script_res - 1;
+    }
 }
 
-std::string VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse) {
-	auto blocks = diag->ParseTags();
-
-	scale = 1;
-	inverse = false;
-
-	param_vec tag = find_tag(blocks, "\\iclip");
-	if (tag)
-		inverse = true;
-	else
-		tag = find_tag(blocks, "\\clip");
-
-	if (tag && tag->size() == 4) {
-		return agi::format("m %d %d l %d %d %d %d %d %d"
-			, (*tag)[0].Get<int>(), (*tag)[1].Get<int>()
-			, (*tag)[2].Get<int>(), (*tag)[1].Get<int>()
-			, (*tag)[2].Get<int>(), (*tag)[3].Get<int>()
-			, (*tag)[0].Get<int>(), (*tag)[3].Get<int>());
-	}
-	if (tag) {
-		scale = std::max((*tag)[0].Get(scale), 1);
-		return (*tag)[1].Get<std::string>("");
-	}
-
-	return "";
+std::string VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse)
+{
+    auto blocks = diag->ParseTags();
+
+    scale = 1;
+    inverse = false;
+
+    param_vec tag = find_tag(blocks, "\\iclip");
+    if (tag)
+        inverse = true;
+    else
+        tag = find_tag(blocks, "\\clip");
+
+    if (tag && tag->size() == 4) {
+        return agi::format("m %d %d l %d %d %d %d %d %d"
+                           , (*tag)[0].Get<int>(), (*tag)[1].Get<int>()
+                           , (*tag)[2].Get<int>(), (*tag)[1].Get<int>()
+                           , (*tag)[2].Get<int>(), (*tag)[3].Get<int>()
+                           , (*tag)[0].Get<int>(), (*tag)[3].Get<int>());
+    }
+    if (tag) {
+        scale = std::max((*tag)[0].Get(scale), 1);
+        return (*tag)[1].Get<std::string>("");
+    }
+
+    return "";
 }
 
-void VisualToolBase::SetSelectedOverride(std::string const& tag, std::string const& value) {
-	for (auto line : c->selectionController->GetSelectedSet())
-		SetOverride(line, tag, 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, std::string const& value) {
-	if (!line) return;
-
-	std::string removeTag;
-	if (tag == "\\1c") removeTag = "\\c";
-	else if (tag == "\\frz") removeTag = "\\fr";
-	else if (tag == "\\pos") removeTag = "\\move";
-	else if (tag == "\\move") removeTag = "\\pos";
-	else if (tag == "\\clip") removeTag = "\\iclip";
-	else if (tag == "\\iclip") removeTag = "\\clip";
-
-	// Get block at start
-	auto blocks = line->ParseTags();
-	AssDialogueBlock *block = blocks.front().get();
-
-	if (block->GetType() == AssBlockType::OVERRIDE) {
-		auto ovr = static_cast<AssDialogueBlockOverride*>(block);
-		// Remove old of same
-		for (size_t i = 0; i < ovr->Tags.size(); i++) {
-			std::string const& name = ovr->Tags[i].Name;
-			if (tag == name || removeTag == name) {
-				ovr->Tags.erase(ovr->Tags.begin() + i);
-				i--;
-			}
-		}
-		ovr->AddTag(tag + value);
-
-		line->UpdateText(blocks);
-	}
-	else
-		line->Text = "{" + tag + value + "}" + line->Text.get();
+void VisualToolBase::SetOverride(AssDialogue *line, std::string const &tag, std::string const &value)
+{
+    if (!line) return;
+
+    std::string removeTag;
+    if (tag == "\\1c") removeTag = "\\c";
+    else if (tag == "\\frz") removeTag = "\\fr";
+    else if (tag == "\\pos") removeTag = "\\move";
+    else if (tag == "\\move") removeTag = "\\pos";
+    else if (tag == "\\clip") removeTag = "\\iclip";
+    else if (tag == "\\iclip") removeTag = "\\clip";
+
+    // Get block at start
+    auto blocks = line->ParseTags();
+    AssDialogueBlock *block = blocks.front().get();
+
+    if (block->GetType() == AssBlockType::OVERRIDE) {
+        auto ovr = static_cast<AssDialogueBlockOverride *>(block);
+        // Remove old of same
+        for (size_t i = 0; i < ovr->Tags.size(); i++) {
+            std::string const &name = ovr->Tags[i].Name;
+            if (tag == name || removeTag == name) {
+                ovr->Tags.erase(ovr->Tags.begin() + i);
+                i--;
+            }
+        }
+        ovr->AddTag(tag + value);
+
+        line->UpdateText(blocks);
+    }
+    else
+        line->Text = "{" + tag + value + "}" + line->Text.get();
 }
 
 // If only export worked
diff --git a/src/visual_tool.h b/src/visual_tool.h
index 72eec42249596dafe0f80c27663621d5c683c9b9..57f61954e405da9325006ce8c039c08709e88859 100644
--- a/src/visual_tool.h
+++ b/src/visual_tool.h
@@ -35,8 +35,8 @@ class wxMouseCaptureLostEvent;
 class wxMouseEvent;
 class wxToolBar;
 namespace agi {
-	struct Context;
-	class OptionValue;
+struct Context;
+class OptionValue;
 }
 
 /// @class VisualToolBase
@@ -47,160 +47,160 @@ namespace agi {
 /// functionality as possible is implemented here to avoid having four copies
 /// of each method for no good reason (and four times as many error messages)
 class VisualToolBase {
-	void OnCommit(int type);
-	void OnSeek(int new_frame);
+    void OnCommit(int type);
+    void OnSeek(int new_frame);
 
-	void OnMouseCaptureLost(wxMouseCaptureLostEvent &);
+    void OnMouseCaptureLost(wxMouseCaptureLostEvent &);
 
-	/// @brief Get the dialogue line currently in the edit box
-	/// @return nullptr if the line is not active on the current frame
-	AssDialogue *GetActiveDialogueLine();
+    /// @brief Get the dialogue line currently in the edit box
+    /// @return nullptr if the line is not active on the current frame
+    AssDialogue *GetActiveDialogueLine();
 
-	// SubtitleSelectionListener implementation
-	void OnActiveLineChanged(AssDialogue *new_line);
+    // SubtitleSelectionListener implementation
+    void OnActiveLineChanged(AssDialogue *new_line);
 
-	// Below here are the virtuals that must be implemented
+    // Below here are the virtuals that must be implemented
 
-	/// Called when the script, video or screen resolutions change
-	virtual void OnCoordinateSystemsChanged() { DoRefresh(); }
+    /// Called when the script, video or screen resolutions change
+    virtual void OnCoordinateSystemsChanged() { DoRefresh(); }
 
-	/// Called when the file is changed by something other than a visual tool
-	virtual void OnFileChanged() { DoRefresh(); }
+    /// Called when the file is changed by something other than a visual tool
+    virtual void OnFileChanged() { DoRefresh(); }
 
-	/// Called when the frame number changes
-	virtual void OnFrameChanged() { }
+    /// Called when the frame number changes
+    virtual void OnFrameChanged() { }
 
-	/// Called when the active line changes
-	virtual void OnLineChanged() { DoRefresh(); }
+    /// Called when the active line changes
+    virtual void OnLineChanged() { DoRefresh(); }
 
-	/// Generic refresh to simplify tools which have no interesting state and
-	/// can simply do do the same thing for any external change (i.e. most of
-	/// them). Called only by the above virtual methods.
-	virtual void DoRefresh() { }
+    /// Generic refresh to simplify tools which have no interesting state and
+    /// can simply do do the same thing for any external change (i.e. most of
+    /// them). Called only by the above virtual methods.
+    virtual void DoRefresh() { }
 
 protected:
-	std::vector<agi::signal::Connection> connections;
+    std::vector<agi::signal::Connection> connections;
 
-	OpenGLWrapper gl;
+    OpenGLWrapper gl;
 
-	/// Called when the user double-clicks
-	virtual void OnDoubleClick() { }
+    /// Called when the user double-clicks
+    virtual void OnDoubleClick() { }
 
-	agi::Context *c;
-	VideoDisplay *parent;
+    agi::Context *c;
+    VideoDisplay *parent;
 
-	bool holding = false; ///< Is a hold currently in progress?
-	AssDialogue *active_line = nullptr; ///< Active dialogue line; nullptr if it is not visible on the current frame
-	bool dragging = false; ///< Is a drag currently in progress?
+    bool holding = false; ///< Is a hold currently in progress?
+    AssDialogue *active_line = nullptr; ///< Active dialogue line; nullptr if it is not visible on the current frame
+    bool dragging = false; ///< Is a drag currently in progress?
 
-	int frame_number; ///< Current frame number
+    int frame_number; ///< Current frame number
 
-	bool shift_down = false; ///< Is shift down?
-	bool ctrl_down = false; ///< Is ctrl down?
-	bool alt_down = false; ///< Is alt down?
+    bool shift_down = false; ///< Is shift down?
+    bool ctrl_down = false; ///< Is ctrl down?
+    bool alt_down = false; ///< Is alt down?
 
-	Vector2D mouse_pos; ///< Last seen mouse position
-	Vector2D drag_start; ///< Mouse position at the beginning of the last drag
-	Vector2D script_res; ///< Script resolution
-	Vector2D video_pos; ///< Top-left corner of the video in the display area
-	Vector2D video_res; ///< Video resolution
+    Vector2D mouse_pos; ///< Last seen mouse position
+    Vector2D drag_start; ///< Mouse position at the beginning of the last drag
+    Vector2D script_res; ///< Script resolution
+    Vector2D video_pos; ///< Top-left corner of the video in the display area
+    Vector2D video_res; ///< Video resolution
 
-	const agi::OptionValue *highlight_color_primary_opt;
-	const agi::OptionValue *highlight_color_secondary_opt;
-	const agi::OptionValue *line_color_primary_opt;
-	const agi::OptionValue *line_color_secondary_opt;
-	const agi::OptionValue *shaded_area_alpha_opt;
+    const agi::OptionValue *highlight_color_primary_opt;
+    const agi::OptionValue *highlight_color_secondary_opt;
+    const agi::OptionValue *line_color_primary_opt;
+    const agi::OptionValue *line_color_secondary_opt;
+    const agi::OptionValue *shaded_area_alpha_opt;
 
-	agi::signal::Connection file_changed_connection;
-	int commit_id = -1; ///< Last used commit id for coalescing
+    agi::signal::Connection file_changed_connection;
+    int commit_id = -1; ///< Last used commit id for coalescing
 
-	/// @brief Commit the current file state
-	/// @param message Description of changes for undo
-	virtual void Commit(wxString message = wxString());
-	bool IsDisplayed(AssDialogue *line) const;
+    /// @brief Commit the current file state
+    /// @param message Description of changes for undo
+    virtual void Commit(wxString message = wxString());
+    bool IsDisplayed(AssDialogue *line) const;
 
-	/// Get the line's position if it's set, or it's default based on style if not
-	Vector2D GetLinePosition(AssDialogue *diag);
-	/// Get the line's origin if it's set, or Vector2D::Bad() if not
-	Vector2D GetLineOrigin(AssDialogue *diag);
-	bool GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2);
-	void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz);
-	void GetLineShear(AssDialogue *diag, float& fax, float& fay);
-	void GetLineScale(AssDialogue *diag, Vector2D &scale);
-	void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
-	std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
+    /// Get the line's position if it's set, or it's default based on style if not
+    Vector2D GetLinePosition(AssDialogue *diag);
+    /// Get the line's origin if it's set, or Vector2D::Bad() if not
+    Vector2D GetLineOrigin(AssDialogue *diag);
+    bool GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2);
+    void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz);
+    void GetLineShear(AssDialogue *diag, float &fax, float &fay);
+    void GetLineScale(AssDialogue *diag, Vector2D &scale);
+    void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
+    std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
 
-	void SetOverride(AssDialogue* line, std::string const& tag, std::string const& value);
-	void SetSelectedOverride(std::string const& tag, std::string 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);
+    VisualToolBase(VideoDisplay *parent, agi::Context *context);
 
 public:
-	/// Convert a point from video to script coordinates
-	Vector2D ToScriptCoords(Vector2D point) const;
-	/// Convert a point from script to video coordinates
-	Vector2D FromScriptCoords(Vector2D point) const;
-
-	// Stuff called by VideoDisplay
-	virtual void OnMouseEvent(wxMouseEvent &event)=0;
-	virtual void Draw()=0;
-	virtual void SetDisplayArea(int x, int y, int w, int h);
-	virtual void SetToolbar(wxToolBar *) { }
-	virtual ~VisualToolBase() = default;
+    /// Convert a point from video to script coordinates
+    Vector2D ToScriptCoords(Vector2D point) const;
+    /// Convert a point from script to video coordinates
+    Vector2D FromScriptCoords(Vector2D point) const;
+
+    // Stuff called by VideoDisplay
+    virtual void OnMouseEvent(wxMouseEvent &event) = 0;
+    virtual void Draw() = 0;
+    virtual void SetDisplayArea(int x, int y, int w, int h);
+    virtual void SetToolbar(wxToolBar *) { }
+    virtual ~VisualToolBase() = default;
 };
 
 /// Visual tool base class containing all common feature-related functionality
 template<class FeatureType>
 class VisualTool : public VisualToolBase {
 protected:
-	typedef FeatureType Feature;
-	typedef agi::owning_intrusive_list<FeatureType> feature_list;
+    typedef FeatureType Feature;
+    typedef agi::owning_intrusive_list<FeatureType> feature_list;
 
 private:
-	bool sel_changed = false; /// Has the selection already been changed in the current click?
-
-	/// @brief Called when a hold is begun
-	/// @return Should the hold actually happen?
-	virtual bool InitializeHold() { return false; }
-	/// @brief Called on every mouse event during a hold
-	virtual void UpdateHold() { }
-
-	/// @brief Called at the beginning of a drag
-	/// @param feature The visual feature clicked on
-	/// @return Should the drag happen?
-	virtual bool InitializeDrag(FeatureType *feature) { return true; }
-	/// @brief Called on every mouse event during a drag
-	/// @param feature The current feature to process; not necessarily the one clicked on
-	virtual void UpdateDrag(FeatureType *feature) { }
+    bool sel_changed = false; /// Has the selection already been changed in the current click?
+
+    /// @brief Called when a hold is begun
+    /// @return Should the hold actually happen?
+    virtual bool InitializeHold() { return false; }
+    /// @brief Called on every mouse event during a hold
+    virtual void UpdateHold() { }
+
+    /// @brief Called at the beginning of a drag
+    /// @param feature The visual feature clicked on
+    /// @return Should the drag happen?
+    virtual bool InitializeDrag(FeatureType *feature) { return true; }
+    /// @brief Called on every mouse event during a drag
+    /// @param feature The current feature to process; not necessarily the one clicked on
+    virtual void UpdateDrag(FeatureType *feature) { }
 
 protected:
-	std::set<FeatureType *> sel_features; ///< Currently selected visual features
+    std::set<FeatureType *> sel_features; ///< Currently selected visual features
 
-	/// Topmost feature under the mouse; generally only valid during a drag
-	FeatureType *active_feature = nullptr;
-	/// List of features which are drawn and can be clicked on
-	/// List is used here for the iterator invalidation properties
-	feature_list features;
+    /// Topmost feature under the mouse; generally only valid during a drag
+    FeatureType *active_feature = nullptr;
+    /// List of features which are drawn and can be clicked on
+    /// List is used here for the iterator invalidation properties
+    feature_list features;
 
-	/// Draw all of the features in the list
-	void DrawAllFeatures();
+    /// Draw all of the features in the list
+    void DrawAllFeatures();
 
-	/// @brief Remove a feature from the selection
-	/// @param i Index in the feature list
-	/// Also deselects lines if all features for that line have been deselected
-	void RemoveSelection(FeatureType *feat);
+    /// @brief Remove a feature from the selection
+    /// @param i Index in the feature list
+    /// Also deselects lines if all features for that line have been deselected
+    void RemoveSelection(FeatureType *feat);
 
-	/// @brief Set the selection to a single feature, deselecting everything else
-	/// @param i Index in the feature list
-	void SetSelection(FeatureType *feat, bool clear);
+    /// @brief Set the selection to a single feature, deselecting everything else
+    /// @param i Index in the feature list
+    void SetSelection(FeatureType *feat, bool clear);
 
 public:
-	/// @brief Handler for all mouse events
-	/// @param event Shockingly enough, the mouse event
-	void OnMouseEvent(wxMouseEvent &event) override;
-
-	/// @brief Constructor
-	/// @param parent The VideoDisplay to use for coordinate conversion
-	/// @param video Video and mouse information passing blob
-	VisualTool(VideoDisplay *parent, agi::Context *context);
+    /// @brief Handler for all mouse events
+    /// @param event Shockingly enough, the mouse event
+    void OnMouseEvent(wxMouseEvent &event) override;
+
+    /// @brief Constructor
+    /// @param parent The VideoDisplay to use for coordinate conversion
+    /// @param video Video and mouse information passing blob
+    VisualTool(VideoDisplay *parent, agi::Context *context);
 };
diff --git a/src/visual_tool_clip.cpp b/src/visual_tool_clip.cpp
index 52a1024b26c5e84b39319e0d490875993bbb80b2..4c9c6b1075a1e39cdd2e0664d6b5e367d8f5b5c4 100644
--- a/src/visual_tool_clip.cpp
+++ b/src/visual_tool_clip.cpp
@@ -31,118 +31,125 @@
 #include <wx/colour.h>
 
 VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context)
-: VisualTool<ClipCorner>(parent, context)
-, cur_1(0, 0)
-, cur_2(video_res)
+    : VisualTool<ClipCorner>(parent, context)
+    , cur_1(0, 0)
+    , cur_2(video_res)
 {
-	ClipCorner *feats[4];
-	for (auto& feat : feats) {
-		feat = new ClipCorner;
-		features.push_back(*feat);
-	}
-
-	// Attach each feature to the two features it shares edges with
-	// Top-left
-	int i = 0;
-	feats[i]->horiz = feats[1];
-	feats[i]->vert = feats[2];
-	i++;
-
-	// Top-right
-	feats[i]->horiz = feats[0];
-	feats[i]->vert = feats[3];
-	i++;
-
-	// Bottom-left
-	feats[i]->horiz = feats[3];
-	feats[i]->vert = feats[0];
-	i++;
-
-	// Bottom-right
-	feats[i]->horiz = feats[2];
-	feats[i]->vert = feats[1];
+    ClipCorner *feats[4];
+    for (auto &feat : feats) {
+        feat = new ClipCorner;
+        features.push_back(*feat);
+    }
+
+    // Attach each feature to the two features it shares edges with
+    // Top-left
+    int i = 0;
+    feats[i]->horiz = feats[1];
+    feats[i]->vert = feats[2];
+    i++;
+
+    // Top-right
+    feats[i]->horiz = feats[0];
+    feats[i]->vert = feats[3];
+    i++;
+
+    // Bottom-left
+    feats[i]->horiz = feats[3];
+    feats[i]->vert = feats[0];
+    i++;
+
+    // Bottom-right
+    feats[i]->horiz = feats[2];
+    feats[i]->vert = feats[1];
 }
 
-void VisualToolClip::Draw() {
-	if (!active_line) return;
-
-	DrawAllFeatures();
-
-	// Load colors from options
-	wxColour line_color = to_wx(line_color_primary_opt->GetColor());
-	float shaded_alpha = static_cast<float>(shaded_area_alpha_opt->GetDouble());
-
-	// Draw rectangle
-	gl.SetLineColour(line_color, 1.0f, 2);
-	gl.SetFillColour(line_color, 0.0f);
-	gl.DrawRectangle(cur_1, cur_2);
-
-	// Draw outside area
-	gl.SetLineColour(line_color, 0.0f);
-	gl.SetFillColour(*wxBLACK, shaded_alpha);
-	if (inverse) {
-		gl.DrawRectangle(cur_1, cur_2);
-	}
-	else {
-		Vector2D v_min = video_pos;
-		Vector2D v_max = video_pos + video_res;
-		Vector2D c_min = cur_1.Min(cur_2);
-		Vector2D c_max = cur_1.Max(cur_2);
-		gl.DrawRectangle(v_min,                  Vector2D(v_max, c_min));
-		gl.DrawRectangle(Vector2D(v_min, c_max), v_max);
-		gl.DrawRectangle(Vector2D(v_min, c_min), Vector2D(c_min, c_max));
-		gl.DrawRectangle(Vector2D(c_max, c_min), Vector2D(v_max, c_max));
-	}
+void VisualToolClip::Draw()
+{
+    if (!active_line) return;
+
+    DrawAllFeatures();
+
+    // Load colors from options
+    wxColour line_color = to_wx(line_color_primary_opt->GetColor());
+    float shaded_alpha = static_cast<float>(shaded_area_alpha_opt->GetDouble());
+
+    // Draw rectangle
+    gl.SetLineColour(line_color, 1.0f, 2);
+    gl.SetFillColour(line_color, 0.0f);
+    gl.DrawRectangle(cur_1, cur_2);
+
+    // Draw outside area
+    gl.SetLineColour(line_color, 0.0f);
+    gl.SetFillColour(*wxBLACK, shaded_alpha);
+    if (inverse) {
+        gl.DrawRectangle(cur_1, cur_2);
+    }
+    else {
+        Vector2D v_min = video_pos;
+        Vector2D v_max = video_pos + video_res;
+        Vector2D c_min = cur_1.Min(cur_2);
+        Vector2D c_max = cur_1.Max(cur_2);
+        gl.DrawRectangle(v_min,                  Vector2D(v_max, c_min));
+        gl.DrawRectangle(Vector2D(v_min, c_max), v_max);
+        gl.DrawRectangle(Vector2D(v_min, c_min), Vector2D(c_min, c_max));
+        gl.DrawRectangle(Vector2D(c_max, c_min), Vector2D(v_max, c_max));
+    }
 }
 
-bool VisualToolClip::InitializeHold() {
-	return true;
+bool VisualToolClip::InitializeHold()
+{
+    return true;
 }
 
-void VisualToolClip::UpdateHold() {
-	// Limit to video area
-	cur_1 = video_pos.Max((video_pos + video_res).Min(drag_start));
-	cur_2 = video_pos.Max((video_pos + video_res).Min(mouse_pos));
+void VisualToolClip::UpdateHold()
+{
+    // Limit to video area
+    cur_1 = video_pos.Max((video_pos + video_res).Min(drag_start));
+    cur_2 = video_pos.Max((video_pos + video_res).Min(mouse_pos));
 
-	SetFeaturePositions();
-	CommitHold();
+    SetFeaturePositions();
+    CommitHold();
 }
 
-void VisualToolClip::CommitHold() {
-	std::string value = agi::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") != std::string::npos;
-		SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
-	}
+void VisualToolClip::CommitHold()
+{
+    std::string value = agi::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") != std::string::npos;
+        SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
+    }
 }
 
-void VisualToolClip::UpdateDrag(ClipCorner *feature) {
-	// Update features which share an edge with the dragged one
-	feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos);
-	feature->vert->pos = Vector2D(feature->pos, feature->vert->pos);
+void VisualToolClip::UpdateDrag(ClipCorner *feature)
+{
+    // Update features which share an edge with the dragged one
+    feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos);
+    feature->vert->pos = Vector2D(feature->pos, feature->vert->pos);
 
-	cur_1 = features.front().pos;
-	cur_2 = features.back().pos;
+    cur_1 = features.front().pos;
+    cur_2 = features.back().pos;
 
-	CommitHold();
+    CommitHold();
 }
 
-void VisualToolClip::SetFeaturePositions() {
-	auto it = features.begin();
-	(it++)->pos = cur_1; // Top-left
-	(it++)->pos = Vector2D(cur_2, cur_1); // Top-right
-	(it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left
-	it->pos = cur_2; // Bottom-right
+void VisualToolClip::SetFeaturePositions()
+{
+    auto it = features.begin();
+    (it++)->pos = cur_1; // Top-left
+    (it++)->pos = Vector2D(cur_2, cur_1); // Top-right
+    (it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left
+    it->pos = cur_2; // Bottom-right
 }
 
-void VisualToolClip::DoRefresh() {
-	if (active_line) {
-		GetLineClip(active_line, cur_1, cur_2, inverse);
-		cur_1 = FromScriptCoords(cur_1);
-		cur_2 = FromScriptCoords(cur_2);
-		SetFeaturePositions();
-	}
+void VisualToolClip::DoRefresh()
+{
+    if (active_line) {
+        GetLineClip(active_line, cur_1, cur_2, inverse);
+        cur_1 = FromScriptCoords(cur_1);
+        cur_2 = FromScriptCoords(cur_2);
+        SetFeaturePositions();
+    }
 }
diff --git a/src/visual_tool_clip.h b/src/visual_tool_clip.h
index 52e4977ad9d469b6d102a5e56c2bbeaf2d45f6ba..6b8c37d271b257987a62bfb36a594e3a21d4a0ba 100644
--- a/src/visual_tool_clip.h
+++ b/src/visual_tool_clip.h
@@ -24,28 +24,28 @@
 
 /// VisualDraggableFeature with siblings
 struct ClipCorner final : public VisualDraggableFeature {
-	ClipCorner *horiz = nullptr; ///< Other corner on this corner's horizontal line
-	ClipCorner *vert = nullptr;  ///< Other corner on this corner's vertical line
-	ClipCorner() { type = DRAG_SMALL_CIRCLE; }
+    ClipCorner *horiz = nullptr; ///< Other corner on this corner's horizontal line
+    ClipCorner *vert = nullptr;  ///< Other corner on this corner's vertical line
+    ClipCorner() { type = DRAG_SMALL_CIRCLE; }
 };
 
 class VisualToolClip final : public VisualTool<ClipCorner> {
-	Vector2D cur_1;
-	Vector2D cur_2;
+    Vector2D cur_1;
+    Vector2D cur_2;
 
-	bool inverse = false; ///< Is this currently in iclip mode?
+    bool inverse = false; ///< Is this currently in iclip mode?
 
-	bool InitializeHold() override;
-	void UpdateHold() override;
-	void CommitHold();
+    bool InitializeHold() override;
+    void UpdateHold() override;
+    void CommitHold();
 
-	void DoRefresh() override;
-	void SetFeaturePositions();
+    void DoRefresh() override;
+    void SetFeaturePositions();
 
-	bool InitializeDrag(ClipCorner *feature) override { return true; }
-	void UpdateDrag(ClipCorner *feature) override;
+    bool InitializeDrag(ClipCorner *feature) override { return true; }
+    void UpdateDrag(ClipCorner *feature) override;
 
-	void Draw() override;
+    void Draw() override;
 public:
-	VisualToolClip(VideoDisplay *parent, agi::Context *context);
+    VisualToolClip(VideoDisplay *parent, agi::Context *context);
 };
diff --git a/src/visual_tool_cross.cpp b/src/visual_tool_cross.cpp
index ffbf5329b3e236be8b1cfd50b271cdb30663232b..85aadb782c7d9b78391b99aabe23b420c0d9fb23 100644
--- a/src/visual_tool_cross.cpp
+++ b/src/visual_tool_cross.cpp
@@ -30,76 +30,80 @@
 #include <libaegisub/make_unique.h>
 
 VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualDraggableFeature>(parent, context)
-, gl_text(agi::make_unique<OpenGLText>())
+    : VisualTool<VisualDraggableFeature>(parent, context)
+    , gl_text(agi::make_unique<OpenGLText>())
 {
-	parent->SetCursor(wxCursor(wxCURSOR_BLANK));
+    parent->SetCursor(wxCursor(wxCURSOR_BLANK));
 }
 
-VisualToolCross::~VisualToolCross() {
-	parent->SetCursor(wxNullCursor);
+VisualToolCross::~VisualToolCross()
+{
+    parent->SetCursor(wxNullCursor);
 }
 
-void VisualToolCross::OnDoubleClick() {
-	Vector2D d = ToScriptCoords(mouse_pos) - GetLinePosition(active_line);
-
-	for (auto line : c->selectionController->GetSelectedSet()) {
-		Vector2D p1, p2;
-		int t1, t2;
-		if (GetLineMove(line, p1, p2, t1, t2)) {
-			if (t1 > 0 || t2 > 0)
-				SetOverride(line, "\\move", agi::format("(%s,%s,%d,%d)", Text(p1 + d), Text(p2 + d), t1, t2));
-			else
-				SetOverride(line, "\\move", agi::format("(%s,%s)", Text(p1 + d), Text(p2 + d)));
-		}
-		else
-			SetOverride(line, "\\pos", "(" + Text(GetLinePosition(line) + d) + ")");
-
-		if (Vector2D org = GetLineOrigin(line))
-			SetOverride(line, "\\org", "(" + Text(org + d) + ")");
-	}
-
-	Commit(_("positioning"));
+void VisualToolCross::OnDoubleClick()
+{
+    Vector2D d = ToScriptCoords(mouse_pos) - GetLinePosition(active_line);
+
+    for (auto line : c->selectionController->GetSelectedSet()) {
+        Vector2D p1, p2;
+        int t1, t2;
+        if (GetLineMove(line, p1, p2, t1, t2)) {
+            if (t1 > 0 || t2 > 0)
+                SetOverride(line, "\\move", agi::format("(%s,%s,%d,%d)", Text(p1 + d), Text(p2 + d), t1, t2));
+            else
+                SetOverride(line, "\\move", agi::format("(%s,%s)", Text(p1 + d), Text(p2 + d)));
+        }
+        else
+            SetOverride(line, "\\pos", "(" + Text(GetLinePosition(line) + d) + ")");
+
+        if (Vector2D org = GetLineOrigin(line))
+            SetOverride(line, "\\org", "(" + Text(org + d) + ")");
+    }
+
+    Commit(_("positioning"));
 }
 
-void VisualToolCross::Draw() {
-	if (!mouse_pos) return;
-
-	// Draw cross
-	gl.SetInvert();
-	gl.SetLineColour(*wxWHITE, 1.0, 1);
-	float lines[] = {
-		0.f, mouse_pos.Y(),
-		video_res.X() + video_pos.X() * 2, mouse_pos.Y(),
-		mouse_pos.X(), 0.f,
-		mouse_pos.X(), video_res.Y() + video_pos.Y() * 2
-	};
-	gl.DrawLines(2, lines, 4);
-	gl.ClearInvert();
-
-	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(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
-	int dx = mouse_pos.X();
-	int dy = mouse_pos.Y();
-	if (dx > video_res.X() / 2)
-		dx -= tw + 4;
-	else
-		dx += 4;
-
-	if (dy < video_res.Y() / 2)
-		dy += 3;
-	else
-		dy -= th + 3;
-
-	gl_text->Print(mouse_text, dx, dy);
+void VisualToolCross::Draw()
+{
+    if (!mouse_pos) return;
+
+    // Draw cross
+    gl.SetInvert();
+    gl.SetLineColour(*wxWHITE, 1.0, 1);
+    float lines[] = {
+        0.f, mouse_pos.Y(),
+        video_res.X() + video_pos.X() * 2, mouse_pos.Y(),
+        mouse_pos.X(), 0.f,
+        mouse_pos.X(), video_res.Y() + video_pos.Y() * 2
+    };
+    gl.DrawLines(2, lines, 4);
+    gl.ClearInvert();
+
+    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(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
+    int dx = mouse_pos.X();
+    int dy = mouse_pos.Y();
+    if (dx > video_res.X() / 2)
+        dx -= tw + 4;
+    else
+        dx += 4;
+
+    if (dy < video_res.Y() / 2)
+        dy += 3;
+    else
+        dy -= th + 3;
+
+    gl_text->Print(mouse_text, dx, dy);
 }
 
-std::string VisualToolCross::Text(Vector2D v) {
-	return video_res.X() > script_res.X() ? v.Str() : v.DStr();
+std::string VisualToolCross::Text(Vector2D v)
+{
+    return video_res.X() > script_res.X() ? v.Str() : v.DStr();
 }
diff --git a/src/visual_tool_cross.h b/src/visual_tool_cross.h
index 8172a40f8ef5b82e4f67ef77b95540a899ff8312..68ad137bae79e5f00a3bc0cb85369419d1b84f74 100644
--- a/src/visual_tool_cross.h
+++ b/src/visual_tool_cross.h
@@ -30,12 +30,12 @@ class OpenGLText;
 /// @brief A crosshair which shows the current mouse position and on double-click
 ///        shifts the selected lines to the clicked point
 class VisualToolCross final : public VisualTool<VisualDraggableFeature> {
-	std::unique_ptr<OpenGLText> gl_text;
+    std::unique_ptr<OpenGLText> gl_text;
 
-	void OnDoubleClick() override;
-	void Draw() override;
-	std::string Text(Vector2D v);
+    void OnDoubleClick() override;
+    void Draw() override;
+    std::string Text(Vector2D v);
 public:
-	VisualToolCross(VideoDisplay *parent, agi::Context *context);
-	~VisualToolCross();
+    VisualToolCross(VideoDisplay *parent, agi::Context *context);
+    ~VisualToolCross();
 };
diff --git a/src/visual_tool_drag.cpp b/src/visual_tool_drag.cpp
index 9d8aa6594b27f72c980389c5b2ee89036f2c7bcb..79a0d48f02039c4021aee811aba38e5cdd406f34 100644
--- a/src/visual_tool_drag.cpp
+++ b/src/visual_tool_drag.cpp
@@ -45,289 +45,303 @@ static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE;
 #define ICON(name) (OPT_GET("App/Toolbar Icon Size")->GetInt() == 16 ? GETIMAGE(name ## _16) : GETIMAGE(name ## _24))
 
 VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualToolDragDraggableFeature>(parent, context)
+    : VisualTool<VisualToolDragDraggableFeature>(parent, context)
 {
-	connections.push_back(c->selectionController->AddSelectionListener(&VisualToolDrag::OnSelectedSetChanged, this));
-	auto const& sel_set = c->selectionController->GetSelectedSet();
-	selection.insert(begin(selection), begin(sel_set), end(sel_set));
+    connections.push_back(c->selectionController->AddSelectionListener(&VisualToolDrag::OnSelectedSetChanged, this));
+    auto const &sel_set = c->selectionController->GetSelectedSet();
+    selection.insert(begin(selection), begin(sel_set), end(sel_set));
 }
 
-void VisualToolDrag::SetToolbar(wxToolBar *tb) {
-	toolbar = tb;
-	toolbar->AddTool(-1, _("Toggle between \\move and \\pos"), ICON(visual_move_conv_move));
-	toolbar->Realize();
-	toolbar->Show(true);
+void VisualToolDrag::SetToolbar(wxToolBar *tb)
+{
+    toolbar = tb;
+    toolbar->AddTool(-1, _("Toggle between \\move and \\pos"), ICON(visual_move_conv_move));
+    toolbar->Realize();
+    toolbar->Show(true);
 
-	toolbar->Bind(wxEVT_TOOL, &VisualToolDrag::OnSubTool, this);
+    toolbar->Bind(wxEVT_TOOL, &VisualToolDrag::OnSubTool, this);
 }
 
-void VisualToolDrag::UpdateToggleButtons() {
-	bool to_move = true;
-	if (active_line) {
-		Vector2D p1, p2;
-		int t1, t2;
-		to_move = !GetLineMove(active_line, p1, p2, t1, t2);
-	}
-
-	if (to_move == button_is_move) return;
-
-	toolbar->SetToolNormalBitmap(toolbar->GetToolByPos(0)->GetId(),
-		to_move ? ICON(visual_move_conv_move) : ICON(visual_move_conv_pos));
-	button_is_move = to_move;
+void VisualToolDrag::UpdateToggleButtons()
+{
+    bool to_move = true;
+    if (active_line) {
+        Vector2D p1, p2;
+        int t1, t2;
+        to_move = !GetLineMove(active_line, p1, p2, t1, t2);
+    }
+
+    if (to_move == button_is_move) return;
+
+    toolbar->SetToolNormalBitmap(toolbar->GetToolByPos(0)->GetId(),
+                                 to_move ? ICON(visual_move_conv_move) : ICON(visual_move_conv_pos));
+    button_is_move = to_move;
 }
 
-void VisualToolDrag::OnSubTool(wxCommandEvent &) {
-	// Toggle \move <-> \pos
-	VideoController *vc = c->videoController.get();
-	for (auto line : selection) {
-		Vector2D p1, p2;
-		int t1, t2;
-
-		bool has_move = GetLineMove(line, p1, p2, t1, t2);
-
-		if (has_move)
-			SetOverride(line, "\\pos", p1.PStr());
-		else {
-			p1 = GetLinePosition(line);
-			// 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", agi::format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end));
-		}
-	}
-
-	Commit();
-	OnFileChanged();
-	UpdateToggleButtons();
+void VisualToolDrag::OnSubTool(wxCommandEvent &)
+{
+    // Toggle \move <-> \pos
+    VideoController *vc = c->videoController.get();
+    for (auto line : selection) {
+        Vector2D p1, p2;
+        int t1, t2;
+
+        bool has_move = GetLineMove(line, p1, p2, t1, t2);
+
+        if (has_move)
+            SetOverride(line, "\\pos", p1.PStr());
+        else {
+            p1 = GetLinePosition(line);
+            // 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", agi::format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end));
+        }
+    }
+
+    Commit();
+    OnFileChanged();
+    UpdateToggleButtons();
 }
 
-void VisualToolDrag::OnLineChanged() {
-	UpdateToggleButtons();
+void VisualToolDrag::OnLineChanged()
+{
+    UpdateToggleButtons();
 }
 
-void VisualToolDrag::OnFileChanged() {
-	/// @todo it should be possible to preserve the selection in some cases
-	features.clear();
-	sel_features.clear();
-	primary = nullptr;
-	active_feature = nullptr;
-
-	for (auto& diag : c->ass->Events) {
-		if (IsDisplayed(&diag))
-			MakeFeatures(&diag);
-	}
-
-	UpdateToggleButtons();
+void VisualToolDrag::OnFileChanged()
+{
+    /// @todo it should be possible to preserve the selection in some cases
+    features.clear();
+    sel_features.clear();
+    primary = nullptr;
+    active_feature = nullptr;
+
+    for (auto &diag : c->ass->Events) {
+        if (IsDisplayed(&diag))
+            MakeFeatures(&diag);
+    }
+
+    UpdateToggleButtons();
 }
 
-void VisualToolDrag::OnFrameChanged() {
-	if (primary && !IsDisplayed(primary->line))
-		primary = nullptr;
-
-	auto feat = features.begin();
-	auto end = features.end();
-
-	for (auto& diag : c->ass->Events) {
-		if (IsDisplayed(&diag)) {
-			// Features don't exist and should
-			if (feat == end || feat->line != &diag)
-				MakeFeatures(&diag, feat);
-			// Move past already existing features for the line
-			else
-				while (feat != end && feat->line == &diag) ++feat;
-		}
-		else {
-			// Remove all features for this line (if any)
-			while (feat != end && feat->line == &diag) {
-				if (&*feat == active_feature) active_feature = nullptr;
-				feat->line = nullptr;
-				RemoveSelection(&*feat);
-				feat = features.erase(feat);
-			}
-		}
-	}
+void VisualToolDrag::OnFrameChanged()
+{
+    if (primary && !IsDisplayed(primary->line))
+        primary = nullptr;
+
+    auto feat = features.begin();
+    auto end = features.end();
+
+    for (auto &diag : c->ass->Events) {
+        if (IsDisplayed(&diag)) {
+            // Features don't exist and should
+            if (feat == end || feat->line != &diag)
+                MakeFeatures(&diag, feat);
+            // Move past already existing features for the line
+            else
+                while (feat != end && feat->line == &diag) ++feat;
+        }
+        else {
+            // Remove all features for this line (if any)
+            while (feat != end && feat->line == &diag) {
+                if (&*feat == active_feature) active_feature = nullptr;
+                feat->line = nullptr;
+                RemoveSelection(&*feat);
+                feat = features.erase(feat);
+            }
+        }
+    }
 }
 
-template<class C, class T> static bool line_not_present(C const& set, T const& it) {
-	return std::none_of(set.begin(), set.end(), [&](typename C::value_type const& cmp) {
-		return cmp->line == it->line;
-	});
+template<class C, class T> static bool line_not_present(C const &set, T const &it)
+{
+    return std::none_of(set.begin(), set.end(), [&](typename C::value_type const & cmp) {
+        return cmp->line == it->line;
+    });
 }
 
-void VisualToolDrag::OnSelectedSetChanged() {
-	auto const& new_sel_set = c->selectionController->GetSelectedSet();
-	std::vector<AssDialogue *> new_sel(begin(new_sel_set), end(new_sel_set));
-
-	bool any_changed = false;
-	for (auto it = features.begin(); it != features.end(); ) {
-		bool was_selected = boost::binary_search(selection, it->line);
-		bool is_selected = boost::binary_search(new_sel, it->line);
-		if (was_selected && !is_selected) {
-			sel_features.erase(&*it++);
-			any_changed = true;
-		}
-		else {
-			if (is_selected && !was_selected && it->type == DRAG_START && line_not_present(sel_features, it)) {
-				sel_features.insert(&*it);
-				any_changed = true;
-			}
-			++it;
-		}
-	}
-
-	if (any_changed)
-		parent->Render();
-	selection = std::move(new_sel);
+void VisualToolDrag::OnSelectedSetChanged()
+{
+    auto const &new_sel_set = c->selectionController->GetSelectedSet();
+    std::vector<AssDialogue *> new_sel(begin(new_sel_set), end(new_sel_set));
+
+    bool any_changed = false;
+    for (auto it = features.begin(); it != features.end(); ) {
+        bool was_selected = boost::binary_search(selection, it->line);
+        bool is_selected = boost::binary_search(new_sel, it->line);
+        if (was_selected && !is_selected) {
+            sel_features.erase(&*it++);
+            any_changed = true;
+        }
+        else {
+            if (is_selected && !was_selected && it->type == DRAG_START && line_not_present(sel_features, it)) {
+                sel_features.insert(&*it);
+                any_changed = true;
+            }
+            ++it;
+        }
+    }
+
+    if (any_changed)
+        parent->Render();
+    selection = std::move(new_sel);
 }
 
-void VisualToolDrag::Draw() {
-	DrawAllFeatures();
-
-	// Load colors from options
-	wxColour line_color = to_wx(line_color_primary_opt->GetColor());
-
-	// Draw connecting lines
-	for (auto& feature : features) {
-		if (feature.type == DRAG_START) continue;
-
-		Feature *p2 = &feature;
-		Feature *p1 = feature.parent;
-
-		// Move end marker has an arrow; origin doesn't
-		bool has_arrow = p2->type == DRAG_END;
-		int arrow_len = has_arrow ? 10 : 0;
-
-		// Don't show the connecting line if the features are very close
-		Vector2D direction = p2->pos - p1->pos;
-		if (direction.SquareLen() < (20 + arrow_len) * (20 + arrow_len)) continue;
-
-		direction = direction.Unit();
-		// Get the start and end points of the line
-		Vector2D start = p1->pos + direction * 10;
-		Vector2D end = p2->pos - direction * (10 + arrow_len);
-
-		if (has_arrow) {
-			gl.SetLineColour(line_color, 0.8f, 2);
-
-			// Arrow line
-			gl.DrawLine(start, end);
-
-			// Arrow head
-			Vector2D t_half_base_w = Vector2D(-direction.Y(), direction.X()) * 4;
-			gl.DrawTriangle(end + direction * arrow_len, end + t_half_base_w, end - t_half_base_w);
-		}
-		// Draw dashed line
-		else {
-			gl.SetLineColour(line_color, 0.5f, 2);
-			gl.DrawDashedLine(start, end, 6);
-		}
-	}
+void VisualToolDrag::Draw()
+{
+    DrawAllFeatures();
+
+    // Load colors from options
+    wxColour line_color = to_wx(line_color_primary_opt->GetColor());
+
+    // Draw connecting lines
+    for (auto &feature : features) {
+        if (feature.type == DRAG_START) continue;
+
+        Feature *p2 = &feature;
+        Feature *p1 = feature.parent;
+
+        // Move end marker has an arrow; origin doesn't
+        bool has_arrow = p2->type == DRAG_END;
+        int arrow_len = has_arrow ? 10 : 0;
+
+        // Don't show the connecting line if the features are very close
+        Vector2D direction = p2->pos - p1->pos;
+        if (direction.SquareLen() < (20 + arrow_len) * (20 + arrow_len)) continue;
+
+        direction = direction.Unit();
+        // Get the start and end points of the line
+        Vector2D start = p1->pos + direction * 10;
+        Vector2D end = p2->pos - direction * (10 + arrow_len);
+
+        if (has_arrow) {
+            gl.SetLineColour(line_color, 0.8f, 2);
+
+            // Arrow line
+            gl.DrawLine(start, end);
+
+            // Arrow head
+            Vector2D t_half_base_w = Vector2D(-direction.Y(), direction.X()) * 4;
+            gl.DrawTriangle(end + direction * arrow_len, end + t_half_base_w, end - t_half_base_w);
+        }
+        // Draw dashed line
+        else {
+            gl.SetLineColour(line_color, 0.5f, 2);
+            gl.DrawDashedLine(start, end, 6);
+        }
+    }
 }
 
-void VisualToolDrag::MakeFeatures(AssDialogue *diag) {
-	MakeFeatures(diag, features.end());
+void VisualToolDrag::MakeFeatures(AssDialogue *diag)
+{
+    MakeFeatures(diag, features.end());
 }
 
-void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_list::iterator pos) {
-	Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
-
-	// Create \pos feature
-	auto feat = agi::make_unique<Feature>();
-	auto parent = feat.get();
-	feat->pos = p1;
-	feat->type = DRAG_START;
-	feat->line = diag;
-
-	if (boost::binary_search(selection, diag))
-		sel_features.insert(feat.get());
-	features.insert(pos, *feat.release());
-
-	Vector2D p2;
-	int t1, t2;
-
-	// Create move destination feature
-	if (GetLineMove(diag, p1, p2, t1, t2)) {
-		feat = agi::make_unique<Feature>();
-		feat->pos = FromScriptCoords(p2);
-		feat->layer = 1;
-		feat->type = DRAG_END;
-		feat->time = t2;
-		feat->line = diag;
-		feat->parent = parent;
-
-		parent->time = t1;
-		parent->parent = feat.get();
-
-		features.insert(pos, *feat.release());
-	}
-
-	// Create org feature
-	if (Vector2D org = GetLineOrigin(diag)) {
-		feat = agi::make_unique<Feature>();
-		feat->pos = FromScriptCoords(org);
-		feat->layer = -1;
-		feat->type = DRAG_ORIGIN;
-		feat->time = 0;
-		feat->line = diag;
-		feat->parent = parent;
-		features.insert(pos, *feat.release());
-	}
+void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_list::iterator pos)
+{
+    Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
+
+    // Create \pos feature
+    auto feat = agi::make_unique<Feature>();
+    auto parent = feat.get();
+    feat->pos = p1;
+    feat->type = DRAG_START;
+    feat->line = diag;
+
+    if (boost::binary_search(selection, diag))
+        sel_features.insert(feat.get());
+    features.insert(pos, *feat.release());
+
+    Vector2D p2;
+    int t1, t2;
+
+    // Create move destination feature
+    if (GetLineMove(diag, p1, p2, t1, t2)) {
+        feat = agi::make_unique<Feature>();
+        feat->pos = FromScriptCoords(p2);
+        feat->layer = 1;
+        feat->type = DRAG_END;
+        feat->time = t2;
+        feat->line = diag;
+        feat->parent = parent;
+
+        parent->time = t1;
+        parent->parent = feat.get();
+
+        features.insert(pos, *feat.release());
+    }
+
+    // Create org feature
+    if (Vector2D org = GetLineOrigin(diag)) {
+        feat = agi::make_unique<Feature>();
+        feat->pos = FromScriptCoords(org);
+        feat->layer = -1;
+        feat->type = DRAG_ORIGIN;
+        feat->time = 0;
+        feat->line = diag;
+        feat->parent = parent;
+        features.insert(pos, *feat.release());
+    }
 }
 
-bool VisualToolDrag::InitializeDrag(Feature *feature) {
-	primary = feature;
-
-	// Set time of clicked feature to the current frame and shift all other
-	// selected features by the same amount
-	if (feature->type != DRAG_ORIGIN) {
-		int time = c->videoController->TimeAtFrame(frame_number) - feature->line->Start;
-		int change = time - feature->time;
-
-		for (auto feat : sel_features)
-			feat->time += change;
-	}
-	return true;
+bool VisualToolDrag::InitializeDrag(Feature *feature)
+{
+    primary = feature;
+
+    // Set time of clicked feature to the current frame and shift all other
+    // selected features by the same amount
+    if (feature->type != DRAG_ORIGIN) {
+        int time = c->videoController->TimeAtFrame(frame_number) - feature->line->Start;
+        int change = time - feature->time;
+
+        for (auto feat : sel_features)
+            feat->time += change;
+    }
+    return true;
 }
 
-void VisualToolDrag::UpdateDrag(Feature *feature) {
-	if (feature->type == DRAG_ORIGIN) {
-		SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
-		return;
-	}
-
-	Feature *end_feature = feature->parent;
-	if (feature->type == DRAG_END)
-		std::swap(feature, end_feature);
-
-	if (!feature->parent)
-		SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr());
-	else
-		SetOverride(feature->line, "\\move", agi::format("(%s,%s,%d,%d)"
-			, ToScriptCoords(feature->pos).Str()
-			, ToScriptCoords(end_feature->pos).Str()
-			, feature->time , end_feature->time));
+void VisualToolDrag::UpdateDrag(Feature *feature)
+{
+    if (feature->type == DRAG_ORIGIN) {
+        SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
+        return;
+    }
+
+    Feature *end_feature = feature->parent;
+    if (feature->type == DRAG_END)
+        std::swap(feature, end_feature);
+
+    if (!feature->parent)
+        SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr());
+    else
+        SetOverride(feature->line, "\\move", agi::format("(%s,%s,%d,%d)"
+                    , ToScriptCoords(feature->pos).Str()
+                    , ToScriptCoords(end_feature->pos).Str()
+                    , feature->time, end_feature->time));
 }
 
-void VisualToolDrag::OnDoubleClick() {
-	Vector2D d = ToScriptCoords(mouse_pos) - (primary ? ToScriptCoords(primary->pos) : GetLinePosition(active_line));
-
-	for (auto line : c->selectionController->GetSelectedSet()) {
-		Vector2D p1, p2;
-		int t1, t2;
-		if (GetLineMove(line, p1, p2, t1, t2)) {
-			if (t1 > 0 || t2 > 0)
-				SetOverride(line, "\\move", agi::format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2));
-			else
-				SetOverride(line, "\\move", agi::format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str()));
-		}
-		else
-			SetOverride(line, "\\pos", (GetLinePosition(line) + d).PStr());
-
-		if (Vector2D org = GetLineOrigin(line))
-			SetOverride(line, "\\org", (org + d).PStr());
-	}
-
-	Commit(_("positioning"));
-
-	OnFileChanged();
+void VisualToolDrag::OnDoubleClick()
+{
+    Vector2D d = ToScriptCoords(mouse_pos) - (primary ? ToScriptCoords(primary->pos) : GetLinePosition(active_line));
+
+    for (auto line : c->selectionController->GetSelectedSet()) {
+        Vector2D p1, p2;
+        int t1, t2;
+        if (GetLineMove(line, p1, p2, t1, t2)) {
+            if (t1 > 0 || t2 > 0)
+                SetOverride(line, "\\move", agi::format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2));
+            else
+                SetOverride(line, "\\move", agi::format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str()));
+        }
+        else
+            SetOverride(line, "\\pos", (GetLinePosition(line) + d).PStr());
+
+        if (Vector2D org = GetLineOrigin(line))
+            SetOverride(line, "\\org", (org + d).PStr());
+    }
+
+    Commit(_("positioning"));
+
+    OnFileChanged();
 }
diff --git a/src/visual_tool_drag.h b/src/visual_tool_drag.h
index d869d37e82e6aa3df5ec3820068005b53a81403d..f6fcacbe238a06840cb68dd404626ceca2ac3d0f 100644
--- a/src/visual_tool_drag.h
+++ b/src/visual_tool_drag.h
@@ -26,8 +26,8 @@
 /// @brief VisualDraggableFeature with a time value
 class VisualToolDragDraggableFeature final : public VisualDraggableFeature {
 public:
-	int time = 0;
-	VisualToolDragDraggableFeature *parent = nullptr;
+    int time = 0;
+    VisualToolDragDraggableFeature *parent = nullptr;
 };
 
 class wxBitmapButton;
@@ -37,42 +37,42 @@ class wxToolBar;
 /// @class VisualToolDrag
 /// @brief Moveable features for the positions of each visible line
 class VisualToolDrag final : public VisualTool<VisualToolDragDraggableFeature> {
-	/// The subtoolbar for the move/pos conversion button
-	wxToolBar *toolbar;
-	/// The feature last clicked on for the double-click handler
-	/// Equal to curFeature during drags; possibly different at all other times
-	/// nullptr if no features have been clicked on or the last clicked on one no
-	/// longer exists
-	Feature *primary = nullptr;
-	/// The last announced selection set
-	std::vector<AssDialogue *> selection;
+    /// The subtoolbar for the move/pos conversion button
+    wxToolBar *toolbar;
+    /// The feature last clicked on for the double-click handler
+    /// Equal to curFeature during drags; possibly different at all other times
+    /// nullptr if no features have been clicked on or the last clicked on one no
+    /// longer exists
+    Feature *primary = nullptr;
+    /// The last announced selection set
+    std::vector<AssDialogue *> selection;
 
-	/// When the button is pressed, will it convert the line to a move (vs. from
-	/// move to pos)? Used to avoid changing the button's icon unnecessarily
-	bool button_is_move = false;
+    /// When the button is pressed, will it convert the line to a move (vs. from
+    /// move to pos)? Used to avoid changing the button's icon unnecessarily
+    bool button_is_move = false;
 
-	/// @brief Create the features for a line
-	/// @param diag Line to create the features for
-	/// @param pos Insertion point in the feature list
-	void MakeFeatures(AssDialogue *diag, feature_list::iterator pos);
-	void MakeFeatures(AssDialogue *diag);
+    /// @brief Create the features for a line
+    /// @param diag Line to create the features for
+    /// @param pos Insertion point in the feature list
+    void MakeFeatures(AssDialogue *diag, feature_list::iterator pos);
+    void MakeFeatures(AssDialogue *diag);
 
-	void OnSelectedSetChanged();
+    void OnSelectedSetChanged();
 
-	void OnFrameChanged() override;
-	void OnFileChanged() override;
-	void OnLineChanged() override;
-	void OnCoordinateSystemsChanged() override { OnFileChanged(); }
+    void OnFrameChanged() override;
+    void OnFileChanged() override;
+    void OnLineChanged() override;
+    void OnCoordinateSystemsChanged() override { OnFileChanged(); }
 
-	bool InitializeDrag(Feature *feature) override;
-	void UpdateDrag(Feature *feature) override;
-	void Draw() override;
-	void OnDoubleClick() override;
+    bool InitializeDrag(Feature *feature) override;
+    void UpdateDrag(Feature *feature) override;
+    void Draw() override;
+    void OnDoubleClick() override;
 
-	/// Set the pos/move button to the correct icon based on the active line
-	void UpdateToggleButtons();
-	void OnSubTool(wxCommandEvent &event);
+    /// Set the pos/move button to the correct icon based on the active line
+    void UpdateToggleButtons();
+    void OnSubTool(wxCommandEvent &event);
 public:
-	VisualToolDrag(VideoDisplay *parent, agi::Context *context);
-	void SetToolbar(wxToolBar *tb) override;
+    VisualToolDrag(VideoDisplay *parent, agi::Context *context);
+    void SetToolbar(wxToolBar *tb) override;
 };
diff --git a/src/visual_tool_rotatexy.cpp b/src/visual_tool_rotatexy.cpp
index acb389acd67f651097c7a6cac60a0b2ea53f67d4..f24972d0c994df8f7d5f9bf988ab891f0a18c470 100644
--- a/src/visual_tool_rotatexy.cpp
+++ b/src/visual_tool_rotatexy.cpp
@@ -31,170 +31,175 @@
 #include <wx/colour.h>
 
 VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualDraggableFeature>(parent, context)
+    : VisualTool<VisualDraggableFeature>(parent, context)
 {
-	org = new Feature;
-	org->type = DRAG_BIG_TRIANGLE;
-	features.push_back(*org);
+    org = new Feature;
+    org->type = DRAG_BIG_TRIANGLE;
+    features.push_back(*org);
 }
 
-void VisualToolRotateXY::Draw() {
-	if (!active_line) return;
-
-	DrawAllFeatures();
-
-	// Load colors from options
-	wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
-	wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
-
-	// Transform grid
-	gl.SetOrigin(org->pos);
-	gl.SetRotation(angle_x, angle_y, angle_z);
-	gl.SetShear(fax, fay);
-
-	// Draw grid
-	gl.SetLineColour(line_color_secondary, 0.5f, 2);
-	gl.SetModeLine();
-	float r = line_color_secondary.Red() / 255.f;
-	float g = line_color_secondary.Green() / 255.f;
-	float b = line_color_secondary.Blue() / 255.f;
-
-	// Number of lines on each side of each axis
-	static const int radius = 15;
-	// Total number of lines, including center axis line
-	static const int line_count = radius * 2 + 1;
-	// Distance between each line in pixels
-	static const int spacing = 20;
-	// Length of each grid line in pixels from axis to one end
-	static const int half_line_length = spacing * (radius + 1);
-	static const float fade_factor = 0.9f / radius;
-
-	std::vector<float> colors(line_count * 8 * 4);
-	for (int i = 0; i < line_count * 8; ++i) {
-		colors[i * 4 + 0] = r;
-		colors[i * 4 + 1] = g;
-		colors[i * 4 + 2] = b;
-		colors[i * 4 + 3] = (i + 3) % 4 > 1 ? 0 : (1.f - abs(i / 8 - radius) * fade_factor);
-	}
-
-	std::vector<float> points(line_count * 8 * 2);
-	for (int i = 0; i < line_count; ++i) {
-		int pos = spacing * (i - radius);
-
-		points[i * 16 + 0] = pos;
-		points[i * 16 + 1] = half_line_length;
-
-		points[i * 16 + 2] = pos;
-		points[i * 16 + 3] = 0;
-
-		points[i * 16 + 4] = pos;
-		points[i * 16 + 5] = 0;
-
-		points[i * 16 + 6] = pos;
-		points[i * 16 + 7] = -half_line_length;
-
-		points[i * 16 + 8] = half_line_length;
-		points[i * 16 + 9] = pos;
-
-		points[i * 16 + 10] = 0;
-		points[i * 16 + 11] = pos;
-
-		points[i * 16 + 12] = 0;
-		points[i * 16 + 13] = pos;
-
-		points[i * 16 + 14] = -half_line_length;
-		points[i * 16 + 15] = pos;
-	}
-
-	gl.DrawLines(2, points, 4, colors);
-
-	// Draw vectors
-	gl.SetLineColour(line_color_primary, 1.f, 2);
-	float vectors[] = {
-		0.f, 0.f, 0.f,
-		50.f, 0.f, 0.f,
-		0.f, 0.f, 0.f,
-		0.f, 50.f, 0.f,
-		0.f, 0.f, 0.f,
-		0.f, 0.f, 50.f,
-	};
-	gl.DrawLines(3, vectors, 6);
-
-	// Draw arrow tops
-	float arrows[] = {
-		60.f,  0.f,  0.f,
-		50.f, -3.f, -3.f,
-		50.f,  3.f, -3.f,
-		50.f,  3.f,  3.f,
-		50.f, -3.f,  3.f,
-		50.f, -3.f, -3.f,
-
-		 0.f, 60.f,  0.f,
-		-3.f, 50.f, -3.f,
-		 3.f, 50.f, -3.f,
-		 3.f, 50.f,  3.f,
-		-3.f, 50.f,  3.f,
-		-3.f, 50.f, -3.f,
-
-		 0.f,  0.f, 60.f,
-		-3.f, -3.f, 50.f,
-		 3.f, -3.f, 50.f,
-		 3.f,  3.f, 50.f,
-		-3.f,  3.f, 50.f,
-		-3.f, -3.f, 50.f,
-	};
-
-	gl.DrawLines(3, arrows, 18);
-
-	gl.ResetTransform();
+void VisualToolRotateXY::Draw()
+{
+    if (!active_line) return;
+
+    DrawAllFeatures();
+
+    // Load colors from options
+    wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
+    wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
+
+    // Transform grid
+    gl.SetOrigin(org->pos);
+    gl.SetRotation(angle_x, angle_y, angle_z);
+    gl.SetShear(fax, fay);
+
+    // Draw grid
+    gl.SetLineColour(line_color_secondary, 0.5f, 2);
+    gl.SetModeLine();
+    float r = line_color_secondary.Red() / 255.f;
+    float g = line_color_secondary.Green() / 255.f;
+    float b = line_color_secondary.Blue() / 255.f;
+
+    // Number of lines on each side of each axis
+    static const int radius = 15;
+    // Total number of lines, including center axis line
+    static const int line_count = radius * 2 + 1;
+    // Distance between each line in pixels
+    static const int spacing = 20;
+    // Length of each grid line in pixels from axis to one end
+    static const int half_line_length = spacing * (radius + 1);
+    static const float fade_factor = 0.9f / radius;
+
+    std::vector<float> colors(line_count * 8 * 4);
+    for (int i = 0; i < line_count * 8; ++i) {
+        colors[i * 4 + 0] = r;
+        colors[i * 4 + 1] = g;
+        colors[i * 4 + 2] = b;
+        colors[i * 4 + 3] = (i + 3) % 4 > 1 ? 0 : (1.f - abs(i / 8 - radius) * fade_factor);
+    }
+
+    std::vector<float> points(line_count * 8 * 2);
+    for (int i = 0; i < line_count; ++i) {
+        int pos = spacing * (i - radius);
+
+        points[i * 16 + 0] = pos;
+        points[i * 16 + 1] = half_line_length;
+
+        points[i * 16 + 2] = pos;
+        points[i * 16 + 3] = 0;
+
+        points[i * 16 + 4] = pos;
+        points[i * 16 + 5] = 0;
+
+        points[i * 16 + 6] = pos;
+        points[i * 16 + 7] = -half_line_length;
+
+        points[i * 16 + 8] = half_line_length;
+        points[i * 16 + 9] = pos;
+
+        points[i * 16 + 10] = 0;
+        points[i * 16 + 11] = pos;
+
+        points[i * 16 + 12] = 0;
+        points[i * 16 + 13] = pos;
+
+        points[i * 16 + 14] = -half_line_length;
+        points[i * 16 + 15] = pos;
+    }
+
+    gl.DrawLines(2, points, 4, colors);
+
+    // Draw vectors
+    gl.SetLineColour(line_color_primary, 1.f, 2);
+    float vectors[] = {
+        0.f, 0.f, 0.f,
+        50.f, 0.f, 0.f,
+        0.f, 0.f, 0.f,
+        0.f, 50.f, 0.f,
+        0.f, 0.f, 0.f,
+        0.f, 0.f, 50.f,
+    };
+    gl.DrawLines(3, vectors, 6);
+
+    // Draw arrow tops
+    float arrows[] = {
+        60.f,  0.f,  0.f,
+        50.f, -3.f, -3.f,
+        50.f,  3.f, -3.f,
+        50.f,  3.f,  3.f,
+        50.f, -3.f,  3.f,
+        50.f, -3.f, -3.f,
+
+        0.f, 60.f,  0.f,
+        -3.f, 50.f, -3.f,
+        3.f, 50.f, -3.f,
+        3.f, 50.f,  3.f,
+        -3.f, 50.f,  3.f,
+        -3.f, 50.f, -3.f,
+
+        0.f,  0.f, 60.f,
+        -3.f, -3.f, 50.f,
+        3.f, -3.f, 50.f,
+        3.f,  3.f, 50.f,
+        -3.f,  3.f, 50.f,
+        -3.f, -3.f, 50.f,
+    };
+
+    gl.DrawLines(3, arrows, 18);
+
+    gl.ResetTransform();
 }
 
-bool VisualToolRotateXY::InitializeHold() {
-	orig_x = angle_x;
-	orig_y = angle_y;
+bool VisualToolRotateXY::InitializeHold()
+{
+    orig_x = angle_x;
+    orig_y = angle_y;
 
-	return true;
+    return true;
 }
 
-void VisualToolRotateXY::UpdateHold() {
-	Vector2D delta = (mouse_pos - drag_start) * 2;
-	if (shift_down)
-		delta = delta.SingleAxis();
+void VisualToolRotateXY::UpdateHold()
+{
+    Vector2D delta = (mouse_pos - drag_start) * 2;
+    if (shift_down)
+        delta = delta.SingleAxis();
 
-	angle_x = orig_x - delta.Y();
-	angle_y = orig_y + delta.X();
+    angle_x = orig_x - delta.Y();
+    angle_y = orig_y + delta.X();
 
-	if (ctrl_down) {
-		angle_x = floorf(angle_x / 30.f + .5f) * 30.f;
-		angle_y = floorf(angle_y / 30.f + .5f) * 30.f;
-	}
+    if (ctrl_down) {
+        angle_x = floorf(angle_x / 30.f + .5f) * 30.f;
+        angle_y = floorf(angle_y / 30.f + .5f) * 30.f;
+    }
 
-	angle_x = fmodf(angle_x + 360.f, 360.f);
-	angle_y = fmodf(angle_y + 360.f, 360.f);
+    angle_x = fmodf(angle_x + 360.f, 360.f);
+    angle_y = fmodf(angle_y + 360.f, 360.f);
 
-	SetSelectedOverride("\\frx", agi::format("%.4g", angle_x));
-	SetSelectedOverride("\\fry", agi::format("%.4g", angle_y));
+    SetSelectedOverride("\\frx", agi::format("%.4g", angle_x));
+    SetSelectedOverride("\\fry", agi::format("%.4g", angle_y));
 }
 
-void VisualToolRotateXY::UpdateDrag(Feature *feature) {
-	auto org = GetLineOrigin(active_line);
-	if (!org) org = GetLinePosition(active_line);
-	auto d = ToScriptCoords(feature->pos) - org;
-
-	for (auto line : c->selectionController->GetSelectedSet()) {
-		org = GetLineOrigin(line);
-		if (!org) org = GetLinePosition(line);
-		SetOverride(line, "\\org", (d + org).PStr());
-	}
+void VisualToolRotateXY::UpdateDrag(Feature *feature)
+{
+    auto org = GetLineOrigin(active_line);
+    if (!org) org = GetLinePosition(active_line);
+    auto d = ToScriptCoords(feature->pos) - org;
+
+    for (auto line : c->selectionController->GetSelectedSet()) {
+        org = GetLineOrigin(line);
+        if (!org) org = GetLinePosition(line);
+        SetOverride(line, "\\org", (d + org).PStr());
+    }
 }
 
-void VisualToolRotateXY::DoRefresh() {
-	if (!active_line) return;
+void VisualToolRotateXY::DoRefresh()
+{
+    if (!active_line) return;
 
-	if (!(org->pos = GetLineOrigin(active_line)))
-		org->pos = GetLinePosition(active_line);
-	org->pos = FromScriptCoords(org->pos);
+    if (!(org->pos = GetLineOrigin(active_line)))
+        org->pos = GetLinePosition(active_line);
+    org->pos = FromScriptCoords(org->pos);
 
-	GetLineRotation(active_line, angle_x, angle_y, angle_z);
-	GetLineShear(active_line, fax, fay);
+    GetLineRotation(active_line, angle_x, angle_y, angle_z);
+    GetLineShear(active_line, fax, fay);
 }
diff --git a/src/visual_tool_rotatexy.h b/src/visual_tool_rotatexy.h
index a4c00a3732c6a60ac40372302124a916c317a808..c657eaed45752aff4e90a08e35adfb76d37e26ad 100644
--- a/src/visual_tool_rotatexy.h
+++ b/src/visual_tool_rotatexy.h
@@ -23,23 +23,23 @@
 #include "visual_tool.h"
 
 class VisualToolRotateXY final : public VisualTool<VisualDraggableFeature> {
-	float angle_x = 0.f; /// Current x rotation
-	float angle_y = 0.f; /// Current y rotation
-	float angle_z = 0.f; /// Current z rotation
+    float angle_x = 0.f; /// Current x rotation
+    float angle_y = 0.f; /// Current y rotation
+    float angle_z = 0.f; /// Current z rotation
 
-	float fax = 0.f;
-	float fay = 0.f;
+    float fax = 0.f;
+    float fay = 0.f;
 
-	float orig_x = 0.f; ///< x rotation at the beginning of the current hold
-	float orig_y = 0.f; ///< y rotation at the beginning of the current hold
+    float orig_x = 0.f; ///< x rotation at the beginning of the current hold
+    float orig_y = 0.f; ///< y rotation at the beginning of the current hold
 
-	Feature *org;
+    Feature *org;
 
-	void DoRefresh() override;
-	void Draw() override;
-	void UpdateDrag(Feature *feature) override;
-	bool InitializeHold() override;
-	void UpdateHold() override;
+    void DoRefresh() override;
+    void Draw() override;
+    void UpdateDrag(Feature *feature) override;
+    bool InitializeHold() override;
+    void UpdateHold() override;
 public:
-	VisualToolRotateXY(VideoDisplay *parent, agi::Context *context);
+    VisualToolRotateXY(VideoDisplay *parent, agi::Context *context);
 };
diff --git a/src/visual_tool_rotatez.cpp b/src/visual_tool_rotatez.cpp
index 0d8bf2a9c948e2019b5dc00e1c0469be078a3ded..31105ec690910bd75e988df84f8cc31f0fe3af66 100644
--- a/src/visual_tool_rotatez.cpp
+++ b/src/visual_tool_rotatez.cpp
@@ -34,115 +34,120 @@ static const float deg2rad = 3.1415926536f / 180.f;
 static const float rad2deg = 180.f / 3.1415926536f;
 
 VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualDraggableFeature>(parent, context)
-, org(new Feature)
+    : VisualTool<VisualDraggableFeature>(parent, context)
+    , org(new Feature)
 {
-	features.push_back(*org);
-	org->type = DRAG_BIG_TRIANGLE;
+    features.push_back(*org);
+    org->type = DRAG_BIG_TRIANGLE;
 }
 
-void VisualToolRotateZ::Draw() {
-	if (!active_line) return;
-
-	DrawAllFeatures();
-
-	// Load colors from options
-	wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
-	wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
-	wxColour highlight_color = to_wx(highlight_color_primary_opt->GetColor());
-
-	float radius = (pos - org->pos).Len();
-	float oRadius = radius;
-	if (radius < 50)
-		radius = 50;
-
-	// Set up the projection
-	gl.SetOrigin(org->pos);
-	gl.SetRotation(rotation_x, rotation_y, 0);
-	gl.SetScale(scale);
-
-	// Draw the circle
-	gl.SetLineColour(line_color_secondary);
-	gl.SetFillColour(highlight_color, 0.3f);
-	gl.DrawRing(Vector2D(0, 0), radius + 4, radius - 4);
-
-	// Draw markers around circle
-	int markers = 6;
-	float markStart = -90.f / markers;
-	float markEnd = markStart + (180.f / markers);
-	for (int i = 0; i < markers; ++i) {
-		float angle = i * (360.f / markers);
-		gl.DrawRing(Vector2D(0, 0), radius+30, radius+12, 1.0, angle+markStart, angle+markEnd);
-	}
-
-	// Draw the baseline through the origin showing current rotation
-	Vector2D angle_vec(Vector2D::FromAngle(angle * deg2rad));
-	gl.SetLineColour(line_color_primary, 1, 2);
-	gl.DrawLine(angle_vec * -radius, angle_vec * radius);
-
-	if (org->pos != pos) {
-		Vector2D rotated_pos = Vector2D::FromAngle(angle * deg2rad - (pos - org->pos).Angle()) * oRadius;
-
-		// Draw the line from origin to rotated position
-		gl.DrawLine(Vector2D(), rotated_pos);
-
-		// Draw the line under the text
-		gl.DrawLine(rotated_pos - angle_vec * 20, rotated_pos + angle_vec * 20);
-	}
-
-	// Draw the fake features on the ring
-	gl.SetLineColour(line_color_secondary, 1.f, 1);
-	gl.SetFillColour(highlight_color, 0.3f);
-	gl.DrawCircle(angle_vec * radius, 4);
-	gl.DrawCircle(angle_vec * -radius, 4);
-
-	// Clear the projection
-	gl.ResetTransform();
-
-	// Draw line to mouse if it isn't over the origin feature
-	if (mouse_pos && (mouse_pos - org->pos).SquareLen() > 100) {
-		gl.SetLineColour(line_color_secondary);
-		gl.DrawLine(org->pos, mouse_pos);
-	}
+void VisualToolRotateZ::Draw()
+{
+    if (!active_line) return;
+
+    DrawAllFeatures();
+
+    // Load colors from options
+    wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
+    wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
+    wxColour highlight_color = to_wx(highlight_color_primary_opt->GetColor());
+
+    float radius = (pos - org->pos).Len();
+    float oRadius = radius;
+    if (radius < 50)
+        radius = 50;
+
+    // Set up the projection
+    gl.SetOrigin(org->pos);
+    gl.SetRotation(rotation_x, rotation_y, 0);
+    gl.SetScale(scale);
+
+    // Draw the circle
+    gl.SetLineColour(line_color_secondary);
+    gl.SetFillColour(highlight_color, 0.3f);
+    gl.DrawRing(Vector2D(0, 0), radius + 4, radius - 4);
+
+    // Draw markers around circle
+    int markers = 6;
+    float markStart = -90.f / markers;
+    float markEnd = markStart + (180.f / markers);
+    for (int i = 0; i < markers; ++i) {
+        float angle = i * (360.f / markers);
+        gl.DrawRing(Vector2D(0, 0), radius + 30, radius + 12, 1.0, angle + markStart, angle + markEnd);
+    }
+
+    // Draw the baseline through the origin showing current rotation
+    Vector2D angle_vec(Vector2D::FromAngle(angle * deg2rad));
+    gl.SetLineColour(line_color_primary, 1, 2);
+    gl.DrawLine(angle_vec * -radius, angle_vec * radius);
+
+    if (org->pos != pos) {
+        Vector2D rotated_pos = Vector2D::FromAngle(angle * deg2rad - (pos - org->pos).Angle()) * oRadius;
+
+        // Draw the line from origin to rotated position
+        gl.DrawLine(Vector2D(), rotated_pos);
+
+        // Draw the line under the text
+        gl.DrawLine(rotated_pos - angle_vec * 20, rotated_pos + angle_vec * 20);
+    }
+
+    // Draw the fake features on the ring
+    gl.SetLineColour(line_color_secondary, 1.f, 1);
+    gl.SetFillColour(highlight_color, 0.3f);
+    gl.DrawCircle(angle_vec * radius, 4);
+    gl.DrawCircle(angle_vec * -radius, 4);
+
+    // Clear the projection
+    gl.ResetTransform();
+
+    // Draw line to mouse if it isn't over the origin feature
+    if (mouse_pos && (mouse_pos - org->pos).SquareLen() > 100) {
+        gl.SetLineColour(line_color_secondary);
+        gl.DrawLine(org->pos, mouse_pos);
+    }
 }
 
-bool VisualToolRotateZ::InitializeHold() {
-	orig_angle = angle + (org->pos - mouse_pos).Angle() * rad2deg;
-	return true;
+bool VisualToolRotateZ::InitializeHold()
+{
+    orig_angle = angle + (org->pos - mouse_pos).Angle() * rad2deg;
+    return true;
 }
 
-void VisualToolRotateZ::UpdateHold() {
-	angle = orig_angle - (org->pos - mouse_pos).Angle() * rad2deg;
+void VisualToolRotateZ::UpdateHold()
+{
+    angle = orig_angle - (org->pos - mouse_pos).Angle() * rad2deg;
 
-	if (ctrl_down)
-		angle = floorf(angle / 30.f + .5f) * 30.f;
+    if (ctrl_down)
+        angle = floorf(angle / 30.f + .5f) * 30.f;
 
-	angle = fmodf(angle + 360.f, 360.f);
+    angle = fmodf(angle + 360.f, 360.f);
 
-	SetSelectedOverride("\\frz", agi::format("%.4g", angle));
+    SetSelectedOverride("\\frz", agi::format("%.4g", angle));
 }
 
-void VisualToolRotateZ::UpdateDrag(Feature *feature) {
-	auto org = GetLineOrigin(active_line);
-	if (!org) org = GetLinePosition(active_line);
-	auto d = ToScriptCoords(feature->pos) - org;
-
-	for (auto line : c->selectionController->GetSelectedSet()) {
-		org = GetLineOrigin(line);
-		if (!org) org = GetLinePosition(line);
-		SetOverride(line, "\\org", (d + org).PStr());
-	}
+void VisualToolRotateZ::UpdateDrag(Feature *feature)
+{
+    auto org = GetLineOrigin(active_line);
+    if (!org) org = GetLinePosition(active_line);
+    auto d = ToScriptCoords(feature->pos) - org;
+
+    for (auto line : c->selectionController->GetSelectedSet()) {
+        org = GetLineOrigin(line);
+        if (!org) org = GetLinePosition(line);
+        SetOverride(line, "\\org", (d + org).PStr());
+    }
 }
 
-void VisualToolRotateZ::DoRefresh() {
-	if (!active_line) return;
+void VisualToolRotateZ::DoRefresh()
+{
+    if (!active_line) return;
 
-	pos = FromScriptCoords(GetLinePosition(active_line));
-	if (!(org->pos = GetLineOrigin(active_line)))
-		org->pos = pos;
-	else
-		org->pos = FromScriptCoords(org->pos);
+    pos = FromScriptCoords(GetLinePosition(active_line));
+    if (!(org->pos = GetLineOrigin(active_line)))
+        org->pos = pos;
+    else
+        org->pos = FromScriptCoords(org->pos);
 
-	GetLineRotation(active_line, rotation_x, rotation_y, angle);
-	GetLineScale(active_line, scale);
+    GetLineRotation(active_line, rotation_x, rotation_y, angle);
+    GetLineScale(active_line, scale);
 }
diff --git a/src/visual_tool_rotatez.h b/src/visual_tool_rotatez.h
index a7bb08cbf4e1c5cf710930c319b4960561910bea..e4e867c4582b798573e355f5922c447241edb610 100644
--- a/src/visual_tool_rotatez.h
+++ b/src/visual_tool_rotatez.h
@@ -23,24 +23,24 @@
 #include "visual_tool.h"
 
 class VisualToolRotateZ final : public VisualTool<VisualDraggableFeature> {
-	float angle = 0.f; ///< Current Z rotation
-	float orig_angle = 0.f; ///< Z rotation at the beginning of the current hold
-	Vector2D pos; ///< Position of the dialogue line
-	Vector2D scale; ///< Current scale
+    float angle = 0.f; ///< Current Z rotation
+    float orig_angle = 0.f; ///< Z rotation at the beginning of the current hold
+    Vector2D pos; ///< Position of the dialogue line
+    Vector2D scale; ///< Current scale
 
-	float rotation_x = 0.f; ///< Current X rotation
-	float rotation_y = 0.f; ///< Current Y rotation
+    float rotation_x = 0.f; ///< Current X rotation
+    float rotation_y = 0.f; ///< Current Y rotation
 
-	Feature *org; ///< The origin feature
+    Feature *org; ///< The origin feature
 
-	bool InitializeHold() override;
-	void UpdateHold() override;
+    bool InitializeHold() override;
+    void UpdateHold() override;
 
-	void UpdateDrag(Feature *feature) override;
+    void UpdateDrag(Feature *feature) override;
 
-	void DoRefresh() override;
+    void DoRefresh() override;
 
-	void Draw() override;
+    void Draw() override;
 public:
-	VisualToolRotateZ(VideoDisplay *parent, agi::Context *context);
+    VisualToolRotateZ(VideoDisplay *parent, agi::Context *context);
 };
diff --git a/src/visual_tool_scale.cpp b/src/visual_tool_scale.cpp
index 10468d3a9784fff84141a3fb38d349d6ba312966..0832d562a1f4db1fc85cbe4742086a4beef4aa04 100644
--- a/src/visual_tool_scale.cpp
+++ b/src/visual_tool_scale.cpp
@@ -26,98 +26,102 @@
 #include <wx/colour.h>
 
 VisualToolScale::VisualToolScale(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualDraggableFeature>(parent, context)
+    : VisualTool<VisualDraggableFeature>(parent, context)
 {
 }
 
-void VisualToolScale::Draw() {
-	if (!active_line) return;
-
-	// The length in pixels of the 100% zoom
-	static const int base_len = 160;
-	// The width of the y scale guide/height of the x scale guide
-	static const int guide_size = 10;
-
-	// Load colors from options
-	wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
-	wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
-	wxColour highlight_color = to_wx(highlight_color_primary_opt->GetColor());
-
-	// Ensure that the scaling UI is comfortably visible on screen
-	Vector2D base_point = pos
-		.Max(Vector2D(base_len / 2 + guide_size, base_len / 2 + guide_size))
-		.Min(video_res - base_len / 2 - guide_size * 3);
-
-	// Set the origin to the base point and apply the line's rotation
-	gl.SetOrigin(base_point);
-	gl.SetRotation(rx, ry, rz);
-
-	Vector2D scale_half_length = scale * base_len / 200;
-	float minor_dim_offset = base_len / 2 + guide_size * 1.5f;
-
-	// The ends of the current scale amount lines
-	Vector2D x_p1(minor_dim_offset, -scale_half_length.Y());
-	Vector2D x_p2(minor_dim_offset, scale_half_length.Y());
-	Vector2D y_p1(-scale_half_length.X(), minor_dim_offset);
-	Vector2D y_p2(scale_half_length.X(), minor_dim_offset);
-
-	// Current scale amount lines
-	gl.SetLineColour(line_color_primary, 1.f, 2);
-	gl.DrawLine(x_p1, x_p2);
-	gl.DrawLine(y_p1, y_p2);
-
-	// Fake features at the end of the lines
-	gl.SetLineColour(line_color_secondary, 1.f, 1);
-	gl.SetFillColour(highlight_color, 0.3f);
-	gl.DrawCircle(x_p1, 4);
-	gl.DrawCircle(x_p2, 4);
-	gl.DrawCircle(y_p1, 4);
-	gl.DrawCircle(y_p2, 4);
-
-	// Draw the guides
-	int half_len = base_len / 2;
-	gl.SetLineColour(line_color_secondary, 1.f, 1);
-	gl.DrawRectangle(Vector2D(half_len, -half_len), Vector2D(half_len + guide_size, half_len));
-	gl.DrawRectangle(Vector2D(-half_len, half_len), Vector2D(half_len, half_len + guide_size));
-
-	// Draw the feet
-	gl.SetLineColour(line_color_secondary, 1.f, 2);
-	gl.DrawLine(Vector2D(half_len + guide_size, -half_len), Vector2D(half_len + guide_size + guide_size / 2, -half_len));
-	gl.DrawLine(Vector2D(half_len + guide_size, half_len), Vector2D(half_len + guide_size + guide_size / 2, half_len));
-	gl.DrawLine(Vector2D(-half_len, half_len + guide_size), Vector2D(-half_len, half_len + guide_size + guide_size / 2));
-	gl.DrawLine(Vector2D(half_len, half_len + guide_size), Vector2D(half_len, half_len + guide_size + guide_size / 2));
-
-	gl.ResetTransform();
+void VisualToolScale::Draw()
+{
+    if (!active_line) return;
+
+    // The length in pixels of the 100% zoom
+    static const int base_len = 160;
+    // The width of the y scale guide/height of the x scale guide
+    static const int guide_size = 10;
+
+    // Load colors from options
+    wxColour line_color_primary = to_wx(line_color_primary_opt->GetColor());
+    wxColour line_color_secondary = to_wx(line_color_secondary_opt->GetColor());
+    wxColour highlight_color = to_wx(highlight_color_primary_opt->GetColor());
+
+    // Ensure that the scaling UI is comfortably visible on screen
+    Vector2D base_point = pos
+                          .Max(Vector2D(base_len / 2 + guide_size, base_len / 2 + guide_size))
+                          .Min(video_res - base_len / 2 - guide_size * 3);
+
+    // Set the origin to the base point and apply the line's rotation
+    gl.SetOrigin(base_point);
+    gl.SetRotation(rx, ry, rz);
+
+    Vector2D scale_half_length = scale * base_len / 200;
+    float minor_dim_offset = base_len / 2 + guide_size * 1.5f;
+
+    // The ends of the current scale amount lines
+    Vector2D x_p1(minor_dim_offset, -scale_half_length.Y());
+    Vector2D x_p2(minor_dim_offset, scale_half_length.Y());
+    Vector2D y_p1(-scale_half_length.X(), minor_dim_offset);
+    Vector2D y_p2(scale_half_length.X(), minor_dim_offset);
+
+    // Current scale amount lines
+    gl.SetLineColour(line_color_primary, 1.f, 2);
+    gl.DrawLine(x_p1, x_p2);
+    gl.DrawLine(y_p1, y_p2);
+
+    // Fake features at the end of the lines
+    gl.SetLineColour(line_color_secondary, 1.f, 1);
+    gl.SetFillColour(highlight_color, 0.3f);
+    gl.DrawCircle(x_p1, 4);
+    gl.DrawCircle(x_p2, 4);
+    gl.DrawCircle(y_p1, 4);
+    gl.DrawCircle(y_p2, 4);
+
+    // Draw the guides
+    int half_len = base_len / 2;
+    gl.SetLineColour(line_color_secondary, 1.f, 1);
+    gl.DrawRectangle(Vector2D(half_len, -half_len), Vector2D(half_len + guide_size, half_len));
+    gl.DrawRectangle(Vector2D(-half_len, half_len), Vector2D(half_len, half_len + guide_size));
+
+    // Draw the feet
+    gl.SetLineColour(line_color_secondary, 1.f, 2);
+    gl.DrawLine(Vector2D(half_len + guide_size, -half_len), Vector2D(half_len + guide_size + guide_size / 2, -half_len));
+    gl.DrawLine(Vector2D(half_len + guide_size, half_len), Vector2D(half_len + guide_size + guide_size / 2, half_len));
+    gl.DrawLine(Vector2D(-half_len, half_len + guide_size), Vector2D(-half_len, half_len + guide_size + guide_size / 2));
+    gl.DrawLine(Vector2D(half_len, half_len + guide_size), Vector2D(half_len, half_len + guide_size + guide_size / 2));
+
+    gl.ResetTransform();
 }
 
-bool VisualToolScale::InitializeHold() {
-	initial_scale = scale;
-	return true;
+bool VisualToolScale::InitializeHold()
+{
+    initial_scale = scale;
+    return true;
 }
 
-void VisualToolScale::UpdateHold() {
-	Vector2D delta = (mouse_pos - drag_start) * Vector2D(1, -1);
-	if (shift_down)
-		delta = delta.SingleAxis();
-	if (alt_down) {
-		if (std::abs(delta.X()) > std::abs(delta.Y()))
-			delta = Vector2D(delta.X(), delta.X() * (initial_scale.Y() / initial_scale.X()));
-		else
-			delta = Vector2D(delta.Y() * (initial_scale.X() / initial_scale.Y()), delta.Y());
-	}
-
-	scale = Vector2D(0, 0).Max(delta * 1.25f + initial_scale);
-	if (ctrl_down)
-		scale = scale.Round(25.f);
-
-	SetSelectedOverride("\\fscx", std::to_string((int)scale.X()));
-	SetSelectedOverride("\\fscy", std::to_string((int)scale.Y()));
+void VisualToolScale::UpdateHold()
+{
+    Vector2D delta = (mouse_pos - drag_start) * Vector2D(1, -1);
+    if (shift_down)
+        delta = delta.SingleAxis();
+    if (alt_down) {
+        if (std::abs(delta.X()) > std::abs(delta.Y()))
+            delta = Vector2D(delta.X(), delta.X() * (initial_scale.Y() / initial_scale.X()));
+        else
+            delta = Vector2D(delta.Y() * (initial_scale.X() / initial_scale.Y()), delta.Y());
+    }
+
+    scale = Vector2D(0, 0).Max(delta * 1.25f + initial_scale);
+    if (ctrl_down)
+        scale = scale.Round(25.f);
+
+    SetSelectedOverride("\\fscx", std::to_string((int)scale.X()));
+    SetSelectedOverride("\\fscy", std::to_string((int)scale.Y()));
 }
 
-void VisualToolScale::DoRefresh() {
-	if (!active_line) return;
+void VisualToolScale::DoRefresh()
+{
+    if (!active_line) return;
 
-	GetLineScale(active_line, scale);
-	GetLineRotation(active_line, rx, ry, rz);
-	pos = FromScriptCoords(GetLinePosition(active_line));
+    GetLineScale(active_line, scale);
+    GetLineRotation(active_line, rx, ry, rz);
+    pos = FromScriptCoords(GetLinePosition(active_line));
 }
diff --git a/src/visual_tool_scale.h b/src/visual_tool_scale.h
index 27a6557f373647c874da183dba8edd6722ae1912..07226fefd5982c53e40e4959ff32df9eba111ad7 100644
--- a/src/visual_tool_scale.h
+++ b/src/visual_tool_scale.h
@@ -23,19 +23,19 @@
 #include "visual_tool.h"
 
 class VisualToolScale final : public VisualTool<VisualDraggableFeature> {
-	Vector2D scale; ///< The current scale
-	Vector2D initial_scale; ///< The scale at the beginning of the current hold
-	Vector2D pos; ///< Position of the line
+    Vector2D scale; ///< The current scale
+    Vector2D initial_scale; ///< The scale at the beginning of the current hold
+    Vector2D pos; ///< Position of the line
 
-	float rx = 0.f; ///< X rotation
-	float ry = 0.f; ///< Y rotation
-	float rz = 0.f; ///< Z rotation
+    float rx = 0.f; ///< X rotation
+    float ry = 0.f; ///< Y rotation
+    float rz = 0.f; ///< Z rotation
 
-	bool InitializeHold() override;
-	void UpdateHold() override;
+    bool InitializeHold() override;
+    void UpdateHold() override;
 
-	void DoRefresh() override;
-	void Draw() override;
+    void DoRefresh() override;
+    void Draw() override;
 public:
-	VisualToolScale(VideoDisplay *parent, agi::Context *context);
+    VisualToolScale(VideoDisplay *parent, agi::Context *context);
 };
diff --git a/src/visual_tool_vector_clip.cpp b/src/visual_tool_vector_clip.cpp
index bbe00d8e919be398ba669f17e17f8936350c3211..1750ff1db5affdde1bf014538974317ee61e1ec9 100644
--- a/src/visual_tool_vector_clip.cpp
+++ b/src/visual_tool_vector_clip.cpp
@@ -32,424 +32,437 @@
 
 /// Button IDs
 enum {
-	BUTTON_DRAG = 1300,
-	BUTTON_LINE,
-	BUTTON_BICUBIC,
-	BUTTON_CONVERT,
-	BUTTON_INSERT,
-	BUTTON_REMOVE,
-	BUTTON_FREEHAND,
-	BUTTON_FREEHAND_SMOOTH,
-	BUTTON_LAST // Leave this at the end and don't use it
+    BUTTON_DRAG = 1300,
+    BUTTON_LINE,
+    BUTTON_BICUBIC,
+    BUTTON_CONVERT,
+    BUTTON_INSERT,
+    BUTTON_REMOVE,
+    BUTTON_FREEHAND,
+    BUTTON_FREEHAND_SMOOTH,
+    BUTTON_LAST // Leave this at the end and don't use it
 };
 
 VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context)
-: VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
-, spline(*this)
+    : VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
+    , spline(*this)
 {
 }
 
-void VisualToolVectorClip::SetToolbar(wxToolBar *toolBar) {
-	this->toolBar = toolBar;
+void VisualToolVectorClip::SetToolbar(wxToolBar *toolBar)
+{
+    this->toolBar = toolBar;
 
-	int icon_size = OPT_GET("App/Toolbar Icon Size")->GetInt();
+    int icon_size = OPT_GET("App/Toolbar Icon Size")->GetInt();
 
 #define ICON(name) icon_size == 16 ? GETIMAGE(name ## _16) : GETIMAGE(name ## _24)
-	toolBar->AddTool(BUTTON_DRAG, _("Drag"), ICON(visual_vector_clip_drag), _("Drag control points"), wxITEM_CHECK);
-	toolBar->AddTool(BUTTON_LINE, _("Line"), ICON(visual_vector_clip_line), _("Appends a line"), wxITEM_CHECK);
-	toolBar->AddTool(BUTTON_BICUBIC, _("Bicubic"), ICON(visual_vector_clip_bicubic), _("Appends a bezier bicubic curve"), wxITEM_CHECK);
-	toolBar->AddSeparator();
-	toolBar->AddTool(BUTTON_CONVERT, _("Convert"), ICON(visual_vector_clip_convert), _("Converts a segment between line and bicubic"), wxITEM_CHECK);
-	toolBar->AddTool(BUTTON_INSERT, _("Insert"), ICON(visual_vector_clip_insert), _("Inserts a control point"), wxITEM_CHECK);
-	toolBar->AddTool(BUTTON_REMOVE, _("Remove"), ICON(visual_vector_clip_remove), _("Removes a control point"), wxITEM_CHECK);
-	toolBar->AddSeparator();
-	toolBar->AddTool(BUTTON_FREEHAND, _("Freehand"), ICON(visual_vector_clip_freehand), _("Draws a freehand shape"), wxITEM_CHECK);
-	toolBar->AddTool(BUTTON_FREEHAND_SMOOTH, _("Freehand smooth"), ICON(visual_vector_clip_freehand_smooth), _("Draws a smoothed freehand shape"), wxITEM_CHECK);
-	toolBar->ToggleTool(BUTTON_DRAG, true);
-	toolBar->Realize();
-	toolBar->Show(true);
-	toolBar->Bind(wxEVT_TOOL, [=](wxCommandEvent& e) { SetMode(e.GetId() - BUTTON_DRAG); });
-	SetMode(features.empty());
+    toolBar->AddTool(BUTTON_DRAG, _("Drag"), ICON(visual_vector_clip_drag), _("Drag control points"), wxITEM_CHECK);
+    toolBar->AddTool(BUTTON_LINE, _("Line"), ICON(visual_vector_clip_line), _("Appends a line"), wxITEM_CHECK);
+    toolBar->AddTool(BUTTON_BICUBIC, _("Bicubic"), ICON(visual_vector_clip_bicubic), _("Appends a bezier bicubic curve"), wxITEM_CHECK);
+    toolBar->AddSeparator();
+    toolBar->AddTool(BUTTON_CONVERT, _("Convert"), ICON(visual_vector_clip_convert), _("Converts a segment between line and bicubic"), wxITEM_CHECK);
+    toolBar->AddTool(BUTTON_INSERT, _("Insert"), ICON(visual_vector_clip_insert), _("Inserts a control point"), wxITEM_CHECK);
+    toolBar->AddTool(BUTTON_REMOVE, _("Remove"), ICON(visual_vector_clip_remove), _("Removes a control point"), wxITEM_CHECK);
+    toolBar->AddSeparator();
+    toolBar->AddTool(BUTTON_FREEHAND, _("Freehand"), ICON(visual_vector_clip_freehand), _("Draws a freehand shape"), wxITEM_CHECK);
+    toolBar->AddTool(BUTTON_FREEHAND_SMOOTH, _("Freehand smooth"), ICON(visual_vector_clip_freehand_smooth), _("Draws a smoothed freehand shape"), wxITEM_CHECK);
+    toolBar->ToggleTool(BUTTON_DRAG, true);
+    toolBar->Realize();
+    toolBar->Show(true);
+    toolBar->Bind(wxEVT_TOOL, [ = ](wxCommandEvent & e) { SetMode(e.GetId() - BUTTON_DRAG); });
+    SetMode(features.empty());
 #undef ICON
 }
 
-void VisualToolVectorClip::SetMode(int new_mode) {
-	// Manually enforce radio behavior as we want one selection in the bar
-	// rather than one per group
-	for (int i = BUTTON_DRAG; i < BUTTON_LAST; i++)
-		toolBar->ToggleTool(i, i == new_mode + BUTTON_DRAG);
+void VisualToolVectorClip::SetMode(int new_mode)
+{
+    // Manually enforce radio behavior as we want one selection in the bar
+    // rather than one per group
+    for (int i = BUTTON_DRAG; i < BUTTON_LAST; i++)
+        toolBar->ToggleTool(i, i == new_mode + BUTTON_DRAG);
 
-	mode = new_mode;
+    mode = new_mode;
 }
 
-void VisualToolVectorClip::Draw() {
-	if (!active_line) return;
-	if (spline.empty()) return;
-
-	// Parse vector
-	std::vector<int> start;
-	std::vector<int> count;
-	auto points = spline.GetPointList(start, count);
-	assert(!start.empty());
-	assert(!count.empty());
-
-	// Load colors from options
-	wxColour line_color = to_wx(line_color_primary_opt->GetColor());
-	wxColour highlight_color_primary = to_wx(highlight_color_primary_opt->GetColor());
-	wxColour highlight_color_secondary = to_wx(highlight_color_secondary_opt->GetColor());
-	float shaded_alpha = static_cast<float>(shaded_area_alpha_opt->GetDouble());
-
-	gl.SetLineColour(line_color, .5f, 2);
-	gl.SetFillColour(*wxBLACK, shaded_alpha);
-
-	// draw the shade over clipped out areas and line showing the clip
-	gl.DrawMultiPolygon(points, start, count, video_pos, video_res, !inverse);
-
-	if (mode == 0 && holding && drag_start && mouse_pos) {
-		// Draw drag-select box
-		Vector2D top_left = drag_start.Min(mouse_pos);
-		Vector2D bottom_right = drag_start.Max(mouse_pos);
-		gl.DrawDashedLine(top_left, Vector2D(top_left.X(), bottom_right.Y()), 6);
-		gl.DrawDashedLine(Vector2D(top_left.X(), bottom_right.Y()), bottom_right, 6);
-		gl.DrawDashedLine(bottom_right, Vector2D(bottom_right.X(), top_left.Y()), 6);
-		gl.DrawDashedLine(Vector2D(bottom_right.X(), top_left.Y()), top_left, 6);
-	}
-
-	Vector2D pt;
-	float t;
-	Spline::iterator highlighted_curve;
-	spline.GetClosestParametricPoint(mouse_pos, highlighted_curve, t, pt);
-
-	// Draw highlighted line
-	if ((mode == 3 || mode == 4) && !active_feature && points.size() > 2) {
-		auto highlighted_points = spline.GetPointList(highlighted_curve);
-		if (!highlighted_points.empty()) {
-			gl.SetLineColour(highlight_color_secondary, 1.f, 2);
-			gl.DrawLineStrip(2, highlighted_points);
-		}
-	}
-
-	// Draw lines connecting the bicubic features
-	gl.SetLineColour(line_color, 0.9f, 1);
-	for (auto const& curve : spline) {
-		if (curve.type == SplineCurve::BICUBIC) {
-			gl.DrawDashedLine(curve.p1, curve.p2, 6);
-			gl.DrawDashedLine(curve.p3, curve.p4, 6);
-		}
-	}
-
-	// Draw features
-	for (auto& feature : features) {
-		wxColour feature_color = line_color;
-		if (&feature == active_feature)
-			feature_color = highlight_color_primary;
-		else if (sel_features.count(&feature))
-			feature_color = highlight_color_secondary;
-		gl.SetFillColour(feature_color, .6f);
-
-		if (feature.type == DRAG_SMALL_SQUARE) {
-			gl.SetLineColour(line_color, .5f, 1);
-			gl.DrawRectangle(feature.pos - 3, feature.pos + 3);
-		}
-		else {
-			gl.SetLineColour(feature_color, .5f, 1);
-			gl.DrawCircle(feature.pos, 2.f);
-		}
-	}
-
-	// Draw preview of inserted line
-	if (mode == 1 || mode == 2) {
-		if (spline.size() && mouse_pos) {
-			auto c0 = std::find_if(spline.rbegin(), spline.rend(),
-				[](SplineCurve const& s) { return s.type == SplineCurve::POINT; });
-			SplineCurve *c1 = &spline.back();
-			gl.DrawDashedLine(mouse_pos, c0->p1, 6);
-			gl.DrawDashedLine(mouse_pos, c1->EndPoint(), 6);
-		}
-	}
-
-	// Draw preview of insert point
-	if (mode == 4)
-		gl.DrawCircle(pt, 4);
+void VisualToolVectorClip::Draw()
+{
+    if (!active_line) return;
+    if (spline.empty()) return;
+
+    // Parse vector
+    std::vector<int> start;
+    std::vector<int> count;
+    auto points = spline.GetPointList(start, count);
+    assert(!start.empty());
+    assert(!count.empty());
+
+    // Load colors from options
+    wxColour line_color = to_wx(line_color_primary_opt->GetColor());
+    wxColour highlight_color_primary = to_wx(highlight_color_primary_opt->GetColor());
+    wxColour highlight_color_secondary = to_wx(highlight_color_secondary_opt->GetColor());
+    float shaded_alpha = static_cast<float>(shaded_area_alpha_opt->GetDouble());
+
+    gl.SetLineColour(line_color, .5f, 2);
+    gl.SetFillColour(*wxBLACK, shaded_alpha);
+
+    // draw the shade over clipped out areas and line showing the clip
+    gl.DrawMultiPolygon(points, start, count, video_pos, video_res, !inverse);
+
+    if (mode == 0 && holding && drag_start && mouse_pos) {
+        // Draw drag-select box
+        Vector2D top_left = drag_start.Min(mouse_pos);
+        Vector2D bottom_right = drag_start.Max(mouse_pos);
+        gl.DrawDashedLine(top_left, Vector2D(top_left.X(), bottom_right.Y()), 6);
+        gl.DrawDashedLine(Vector2D(top_left.X(), bottom_right.Y()), bottom_right, 6);
+        gl.DrawDashedLine(bottom_right, Vector2D(bottom_right.X(), top_left.Y()), 6);
+        gl.DrawDashedLine(Vector2D(bottom_right.X(), top_left.Y()), top_left, 6);
+    }
+
+    Vector2D pt;
+    float t;
+    Spline::iterator highlighted_curve;
+    spline.GetClosestParametricPoint(mouse_pos, highlighted_curve, t, pt);
+
+    // Draw highlighted line
+    if ((mode == 3 || mode == 4) && !active_feature && points.size() > 2) {
+        auto highlighted_points = spline.GetPointList(highlighted_curve);
+        if (!highlighted_points.empty()) {
+            gl.SetLineColour(highlight_color_secondary, 1.f, 2);
+            gl.DrawLineStrip(2, highlighted_points);
+        }
+    }
+
+    // Draw lines connecting the bicubic features
+    gl.SetLineColour(line_color, 0.9f, 1);
+    for (auto const &curve : spline) {
+        if (curve.type == SplineCurve::BICUBIC) {
+            gl.DrawDashedLine(curve.p1, curve.p2, 6);
+            gl.DrawDashedLine(curve.p3, curve.p4, 6);
+        }
+    }
+
+    // Draw features
+    for (auto &feature : features) {
+        wxColour feature_color = line_color;
+        if (&feature == active_feature)
+            feature_color = highlight_color_primary;
+        else if (sel_features.count(&feature))
+            feature_color = highlight_color_secondary;
+        gl.SetFillColour(feature_color, .6f);
+
+        if (feature.type == DRAG_SMALL_SQUARE) {
+            gl.SetLineColour(line_color, .5f, 1);
+            gl.DrawRectangle(feature.pos - 3, feature.pos + 3);
+        }
+        else {
+            gl.SetLineColour(feature_color, .5f, 1);
+            gl.DrawCircle(feature.pos, 2.f);
+        }
+    }
+
+    // Draw preview of inserted line
+    if (mode == 1 || mode == 2) {
+        if (spline.size() && mouse_pos) {
+            auto c0 = std::find_if(spline.rbegin(), spline.rend(),
+            [](SplineCurve const & s) { return s.type == SplineCurve::POINT; });
+            SplineCurve *c1 = &spline.back();
+            gl.DrawDashedLine(mouse_pos, c0->p1, 6);
+            gl.DrawDashedLine(mouse_pos, c1->EndPoint(), 6);
+        }
+    }
+
+    // Draw preview of insert point
+    if (mode == 4)
+        gl.DrawCircle(pt, 4);
 }
 
-void VisualToolVectorClip::MakeFeature(size_t idx) {
-	auto feat = agi::make_unique<Feature>();
-	feat->idx = idx;
-
-	auto const& curve = spline[idx];
-	if (curve.type == SplineCurve::POINT) {
-		feat->pos = curve.p1;
-		feat->type = DRAG_SMALL_CIRCLE;
-		feat->point = 0;
-	}
-	else if (curve.type == SplineCurve::LINE) {
-		feat->pos = curve.p2;
-		feat->type = DRAG_SMALL_CIRCLE;
-		feat->point = 1;
-	}
-	else if (curve.type == SplineCurve::BICUBIC) {
-		// Control points
-		feat->pos = curve.p2;
-		feat->point = 1;
-		feat->type = DRAG_SMALL_SQUARE;
-		features.push_back(*feat.release());
-
-		feat = agi::make_unique<Feature>();
-		feat->idx = idx;
-		feat->pos = curve.p3;
-		feat->point = 2;
-		feat->type = DRAG_SMALL_SQUARE;
-		features.push_back(*feat.release());
-
-		// End point
-		feat = agi::make_unique<Feature>();
-		feat->idx = idx;
-		feat->pos = curve.p4;
-		feat->point = 3;
-		feat->type = DRAG_SMALL_CIRCLE;
-	}
-	features.push_back(*feat.release());
+void VisualToolVectorClip::MakeFeature(size_t idx)
+{
+    auto feat = agi::make_unique<Feature>();
+    feat->idx = idx;
+
+    auto const &curve = spline[idx];
+    if (curve.type == SplineCurve::POINT) {
+        feat->pos = curve.p1;
+        feat->type = DRAG_SMALL_CIRCLE;
+        feat->point = 0;
+    }
+    else if (curve.type == SplineCurve::LINE) {
+        feat->pos = curve.p2;
+        feat->type = DRAG_SMALL_CIRCLE;
+        feat->point = 1;
+    }
+    else if (curve.type == SplineCurve::BICUBIC) {
+        // Control points
+        feat->pos = curve.p2;
+        feat->point = 1;
+        feat->type = DRAG_SMALL_SQUARE;
+        features.push_back(*feat.release());
+
+        feat = agi::make_unique<Feature>();
+        feat->idx = idx;
+        feat->pos = curve.p3;
+        feat->point = 2;
+        feat->type = DRAG_SMALL_SQUARE;
+        features.push_back(*feat.release());
+
+        // End point
+        feat = agi::make_unique<Feature>();
+        feat->idx = idx;
+        feat->pos = curve.p4;
+        feat->point = 3;
+        feat->type = DRAG_SMALL_CIRCLE;
+    }
+    features.push_back(*feat.release());
 }
 
-void VisualToolVectorClip::MakeFeatures() {
-	sel_features.clear();
-	features.clear();
-	active_feature = nullptr;
-	for (size_t i = 0; i < spline.size(); ++i)
-		MakeFeature(i);
+void VisualToolVectorClip::MakeFeatures()
+{
+    sel_features.clear();
+    features.clear();
+    active_feature = nullptr;
+    for (size_t i = 0; i < spline.size(); ++i)
+        MakeFeature(i);
 }
 
-void VisualToolVectorClip::Save() {
-	std::string value = "(";
-	if (spline.GetScale() != 1)
-		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") != std::string::npos;
-		SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
-	}
+void VisualToolVectorClip::Save()
+{
+    std::string value = "(";
+    if (spline.GetScale() != 1)
+        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") != std::string::npos;
+        SetOverride(line, has_iclip ? "\\iclip" : "\\clip", value);
+    }
 }
 
-void VisualToolVectorClip::Commit(wxString message) {
-	Save();
-	VisualToolBase::Commit(message);
+void VisualToolVectorClip::Commit(wxString message)
+{
+    Save();
+    VisualToolBase::Commit(message);
 }
 
-void VisualToolVectorClip::UpdateDrag(Feature *feature) {
-	spline.MovePoint(spline.begin() + feature->idx, feature->point, feature->pos);
+void VisualToolVectorClip::UpdateDrag(Feature *feature)
+{
+    spline.MovePoint(spline.begin() + feature->idx, feature->point, feature->pos);
 }
 
-bool VisualToolVectorClip::InitializeDrag(Feature *feature) {
-	if (mode != 5) return true;
-
-	auto curve = spline.begin() + feature->idx;
-	if (curve->type == SplineCurve::BICUBIC && (feature->point == 1 || feature->point == 2)) {
-		// Deleting bicubic curve handles, so convert to line
-		curve->type = SplineCurve::LINE;
-		curve->p2 = curve->p4;
-	}
-	else {
-		auto next = std::next(curve);
-		if (next != spline.end()) {
-			if (curve->type == SplineCurve::POINT) {
-				next->p1 = next->EndPoint();
-				next->type = SplineCurve::POINT;
-			}
-			else {
-				next->p1 = curve->p1;
-			}
-		}
-
-		spline.erase(curve);
-	}
-	active_feature = nullptr;
-
-	MakeFeatures();
-	Commit(_("delete control point"));
-
-	return false;
+bool VisualToolVectorClip::InitializeDrag(Feature *feature)
+{
+    if (mode != 5) return true;
+
+    auto curve = spline.begin() + feature->idx;
+    if (curve->type == SplineCurve::BICUBIC && (feature->point == 1 || feature->point == 2)) {
+        // Deleting bicubic curve handles, so convert to line
+        curve->type = SplineCurve::LINE;
+        curve->p2 = curve->p4;
+    }
+    else {
+        auto next = std::next(curve);
+        if (next != spline.end()) {
+            if (curve->type == SplineCurve::POINT) {
+                next->p1 = next->EndPoint();
+                next->type = SplineCurve::POINT;
+            }
+            else {
+                next->p1 = curve->p1;
+            }
+        }
+
+        spline.erase(curve);
+    }
+    active_feature = nullptr;
+
+    MakeFeatures();
+    Commit(_("delete control point"));
+
+    return false;
 }
 
-bool VisualToolVectorClip::InitializeHold() {
-	// Box selection
-	if (mode == 0) {
-		box_added.clear();
-		return true;
-	}
-
-	// Insert line/bicubic
-	if (mode == 1 || mode == 2) {
-		SplineCurve curve;
-
-		// New spline beginning at the clicked point
-		if (spline.empty()) {
-			curve.p1 = mouse_pos;
-			curve.type = SplineCurve::POINT;
-		}
-		else {
-			// Continue from the spline in progress
-			// Don't bother setting p2 as UpdateHold will handle that
-			curve.p1 = spline.back().EndPoint();
-			curve.type = mode == 1 ? SplineCurve::LINE : SplineCurve::BICUBIC;
-		}
-
-		spline.push_back(curve);
-		sel_features.clear();
-		MakeFeature(spline.size() - 1);
-		UpdateHold();
-		return true;
-	}
-
-	// Convert and insert
-	if (mode == 3 || mode == 4) {
-		// Get closest point
-		Vector2D pt;
-		Spline::iterator curve;
-		float t;
-		spline.GetClosestParametricPoint(mouse_pos, curve, t, pt);
-
-		// Convert line <-> bicubic
-		if (mode == 3) {
-			if (curve != spline.end()) {
-				if (curve->type == SplineCurve::LINE) {
-					curve->type = SplineCurve::BICUBIC;
-					curve->p4 = curve->p2;
-					curve->p2 = curve->p1 * 0.75 + curve->p4 * 0.25;
-					curve->p3 = curve->p1 * 0.25 + curve->p4 * 0.75;
-				}
-
-				else if (curve->type == SplineCurve::BICUBIC) {
-					curve->type = SplineCurve::LINE;
-					curve->p2 = curve->p4;
-				}
-			}
-		}
-		// Insert
-		else {
-			if (spline.empty()) return false;
-
-			// Split the curve
-			if (curve == spline.end()) {
-				SplineCurve ct(spline.back().EndPoint(), spline.front().p1);
-				ct.p2 = ct.p1 * (1 - t) + ct.p2 * t;
-				spline.push_back(ct);
-			}
-			else {
-				std::pair<SplineCurve, SplineCurve> split = curve->Split(t);
-				*curve = split.first;
-				spline.insert(++curve, split.second);
-			}
-		}
-
-		MakeFeatures();
-		Commit();
-		return false;
-	}
-
-	// Freehand spline draw
-	if (mode == 6 || mode == 7) {
-		sel_features.clear();
-		features.clear();
-		active_feature = nullptr;
-		spline.clear();
-		spline.emplace_back(mouse_pos);
-		return true;
-	}
-
-	// Nothing to do for mode 5 (remove)
-	return false;
+bool VisualToolVectorClip::InitializeHold()
+{
+    // Box selection
+    if (mode == 0) {
+        box_added.clear();
+        return true;
+    }
+
+    // Insert line/bicubic
+    if (mode == 1 || mode == 2) {
+        SplineCurve curve;
+
+        // New spline beginning at the clicked point
+        if (spline.empty()) {
+            curve.p1 = mouse_pos;
+            curve.type = SplineCurve::POINT;
+        }
+        else {
+            // Continue from the spline in progress
+            // Don't bother setting p2 as UpdateHold will handle that
+            curve.p1 = spline.back().EndPoint();
+            curve.type = mode == 1 ? SplineCurve::LINE : SplineCurve::BICUBIC;
+        }
+
+        spline.push_back(curve);
+        sel_features.clear();
+        MakeFeature(spline.size() - 1);
+        UpdateHold();
+        return true;
+    }
+
+    // Convert and insert
+    if (mode == 3 || mode == 4) {
+        // Get closest point
+        Vector2D pt;
+        Spline::iterator curve;
+        float t;
+        spline.GetClosestParametricPoint(mouse_pos, curve, t, pt);
+
+        // Convert line <-> bicubic
+        if (mode == 3) {
+            if (curve != spline.end()) {
+                if (curve->type == SplineCurve::LINE) {
+                    curve->type = SplineCurve::BICUBIC;
+                    curve->p4 = curve->p2;
+                    curve->p2 = curve->p1 * 0.75 + curve->p4 * 0.25;
+                    curve->p3 = curve->p1 * 0.25 + curve->p4 * 0.75;
+                }
+
+                else if (curve->type == SplineCurve::BICUBIC) {
+                    curve->type = SplineCurve::LINE;
+                    curve->p2 = curve->p4;
+                }
+            }
+        }
+        // Insert
+        else {
+            if (spline.empty()) return false;
+
+            // Split the curve
+            if (curve == spline.end()) {
+                SplineCurve ct(spline.back().EndPoint(), spline.front().p1);
+                ct.p2 = ct.p1 * (1 - t) + ct.p2 * t;
+                spline.push_back(ct);
+            }
+            else {
+                std::pair<SplineCurve, SplineCurve> split = curve->Split(t);
+                *curve = split.first;
+                spline.insert(++curve, split.second);
+            }
+        }
+
+        MakeFeatures();
+        Commit();
+        return false;
+    }
+
+    // Freehand spline draw
+    if (mode == 6 || mode == 7) {
+        sel_features.clear();
+        features.clear();
+        active_feature = nullptr;
+        spline.clear();
+        spline.emplace_back(mouse_pos);
+        return true;
+    }
+
+    // Nothing to do for mode 5 (remove)
+    return false;
 }
 
-static bool in_box(Vector2D top_left, Vector2D bottom_right, Vector2D p) {
-	return p.X() >= top_left.X()
-		&& p.X() <= bottom_right.X()
-		&& p.Y() >= top_left.Y()
-		&& p.Y() <= bottom_right.Y();
+static bool in_box(Vector2D top_left, Vector2D bottom_right, Vector2D p)
+{
+    return p.X() >= top_left.X()
+           && p.X() <= bottom_right.X()
+           && p.Y() >= top_left.Y()
+           && p.Y() <= bottom_right.Y();
 }
 
-void VisualToolVectorClip::UpdateHold() {
-	// Box selection
-	if (mode == 0) {
-		std::set<Feature *> boxed_features;
-		Vector2D p1 = drag_start.Min(mouse_pos);
-		Vector2D p2 = drag_start.Max(mouse_pos);
-		for (auto& feature : features) {
-			if (in_box(p1, p2, feature.pos))
-				boxed_features.insert(&feature);
-		}
-
-		// Keep track of which features were selected by the box selection so
-		// that only those are deselected if the user is holding ctrl
-		boost::set_difference(boxed_features, sel_features,
-			std::inserter(box_added, end(box_added)));
-
-		boost::copy(boxed_features, std::inserter(sel_features, end(sel_features)));
-
-		std::vector<Feature *> to_deselect;
-		boost::set_difference(box_added, boxed_features, std::back_inserter(to_deselect));
-		for (auto feature : to_deselect)
-			sel_features.erase(feature);
-
-		return;
-	}
-
-	if (mode == 1) {
-		spline.back().EndPoint() = mouse_pos;
-		features.back().pos = mouse_pos;
-	}
-
-	// Insert bicubic
-	else if (mode == 2) {
-		SplineCurve &curve = spline.back();
-		curve.EndPoint() = mouse_pos;
-
-		// Control points
-		if (spline.size() > 1) {
-			SplineCurve &c0 = spline.back();
-			float len = (curve.p4 - curve.p1).Len();
-			curve.p2 = (c0.type == SplineCurve::LINE ? c0.p2 - c0.p1 : c0.p4 - c0.p3).Unit() * (0.25f * len) + curve.p1;
-		}
-		else
-			curve.p2 = curve.p1 * 0.75 + curve.p4 * 0.25;
-		curve.p3 = curve.p1 * 0.25 + curve.p4 * 0.75;
-		MakeFeatures();
-	}
-
-	// Freehand
-	else if (mode == 6 || mode == 7) {
-		// See if distance is enough
-		Vector2D const& last = spline.back().EndPoint();
-		float len = (last - mouse_pos).SquareLen();
-		if ((mode == 6 && len >= 900) || (mode == 7 && len >= 3600)) {
-			spline.emplace_back(last, mouse_pos);
-			MakeFeature(spline.size() - 1);
-		}
-	}
-
-	if (mode == 3 || mode == 4) return;
-
-	// Smooth spline
-	if (!holding && mode == 7)
-		spline.Smooth();
-
-	// End freedraw
-	if (!holding && (mode == 6 || mode == 7)) {
-		SetMode(0);
-		MakeFeatures();
-	}
+void VisualToolVectorClip::UpdateHold()
+{
+    // Box selection
+    if (mode == 0) {
+        std::set<Feature *> boxed_features;
+        Vector2D p1 = drag_start.Min(mouse_pos);
+        Vector2D p2 = drag_start.Max(mouse_pos);
+        for (auto &feature : features) {
+            if (in_box(p1, p2, feature.pos))
+                boxed_features.insert(&feature);
+        }
+
+        // Keep track of which features were selected by the box selection so
+        // that only those are deselected if the user is holding ctrl
+        boost::set_difference(boxed_features, sel_features,
+                              std::inserter(box_added, end(box_added)));
+
+        boost::copy(boxed_features, std::inserter(sel_features, end(sel_features)));
+
+        std::vector<Feature *> to_deselect;
+        boost::set_difference(box_added, boxed_features, std::back_inserter(to_deselect));
+        for (auto feature : to_deselect)
+            sel_features.erase(feature);
+
+        return;
+    }
+
+    if (mode == 1) {
+        spline.back().EndPoint() = mouse_pos;
+        features.back().pos = mouse_pos;
+    }
+
+    // Insert bicubic
+    else if (mode == 2) {
+        SplineCurve &curve = spline.back();
+        curve.EndPoint() = mouse_pos;
+
+        // Control points
+        if (spline.size() > 1) {
+            SplineCurve &c0 = spline.back();
+            float len = (curve.p4 - curve.p1).Len();
+            curve.p2 = (c0.type == SplineCurve::LINE ? c0.p2 - c0.p1 : c0.p4 - c0.p3).Unit() * (0.25f * len) + curve.p1;
+        }
+        else
+            curve.p2 = curve.p1 * 0.75 + curve.p4 * 0.25;
+        curve.p3 = curve.p1 * 0.25 + curve.p4 * 0.75;
+        MakeFeatures();
+    }
+
+    // Freehand
+    else if (mode == 6 || mode == 7) {
+        // See if distance is enough
+        Vector2D const &last = spline.back().EndPoint();
+        float len = (last - mouse_pos).SquareLen();
+        if ((mode == 6 && len >= 900) || (mode == 7 && len >= 3600)) {
+            spline.emplace_back(last, mouse_pos);
+            MakeFeature(spline.size() - 1);
+        }
+    }
+
+    if (mode == 3 || mode == 4) return;
+
+    // Smooth spline
+    if (!holding && mode == 7)
+        spline.Smooth();
+
+    // End freedraw
+    if (!holding && (mode == 6 || mode == 7)) {
+        SetMode(0);
+        MakeFeatures();
+    }
 }
 
-void VisualToolVectorClip::DoRefresh() {
-	if (!active_line) return;
+void VisualToolVectorClip::DoRefresh()
+{
+    if (!active_line) return;
 
-	int scale;
-	std::string vect = GetLineVectorClip(active_line, scale, inverse);
-	spline.SetScale(scale);
-	spline.DecodeFromAss(vect);
+    int scale;
+    std::string vect = GetLineVectorClip(active_line, scale, inverse);
+    spline.SetScale(scale);
+    spline.DecodeFromAss(vect);
 
-	MakeFeatures();
+    MakeFeatures();
 }
diff --git a/src/visual_tool_vector_clip.h b/src/visual_tool_vector_clip.h
index ae88213a6355ccc0f58abdf2bb9eb61082bdd456..71bb1f5adc7150ff88c0a7997f9945964ab22842 100644
--- a/src/visual_tool_vector_clip.h
+++ b/src/visual_tool_vector_clip.h
@@ -24,40 +24,40 @@ class wxToolBar;
 /// @brief VisualDraggableFeature with information about a feature's location
 ///        in the spline
 struct VisualToolVectorClipDraggableFeature final : public VisualDraggableFeature {
-	/// Which curve in the spline this feature is a point on
-	size_t idx = 0;
-	/// 0-3; indicates which part of the curve this point is
-	int point = 0;
+    /// Which curve in the spline this feature is a point on
+    size_t idx = 0;
+    /// 0-3; indicates which part of the curve this point is
+    int point = 0;
 };
 
 class VisualToolVectorClip final : public VisualTool<VisualToolVectorClipDraggableFeature> {
-	Spline spline; /// The current spline
-	wxToolBar *toolBar = nullptr; /// The subtoolbar
-	int mode = 0; /// 0-7
-	bool inverse = false; /// is iclip?
+    Spline spline; /// The current spline
+    wxToolBar *toolBar = nullptr; /// The subtoolbar
+    int mode = 0; /// 0-7
+    bool inverse = false; /// is iclip?
 
-	std::set<Feature *> box_added;
+    std::set<Feature *> box_added;
 
-	/// @brief Set the mode
-	/// @param mode 0-7
-	void SetMode(int mode);
+    /// @brief Set the mode
+    /// @param mode 0-7
+    void SetMode(int mode);
 
-	void Save();
-	void Commit(wxString message="") override;
+    void Save();
+    void Commit(wxString message = "") override;
 
-	void MakeFeature(size_t idx);
-	void MakeFeatures();
+    void MakeFeature(size_t idx);
+    void MakeFeatures();
 
-	bool InitializeHold() override;
-	void UpdateHold() override;
+    bool InitializeHold() override;
+    void UpdateHold() override;
 
-	void UpdateDrag(Feature *feature) override;
-	bool InitializeDrag(Feature *feature) override;
+    void UpdateDrag(Feature *feature) override;
+    bool InitializeDrag(Feature *feature) override;
 
-	void DoRefresh() override;
-	void Draw() override;
+    void DoRefresh() override;
+    void Draw() override;
 
 public:
-	VisualToolVectorClip(VideoDisplay *parent, agi::Context *context);
-	void SetToolbar(wxToolBar *tb) override;
+    VisualToolVectorClip(VideoDisplay *parent, agi::Context *context);
+    void SetToolbar(wxToolBar *tb) override;
 };
diff --git a/tools/astyle.bash b/tools/astyle.bash
index 900cf32d6a7f521a1dd3ce7cc49ffb924b4f1b4b..e37303069b0516014b8af0a3f35263aa717e6986 100755
--- a/tools/astyle.bash
+++ b/tools/astyle.bash
@@ -22,7 +22,7 @@ OPTIONS='
     --lineend=linux
     --attach-inlines --attach-namespaces --attach-classes --attach-extern-c --attach-closing-while --close-templates
     --min-conditional-indent=0
-    --indent=tab
+    --indent=spaces=4
     --pad-oper --pad-comma --pad-header
     --align-pointer=name --align-reference=name
     --break-closing-braces