diff --git a/libaegisub/common/cajun/elements.cpp b/libaegisub/common/cajun/elements.cpp
index a39341f4396c4bf989cd2bdc3601460f6af274ea..7e39e826099ea7c24a238bf550eb169398068bb4 100644
--- a/libaegisub/common/cajun/elements.cpp
+++ b/libaegisub/common/cajun/elements.cpp
@@ -38,7 +38,7 @@ namespace {
 	};
 
 	template<class T>
-	struct CastVisitor : public CastVisitorBase {
+	struct CastVisitor final : public CastVisitorBase {
 		T *element;
 		CastVisitor() : element(0) { }
 		void Visit(T& ele) { element = &ele; }
@@ -64,7 +64,7 @@ public:
 
 
 template <typename ElementTypeT>
-class UnknownElement::Imp_T : public UnknownElement::Imp
+class UnknownElement::Imp_T final : public UnknownElement::Imp
 {
 public:
    Imp_T(const ElementTypeT& element) : m_Element(element) { }
diff --git a/libaegisub/common/charset.cpp b/libaegisub/common/charset.cpp
index c92293e1c3b01f63817bf7c445109b68d8a95ce4..7a904f692cc13e1f2764b357a8680c71b5620139 100644
--- a/libaegisub/common/charset.cpp
+++ b/libaegisub/common/charset.cpp
@@ -35,7 +35,7 @@
 namespace {
 using namespace agi::charset;
 
-class UCDetect : public nsUniversalDetector {
+class UCDetect final : public nsUniversalDetector {
 	/// List of detected character sets
 	CharsetListDetected list;
 
diff --git a/libaegisub/common/charset_6937.h b/libaegisub/common/charset_6937.h
index 58953709298f2b92d0bb0bb7c2ccc44f7eca07f4..b95113bf08871e58a660b1006b960e3cbe8d2573 100644
--- a/libaegisub/common/charset_6937.h
+++ b/libaegisub/common/charset_6937.h
@@ -25,7 +25,7 @@ namespace agi { namespace charset {
 ///
 /// 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 : public Converter {
+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;
 
diff --git a/libaegisub/common/charset_conv.cpp b/libaegisub/common/charset_conv.cpp
index 9bd77bb78f8cbc4c30d0fce2cfbeb720ae7c592a..9ed31ad3c6321b2f97d945c538cf600162f85cf6 100644
--- a/libaegisub/common/charset_conv.cpp
+++ b/libaegisub/common/charset_conv.cpp
@@ -135,7 +135,7 @@ namespace {
 	}
 
 #ifdef ICONV_POSIX
-	class ConverterImpl : public agi::charset::Converter {
+	class ConverterImpl final : public agi::charset::Converter {
 		size_t bomSize;
 		iconv_t cd;
 	public:
@@ -175,7 +175,7 @@ namespace {
 
 #else
 
-	class ConverterImpl : public iconv_fallbacks, public agi::charset::Converter {
+	class ConverterImpl final : public iconv_fallbacks, public agi::charset::Converter {
 		size_t bomSize;
 		char invalidRep[8];
 		size_t invalidRepSize;
diff --git a/libaegisub/common/dispatch.cpp b/libaegisub/common/dispatch.cpp
index e3a5baa020870c5e3e6b2aa85106e08179bd782a..5ef2682e176076534731ba7f759a92652e2311ef 100644
--- a/libaegisub/common/dispatch.cpp
+++ b/libaegisub/common/dispatch.cpp
@@ -32,19 +32,19 @@ namespace {
 	std::function<void (agi::dispatch::Thunk)> invoke_main;
 	std::atomic<uint_fast32_t> threads_running;
 
-	class MainQueue : public agi::dispatch::Queue {
+	class MainQueue final : public agi::dispatch::Queue {
 		void DoInvoke(agi::dispatch::Thunk thunk) override {
 			invoke_main(thunk);
 		}
 	};
 
-	class BackgroundQueue : public agi::dispatch::Queue {
+	class BackgroundQueue final : public agi::dispatch::Queue {
 		void DoInvoke(agi::dispatch::Thunk thunk) override {
 			service->post(thunk);
 		}
 	};
 
-	class SerialQueue : public agi::dispatch::Queue {
+	class SerialQueue final : public agi::dispatch::Queue {
 		boost::asio::io_service::strand strand;
 
 		void DoInvoke(agi::dispatch::Thunk thunk) override {
diff --git a/libaegisub/common/option_visit.h b/libaegisub/common/option_visit.h
index 76f666169ecbd17875a3f711ac1ecedb160fd3b5..33eb342d934fb2e681e5c2ce07bc358de2a61b97 100644
--- a/libaegisub/common/option_visit.h
+++ b/libaegisub/common/option_visit.h
@@ -31,7 +31,7 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionJsonValueArray, OptionJsonValueError, "opt
 DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionJsonValueSingle, OptionJsonValueError, "options/value")
 DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionJsonValueNull, OptionJsonValueError, "options/value")
 
-class ConfigVisitor : public json::ConstVisitor {
+class ConfigVisitor final : public json::ConstVisitor {
 	/// Option map being populated
 	OptionValueMap &values;
 	/// Option name prefix to add to read names
diff --git a/libaegisub/common/parser.cpp b/libaegisub/common/parser.cpp
index 9e36db3c0ecfba1d3a3bdf69431dbd1bea30badb..a0ed8a0f09ce5dc327238690ff0b33d172f89d99 100644
--- a/libaegisub/common/parser.cpp
+++ b/libaegisub/common/parser.cpp
@@ -109,7 +109,7 @@ struct color_grammar : qi::grammar<Iterator, agi::Color()> {
 };
 
 template <typename Lexer>
-struct dialogue_tokens : lex::lexer<Lexer> {
+struct dialogue_tokens final : lex::lexer<Lexer> {
 	int paren_depth;
 
 	template<typename KT>
diff --git a/libaegisub/include/libaegisub/cajun/reader.h b/libaegisub/include/libaegisub/cajun/reader.h
index a0d21c50259536e499503506adad282517d99bfa..16e18b8ba45ac62b2b9e78432498da3572251c62 100644
--- a/libaegisub/include/libaegisub/cajun/reader.h
+++ b/libaegisub/include/libaegisub/cajun/reader.h
@@ -29,7 +29,7 @@ public:
 
 	// thrown during the first phase of reading. generally catches low-level
 	// problems such as errant characters or corrupt/incomplete documents
-	class ScanException : public Exception {
+	class ScanException final : public Exception {
 	public:
 		ScanException(std::string const& sMessage, Reader::Location locError)
 		: Exception(sMessage)
@@ -41,7 +41,7 @@ public:
 
 	// thrown during the second phase of reading. generally catches
 	// higher-level problems such as missing commas or brackets
-	class ParseException : public Exception {
+	class ParseException final : public Exception {
 	public:
 		ParseException(std::string const& sMessage, Reader::Location locTokenBegin, Reader::Location locTokenEnd)
 		: Exception(sMessage)
diff --git a/libaegisub/include/libaegisub/cajun/writer.h b/libaegisub/include/libaegisub/cajun/writer.h
index f663927e3c7841df382dc32135c3ebfaa7619810..27dc4b1e4cd2c598c3f774a3ac3bb27e068c395d 100644
--- a/libaegisub/include/libaegisub/cajun/writer.h
+++ b/libaegisub/include/libaegisub/cajun/writer.h
@@ -15,7 +15,7 @@ Author: Terry Caton
 
 namespace json {
 
-class Writer : private ConstVisitor {
+class Writer final : private ConstVisitor {
 	Writer(std::ostream& ostr);
 	void Write(const Object& object);
 	void Write(const Array& array);
diff --git a/libaegisub/include/libaegisub/line_iterator.h b/libaegisub/include/libaegisub/line_iterator.h
index 9e86ed1c0a78515f9d239e9cc27186e4a898dfd5..a040e8fceacbff83a57997b3f68ef714bf97daad 100644
--- a/libaegisub/include/libaegisub/line_iterator.h
+++ b/libaegisub/include/libaegisub/line_iterator.h
@@ -32,7 +32,7 @@ namespace agi {
 /// @class line_iterator
 /// @brief An iterator over lines in a stream
 template<class OutputType = std::string>
-class line_iterator : public std::iterator<std::input_iterator_tag, OutputType> {
+class line_iterator final : public std::iterator<std::input_iterator_tag, OutputType> {
 	std::istream *stream; ///< Stream to iterator over
 	OutputType value; ///< Value to return when this is dereference
 	std::shared_ptr<agi::charset::IconvWrapper> conv;
diff --git a/libaegisub/include/libaegisub/log.h b/libaegisub/include/libaegisub/log.h
index 05b5766ec384207b144121e7c49a75fae43490d2..da965f458c36c914a9a67da4aeb84f1ad973340c 100644
--- a/libaegisub/include/libaegisub/log.h
+++ b/libaegisub/include/libaegisub/log.h
@@ -120,7 +120,7 @@ public:
 };
 
 /// A simple emitter which writes the log to a file in json format
-class JsonEmitter : public Emitter {
+class JsonEmitter final : public Emitter {
 	std::unique_ptr<std::ostream> fp;
 
 	void WriteTime(const char *key);
diff --git a/libaegisub/include/libaegisub/option.h b/libaegisub/include/libaegisub/option.h
index 583209d7be08a0ee6180c6a9fe86f954f87e613e..00d4c0ec359ec83a5e83bd4462037bd405acde5f 100644
--- a/libaegisub/include/libaegisub/option.h
+++ b/libaegisub/include/libaegisub/option.h
@@ -39,7 +39,7 @@ DEFINE_SIMPLE_EXCEPTION_NOINNER(OptionErrorDuplicateKey, OptionError, "options/d
 
 class OptionValue;
 
-class OptionValueMap : public std::map<std::string, std::unique_ptr<OptionValue>> {
+class OptionValueMap final : public std::map<std::string, std::unique_ptr<OptionValue>> {
 private:
 	OptionValueMap(const OptionValueMap& x);
 	OptionValueMap& operator=(const OptionValueMap& x);
diff --git a/libaegisub/include/libaegisub/option_value.h b/libaegisub/include/libaegisub/option_value.h
index 1bcf8ff65d0d886956696712cc66a495ad06976a..185e68a3f49abaa9196fa72a5c40a2fc8d7a4393 100644
--- a/libaegisub/include/libaegisub/option_value.h
+++ b/libaegisub/include/libaegisub/option_value.h
@@ -99,7 +99,7 @@ public:
 };
 
 #define CONFIG_OPTIONVALUE(type_name, type)                                                   \
-	class OptionValue##type_name : public OptionValue {                                       \
+	class OptionValue##type_name final : public OptionValue {                                 \
 		type value;                                                                           \
 		type value_default;                                                                   \
 	public:                                                                                   \
@@ -121,7 +121,7 @@ CONFIG_OPTIONVALUE(Color, Color)
 CONFIG_OPTIONVALUE(Bool, bool)
 
 #define CONFIG_OPTIONVALUE_LIST(type_name, type)                                              \
-	class OptionValueList##type_name : public OptionValue {                                   \
+	class OptionValueList##type_name final : public OptionValue {                             \
 		std::vector<type> array;                                                              \
 		std::vector<type> array_default;                                                      \
 		std::string name;                                                                     \
diff --git a/libaegisub/include/libaegisub/owning_intrusive_list.h b/libaegisub/include/libaegisub/owning_intrusive_list.h
index d5b88e1b594b41ccb5233867cfd04104be91b5f0..63173583556542ab9fa17580773aa9e0d0c87805 100644
--- a/libaegisub/include/libaegisub/owning_intrusive_list.h
+++ b/libaegisub/include/libaegisub/owning_intrusive_list.h
@@ -21,7 +21,7 @@
 namespace agi {
 
 template<typename T>
-class owning_intrusive_list : private boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type {
+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;
 public:
 	using base::back;
diff --git a/libaegisub/include/libaegisub/signal.h b/libaegisub/include/libaegisub/signal.h
index 4067233bc1d50724dde28dc5d3cebf34d084200a..4d220187db36ad8c7084d32b28d42d2f4805d46e 100644
--- a/libaegisub/include/libaegisub/signal.h
+++ b/libaegisub/include/libaegisub/signal.h
@@ -133,7 +133,7 @@ namespace detail {
 
 	/// @brief Templated common code for signals
 	template<class Slot>
-	class SignalBaseImpl : public SignalBase {
+	class SignalBaseImpl final : public SignalBase {
 	protected:
 		typedef boost::container::map<ConnectionToken*, Slot> SlotMap;
 
@@ -187,7 +187,7 @@ namespace detail {
 /// @param Arg1 Type of first argument to pass to slots
 /// @param Arg2 Type of second argument to pass to slots
 template<class Arg1 = void, class Arg2 = void>
-class Signal : public detail::SignalBaseImpl<std::function<void (Arg1, Arg2)> > {
+class Signal final : public detail::SignalBaseImpl<std::function<void (Arg1, Arg2)> > {
 	typedef detail::SignalBaseImpl<std::function<void (Arg1, Arg2)> > super;
 	using super::Blocked;
 	using super::slots;
@@ -266,7 +266,7 @@ public:
 /// @class Signal
 /// @brief Zero-argument signal
 template<>
-class Signal<void> : public detail::SignalBaseImpl<std::function<void ()> > {
+class Signal<void> final : public detail::SignalBaseImpl<std::function<void ()> > {
 	typedef detail::SignalBaseImpl<std::function<void ()> > super;
 	using super::Blocked;
 	using super::slots;
diff --git a/src/ass_attachment.h b/src/ass_attachment.h
index 7c0aafffcdd2ee74fe809ccd06d6a34860e36b0f..2a7efe24965e056aeacc56907f8c1d4bc9d52975 100644
--- a/src/ass_attachment.h
+++ b/src/ass_attachment.h
@@ -22,7 +22,7 @@
 #include <vector>
 
 /// @class AssAttachment
-class AssAttachment : public AssEntry {
+class AssAttachment final : public AssEntry {
 	/// ASS uuencoded entry data, including header.
 	boost::flyweight<std::string> entry_data;
 
diff --git a/src/ass_dialogue.h b/src/ass_dialogue.h
index 7dae6ae37338ea7fa212a775486350ca463b6915..d0a8d0f4f11dfcb99c1fdece8bc04937120a7cc1 100644
--- a/src/ass_dialogue.h
+++ b/src/ass_dialogue.h
@@ -83,20 +83,20 @@ public:
 	virtual std::string GetText() { return text; }
 };
 
-class AssDialogueBlockPlain : public AssDialogueBlock {
+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) { }
 };
 
-class AssDialogueBlockComment : public AssDialogueBlock {
+class AssDialogueBlockComment final : public AssDialogueBlock {
 public:
 	AssBlockType GetType() const override { return AssBlockType::COMMENT; }
 	AssDialogueBlockComment(std::string const& text = std::string()) : AssDialogueBlock("{" + text + "}") { }
 };
 
-class AssDialogueBlockDrawing : public AssDialogueBlock {
+class AssDialogueBlockDrawing final : public AssDialogueBlock {
 public:
 	using AssDialogueBlock::text;
 	int Scale;
@@ -105,7 +105,7 @@ public:
 	AssDialogueBlockDrawing(std::string const& text, int scale) : AssDialogueBlock(text), Scale(scale) { }
 };
 
-class AssDialogueBlockOverride : public AssDialogueBlock {
+class AssDialogueBlockOverride final : public AssDialogueBlock {
 public:
 	AssDialogueBlockOverride(std::string const& text = std::string()) : AssDialogueBlock(text) { }
 
@@ -150,7 +150,7 @@ struct AssDialogueBase {
 	boost::flyweight<std::string> Text;
 };
 
-class AssDialogue : public AssEntry, public AssDialogueBase, public AssEntryListHook {
+class AssDialogue final : public AssEntry, public AssDialogueBase, public AssEntryListHook {
 	std::string GetData(bool ssa) const;
 
 	/// @brief Parse raw ASS data into everything else
diff --git a/src/ass_info.h b/src/ass_info.h
index de4601749a0c46e83d457cbc0093bcd114087a32..3f491e8f76d6e9e94792ce3def0fefb01b1391ab 100644
--- a/src/ass_info.h
+++ b/src/ass_info.h
@@ -18,7 +18,7 @@
 
 #include <boost/algorithm/string/predicate.hpp>
 
-class AssInfo : public AssEntry {
+class AssInfo final : public AssEntry {
 	std::string key;
 	std::string value;
 
diff --git a/src/ass_override.h b/src/ass_override.h
index 9e2119c48371dcbaaca59c212c46c1276c431292..3da32f525e0727630022140fa2bb3f981cda8f05 100644
--- a/src/ass_override.h
+++ b/src/ass_override.h
@@ -62,7 +62,7 @@ enum class VariableDataType {
 };
 
 /// A single parameter to an override tag
-class AssOverrideParameter : boost::noncopyable {
+class AssOverrideParameter final : boost::noncopyable {
 	std::string value;
 	mutable std::unique_ptr<AssDialogueBlockOverride> block;
 	VariableDataType type;
@@ -87,7 +87,7 @@ public:
 	}
 };
 
-class AssOverrideTag : boost::noncopyable {
+class AssOverrideTag final : boost::noncopyable {
 	bool valid;
 
 public:
diff --git a/src/ass_style.h b/src/ass_style.h
index 61a6a1fef43fb657e1abb91eaa123cff2b54ec09..432db702afa9638adae44e3423da462df070b73b 100644
--- a/src/ass_style.h
+++ b/src/ass_style.h
@@ -39,7 +39,7 @@
 #include <array>
 #include <wx/arrstr.h>
 
-class AssStyle : public AssEntry, public AssEntryListHook {
+class AssStyle final : public AssEntry, public AssEntryListHook {
 	std::string data;
 
 public:
diff --git a/src/audio_box.h b/src/audio_box.h
index ee5ca6078692e78860f903813fa8ea6d8e8f417c..a449ddc317979f3c3b9643fb485b863c64f3c71c 100644
--- a/src/audio_box.h
+++ b/src/audio_box.h
@@ -53,7 +53,7 @@ class wxSlider;
 
 /// @class AudioBox
 /// @brief Panel with audio playback and timing controls, also containing an AudioDisplay
-class AudioBox : public wxSashWindow {
+class AudioBox final : public wxSashWindow {
 	/// The controller controlling this audio box
 	AudioController *controller;
 
diff --git a/src/audio_controller.h b/src/audio_controller.h
index 7f7ba88a549d6ed44f1b747d8947d4bb1debc554..546cb89f75a42144ef9ab8d0bb5854adfde64e7f 100644
--- a/src/audio_controller.h
+++ b/src/audio_controller.h
@@ -63,7 +63,7 @@ class TimeRange;
 /// providers or players owned by a controller. If some operation that isn't
 /// possible in the existing design is needed, the controller should be
 /// extended in some way to allow it.
-class AudioController : public wxEvtHandler {
+class AudioController final : public wxEvtHandler {
 	/// Project context this controller belongs to
 	agi::Context *context;
 
diff --git a/src/audio_display.cpp b/src/audio_display.cpp
index 5d33fc6af64bbfe0a831717310ac1ec757245e77..a8fe3c7383172dd1d21c1d0e45db3d386ed0f7d2 100644
--- a/src/audio_display.cpp
+++ b/src/audio_display.cpp
@@ -108,7 +108,7 @@ public:
 	wxColour Selection() const { return focused ? sel_focused_colour : sel_colour; }
 };
 
-class AudioDisplayScrollbar : public AudioDisplayInteractionObject {
+class AudioDisplayScrollbar final : public AudioDisplayInteractionObject {
 	static const int height = 15;
 	static const int min_width = 10;
 
@@ -247,7 +247,7 @@ public:
 const int AudioDisplayScrollbar::min_width;
 
 
-class AudioDisplayTimeline : public AudioDisplayInteractionObject {
+class AudioDisplayTimeline final : public AudioDisplayInteractionObject {
 	int duration;        ///< Total duration in ms
 	double ms_per_pixel; ///< Milliseconds per pixel
 	int pixel_left;      ///< Leftmost visible pixel (i.e. scroll position)
@@ -460,7 +460,7 @@ public:
 	}
 };
 
-class AudioMarkerInteractionObject : public AudioDisplayInteractionObject {
+class AudioMarkerInteractionObject final : public AudioDisplayInteractionObject {
 	// Object-pair being interacted with
 	std::vector<AudioMarker*> markers;
 	AudioTimingController *timing_controller;
@@ -502,7 +502,7 @@ public:
 	int GetPosition() const { return markers.front()->GetPosition(); }
 };
 
-class AudioStyleRangeMerger : public AudioRenderingStyleRanges {
+class AudioStyleRangeMerger final : public AudioRenderingStyleRanges {
 	typedef std::map<int, AudioRenderingStyle> style_map;
 public:
 	typedef style_map::iterator iterator;
diff --git a/src/audio_karaoke.h b/src/audio_karaoke.h
index e026cb282aa793bee0a67eacf7fe4aaa0198458e..c7c16942cb6b13eaeec6c6fd967a496545a2c081 100644
--- a/src/audio_karaoke.h
+++ b/src/audio_karaoke.h
@@ -67,7 +67,7 @@ namespace agi { struct Context; }
 /// actually updated until the line is committed (which if auto-commit timing
 /// changes is on, will happen as soon as the user adjusts the timing of the
 /// new syllable).
-class AudioKaraoke : public wxWindow {
+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
diff --git a/src/audio_marker.cpp b/src/audio_marker.cpp
index 6c0e4c7a366877e293c9e2227c91e2157423d44a..de0a1953e9ebd317930e6171927836f7f3c8c9e5 100644
--- a/src/audio_marker.cpp
+++ b/src/audio_marker.cpp
@@ -32,7 +32,7 @@
 
 #include <algorithm>
 
-class AudioMarkerKeyframe : public AudioMarker {
+class AudioMarkerKeyframe final : public AudioMarker {
 	Pen *style;
 	int position;
 public:
@@ -86,7 +86,7 @@ void AudioMarkerProviderKeyframes::GetMarkers(TimeRange const& range, AudioMarke
 		out.push_back(&*a);
 }
 
-class VideoPositionMarker : public AudioMarker {
+class VideoPositionMarker final : public AudioMarker {
 	Pen style;
 	int position;
 
diff --git a/src/audio_marker.h b/src/audio_marker.h
index ca32e603d830e8475ab0b78ccb11f43053ed1d88..fae6607794530d0b0c7871ea8818d096b5f2593f 100644
--- a/src/audio_marker.h
+++ b/src/audio_marker.h
@@ -122,7 +122,7 @@ public:
 
 
 /// Marker provider for video keyframes
-class AudioMarkerProviderKeyframes : public AudioMarkerProvider {
+class AudioMarkerProviderKeyframes final : public AudioMarkerProvider {
 	/// Video controller to get keyframes from
 	VideoContext *vc;
 
@@ -155,7 +155,7 @@ public:
 };
 
 /// Marker provider for the current video playback position
-class VideoPositionMarkerProvider : public AudioMarkerProvider {
+class VideoPositionMarkerProvider final : public AudioMarkerProvider {
 	VideoContext *vc;
 
 	std::unique_ptr<VideoPositionMarker> marker;
@@ -174,8 +174,8 @@ public:
 };
 
 /// Marker provider for lines every second
-class SecondsMarkerProvider : public AudioMarkerProvider {
-	struct Marker : public AudioMarker {
+class SecondsMarkerProvider final : public AudioMarkerProvider {
+	struct Marker final : public AudioMarker {
 		Pen *style;
 		int position;
 
diff --git a/src/audio_player_alsa.h b/src/audio_player_alsa.h
index 99929091f12d4e36f24357bd45f9ae6a9a9e5ee8..a524f7894fd2c671c343cb86627538d408318ba2 100644
--- a/src/audio_player_alsa.h
+++ b/src/audio_player_alsa.h
@@ -40,7 +40,7 @@
 
 struct PlaybackState;
 
-class AlsaPlayer : public AudioPlayer {
+class AlsaPlayer final : public AudioPlayer {
 	std::unique_ptr<PlaybackState> ps;
 	pthread_t thread;
 
diff --git a/src/audio_player_dsound.h b/src/audio_player_dsound.h
index a77360a79e69681a87e81b89930758233c037be1..ec9fd244ecd3444dd46328d8e0ada7bca1f62087 100644
--- a/src/audio_player_dsound.h
+++ b/src/audio_player_dsound.h
@@ -43,7 +43,7 @@
 
 class DirectSoundPlayer;
 
-class DirectSoundPlayerThread : public wxThread {
+class DirectSoundPlayerThread final : public wxThread {
 	DirectSoundPlayer *parent;
 	HANDLE stopnotify;
 
@@ -55,7 +55,7 @@ public:
 	wxThread::ExitCode Entry();
 };
 
-class DirectSoundPlayer : public AudioPlayer {
+class DirectSoundPlayer final : public AudioPlayer {
 	friend class DirectSoundPlayerThread;
 
 	volatile bool playing;
diff --git a/src/audio_player_dsound2.cpp b/src/audio_player_dsound2.cpp
index 9dc495f3d584fdc8d509f909b22509e1524317ce..a91c1acfd083fff4531bae3bfbde542d2170ac74 100644
--- a/src/audio_player_dsound2.cpp
+++ b/src/audio_player_dsound2.cpp
@@ -117,7 +117,7 @@ struct COMObjectRetainer {
 };
 
 /// @brief RAII wrapper around Win32 HANDLE type
-struct Win32KernelHandle : public agi::scoped_holder<HANDLE, BOOL (__stdcall *)(HANDLE)> {
+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)
diff --git a/src/audio_player_dsound2.h b/src/audio_player_dsound2.h
index f1e4619d01a9c6bfa3443f16631f9a94dd74d26c..62e0b0048459f8f82e36f4bc80735df942b20891 100644
--- a/src/audio_player_dsound2.h
+++ b/src/audio_player_dsound2.h
@@ -44,7 +44,7 @@ class DirectSoundPlayer2Thread;
 /// The core design idea is to have a playback thread that owns the DirectSound COM objects
 /// and performs all playback operations, and use the player object as a proxy to
 /// send commands to the playback thread.
-class DirectSoundPlayer2 : public AudioPlayer {
+class DirectSoundPlayer2 final : public AudioPlayer {
 	/// The playback thread
 	std::unique_ptr<DirectSoundPlayer2Thread> thread;
 
diff --git a/src/audio_player_openal.h b/src/audio_player_openal.h
index bdb64dc223ceef62dfb74f87ea7b04866d3ed0a6..bab9e7137e1213cbc3f21172725062c05ec8e673 100644
--- a/src/audio_player_openal.h
+++ b/src/audio_player_openal.h
@@ -51,7 +51,7 @@
 
 #include <wx/timer.h>
 
-class OpenALPlayer : public AudioPlayer, wxTimer {
+class OpenALPlayer final : public AudioPlayer, wxTimer {
 	/// Number of OpenAL buffers to use
 	static const ALsizei num_buffers = 8;
 
diff --git a/src/audio_player_oss.h b/src/audio_player_oss.h
index 6bc7631207dc02b21fa4ce25805615d8f05e65ab..178054442e6a9ee3d3850e986fa5be895e43580a 100644
--- a/src/audio_player_oss.h
+++ b/src/audio_player_oss.h
@@ -52,7 +52,7 @@ class AudioProvider;
 class OSSPlayer;
 
 /// Worker thread to asynchronously write audio data to the output device
-class OSSPlayerThread : public wxThread {
+class OSSPlayerThread final : public wxThread {
     /// Parent player
     OSSPlayer *parent;
 
@@ -65,7 +65,7 @@ public:
     wxThread::ExitCode Entry();
 };
 
-class OSSPlayer : public AudioPlayer {
+class OSSPlayer final : public AudioPlayer {
     friend class OSSPlayerThread;
 
     /// sample rate of audio
diff --git a/src/audio_player_portaudio.h b/src/audio_player_portaudio.h
index 1c5ad5767389d82ddf2a4ac99ba8300881c84322..4183cd1e6dbd3f050b060bd67fc4124b5b1dfcdf 100644
--- a/src/audio_player_portaudio.h
+++ b/src/audio_player_portaudio.h
@@ -49,7 +49,7 @@ class wxArrayString;
 /// @class PortAudioPlayer
 /// @brief PortAudio Player
 ///
-class PortAudioPlayer : public AudioPlayer {
+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;
diff --git a/src/audio_player_pulse.h b/src/audio_player_pulse.h
index 28865f5d94d5b7fd2b31f0550f040c2db0b367b5..7c190d44bbbf286fbbd04fd1d8427b759f5134d5 100644
--- a/src/audio_player_pulse.h
+++ b/src/audio_player_pulse.h
@@ -39,7 +39,7 @@
 
 class PulseAudioPlayer;
 
-class PulseAudioPlayer : public AudioPlayer {
+class PulseAudioPlayer final : public AudioPlayer {
 	float volume = 1.f;
 	bool is_playing = false;
 
diff --git a/src/audio_provider_avs.h b/src/audio_provider_avs.h
index fa78d50ae9bbdd5b973ab5543183b989ace39f5c..fe4e7c3a2692651b3598aca27233e84fd8ea8a96 100644
--- a/src/audio_provider_avs.h
+++ b/src/audio_provider_avs.h
@@ -38,7 +38,7 @@
 #include "avisynth.h"
 #include "avisynth_wrap.h"
 
-class AvisynthAudioProvider : public AudioProvider {
+class AvisynthAudioProvider final : public AudioProvider {
 	AviSynthWrapper avs_wrapper;
 	PClip clip;
 
diff --git a/src/audio_provider_convert.cpp b/src/audio_provider_convert.cpp
index 6fe1de0e77392c5b99c7ccb71f1d376886fbcd37..4e2c9b98e89b9dda217188629ef04feeefe62988 100644
--- a/src/audio_provider_convert.cpp
+++ b/src/audio_provider_convert.cpp
@@ -33,7 +33,7 @@
 
 /// Anything integral -> 16 bit signed machine-endian audio converter
 template<class Target>
-class BitdepthConvertAudioProvider : public AudioProviderWrapper {
+class BitdepthConvertAudioProvider final : public AudioProviderWrapper {
 	int src_bytes_per_sample;
 public:
 	BitdepthConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
@@ -74,7 +74,7 @@ public:
 
 /// Floating point -> 16 bit signed machine-endian audio converter
 template<class Source, class Target>
-class FloatConvertAudioProvider : public AudioProviderWrapper {
+class FloatConvertAudioProvider final : public AudioProviderWrapper {
 public:
 	FloatConvertAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
 		bytes_per_sample = sizeof(Target);
@@ -105,7 +105,7 @@ public:
 };
 
 /// Non-mono 16-bit signed machine-endian -> mono 16-bit signed machine endian converter
-class DownmixAudioProvider : public AudioProviderWrapper {
+class DownmixAudioProvider final : public AudioProviderWrapper {
 	int src_channels;
 public:
 	DownmixAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
@@ -136,7 +136,7 @@ public:
 
 /// Sample doubler with linear interpolation for the agi::util::make_unique<samples>
 /// Requires 16-bit mono input
-class SampleDoublingAudioProvider : public AudioProviderWrapper {
+class SampleDoublingAudioProvider final : public AudioProviderWrapper {
 public:
 	SampleDoublingAudioProvider(std::unique_ptr<AudioProvider> src) : AudioProviderWrapper(std::move(src)) {
 		if (source->GetBytesPerSample() != 2)
diff --git a/src/audio_provider_dummy.h b/src/audio_provider_dummy.h
index e63584a60fcd81d395cabf97ab37f3c8b3d38724..196e43ab53b83c42ffe852d53416a8397c19492e 100644
--- a/src/audio_provider_dummy.h
+++ b/src/audio_provider_dummy.h
@@ -34,7 +34,7 @@
 
 #include "include/aegisub/audio_provider.h"
 
-class DummyAudioProvider : public AudioProvider {
+class DummyAudioProvider final : public AudioProvider {
 	bool noise;
 	void FillBuffer(void *buf, int64_t start, int64_t count) const override;
 
diff --git a/src/audio_provider_ffmpegsource.h b/src/audio_provider_ffmpegsource.h
index 0946af7ea0669a402aea172677bb251ed1f3311e..184d392512a678015432db576790287b6e8e4d24 100644
--- a/src/audio_provider_ffmpegsource.h
+++ b/src/audio_provider_ffmpegsource.h
@@ -39,7 +39,7 @@
 
 /// @class FFmpegSourceAudioProvider
 /// @brief Implements audio loading with the FFMS library.
-class FFmpegSourceAudioProvider : public AudioProvider, FFmpegSourceProvider {
+class FFmpegSourceAudioProvider final : public AudioProvider, FFmpegSourceProvider {
 	/// audio source object
 	agi::scoped_holder<FFMS_AudioSource*, void (FFMS_CC *)(FFMS_AudioSource*)> AudioSource;
 
diff --git a/src/audio_provider_hd.cpp b/src/audio_provider_hd.cpp
index 235067fd4cc960c352f8cb500028a17c4836748c..3213dbedf25c50dd5b5ff6ce6c5782220fee94c5 100644
--- a/src/audio_provider_hd.cpp
+++ b/src/audio_provider_hd.cpp
@@ -71,7 +71,7 @@ agi::fs::path cache_path() {
 }
 
 /// A PCM audio provider for raw dumps with no header
-class RawAudioProvider : public PCMAudioProvider {
+class RawAudioProvider final : public PCMAudioProvider {
 public:
 	RawAudioProvider(agi::fs::path const& cache_filename, AudioProvider *src)
 	: PCMAudioProvider(cache_filename)
diff --git a/src/audio_provider_hd.h b/src/audio_provider_hd.h
index b36b8f67a3d3291e698fe056d941592a39f21a24..fa3d2b3c35baa460f0c3e91c8e262be73139b435 100644
--- a/src/audio_provider_hd.h
+++ b/src/audio_provider_hd.h
@@ -39,7 +39,7 @@ namespace agi {
 	class ProgressSink;
 }
 
-class HDAudioProvider : public AudioProviderWrapper {
+class HDAudioProvider final : public AudioProviderWrapper {
 	/// Name of the file which the decoded audio is written to
 	agi::fs::path diskCacheFilename;
 	/// Audio provider which reads from the decoded cache
diff --git a/src/audio_provider_lock.h b/src/audio_provider_lock.h
index 37f8c5c3ac62cf5f2a6bdf2dc71f36d7f472cb2e..e998b81c8ebf291203600b13eb2c88d3c4e3f343 100644
--- a/src/audio_provider_lock.h
+++ b/src/audio_provider_lock.h
@@ -21,7 +21,7 @@
 #include <memory>
 #include <mutex>
 
-class LockAudioProvider : public AudioProviderWrapper {
+class LockAudioProvider final : public AudioProviderWrapper {
 	mutable std::mutex mutex;
 
 	void FillBuffer(void *buf, int64_t start, int64_t count) const override;
diff --git a/src/audio_provider_pcm.cpp b/src/audio_provider_pcm.cpp
index 8e3377814041be193f409aba5ace0e649391c2bd..b01b18ab4a387d5ba56487f1691e0203baa1a73a 100644
--- a/src/audio_provider_pcm.cpp
+++ b/src/audio_provider_pcm.cpp
@@ -351,7 +351,7 @@ static const uint8_t w64Guiddata[16] = {
 /// @brief Sony Wave64 audio provider
 ///
 /// http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
-class Wave64AudioProvider : public PCMAudioProvider {
+class Wave64AudioProvider final : public PCMAudioProvider {
 	// Here's some copy-paste from the FFmpegSource2 code
 
 	/// http://msdn.microsoft.com/en-us/library/dd757720(VS.85).aspx
diff --git a/src/audio_provider_ram.h b/src/audio_provider_ram.h
index 8891951bc1ca515deffdd0f07ef3dd70915ac126..a9056d40dc7deaa5de1afa652e0e26ffc47f3245 100644
--- a/src/audio_provider_ram.h
+++ b/src/audio_provider_ram.h
@@ -42,7 +42,7 @@ namespace agi {
 	class ProgressSink;
 }
 
-class RAMAudioProvider : public AudioProviderWrapper {
+class RAMAudioProvider final : public AudioProviderWrapper {
 #ifdef _MSC_VER
 	boost::container::stable_vector<char[1 << 22]> blockcache;
 #else
diff --git a/src/audio_renderer_spectrum.h b/src/audio_renderer_spectrum.h
index b322c1c3208e1c6365b16ecf12a2be79990ddfdf..d4641f37de329d59e1c04d18b2b8f280e04dc810 100644
--- a/src/audio_renderer_spectrum.h
+++ b/src/audio_renderer_spectrum.h
@@ -52,7 +52,7 @@ struct AudioSpectrumCacheBlockFactory;
 ///
 /// Renders frequency-power spectrum graphs of PCM audio data using a derivation function
 /// such as the fast fourier transform.
-class AudioSpectrumRenderer : public AudioRendererBitmapProvider {
+class AudioSpectrumRenderer final : public AudioRendererBitmapProvider {
 	friend struct AudioSpectrumCacheBlockFactory;
 
 	/// Internal cache management for the spectrum
diff --git a/src/audio_renderer_waveform.h b/src/audio_renderer_waveform.h
index e1f80d09a5542b7dcf0d9c6850820ff25dc5e79a..79213421bb196b444b2dc20f200eb1567168f7dc 100644
--- a/src/audio_renderer_waveform.h
+++ b/src/audio_renderer_waveform.h
@@ -40,7 +40,7 @@ class AudioColorScheme;
 
 #include "audio_renderer.h"
 
-class AudioWaveformRenderer : public AudioRendererBitmapProvider {
+class AudioWaveformRenderer final : public AudioRendererBitmapProvider {
 	/// Colour tables used for rendering
 	std::vector<AudioColorScheme> colors;
 
diff --git a/src/audio_timing_dialogue.cpp b/src/audio_timing_dialogue.cpp
index adc0468db7cbc65becce0f2f615c9a551661e26b..15357758dbe531b1047d17763de869eed2fc9ac7 100644
--- a/src/audio_timing_dialogue.cpp
+++ b/src/audio_timing_dialogue.cpp
@@ -54,7 +54,7 @@ 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 : public AudioMarker {
+class DialogueTimingMarker final : public AudioMarker {
 	/// Current ms position of this marker
 	int position;
 
@@ -285,7 +285,7 @@ void DialogueTimingMarker::SetPosition(int new_position) {
 /// addition, any markers for inactive lines that start/end at the same time
 /// as the active line starts/ends can optionally be dragged along with the
 /// active line's markers, updating those lines as well.
-class AudioTimingControllerDialogue : public AudioTimingController {
+class AudioTimingControllerDialogue final : public AudioTimingController {
 	/// The rendering style for the active line's start marker
 	Pen style_left;
 	/// The rendering style for the active line's end marker
diff --git a/src/audio_timing_karaoke.cpp b/src/audio_timing_karaoke.cpp
index 6be7398ad7f91b04c08936ab018275f3efe46047..b2dcd36b71162166d2ad7a88646c747091415515 100644
--- a/src/audio_timing_karaoke.cpp
+++ b/src/audio_timing_karaoke.cpp
@@ -43,7 +43,7 @@
 
 /// @class KaraokeMarker
 /// @brief AudioMarker implementation for AudioTimingControllerKaraoke
-class KaraokeMarker : public AudioMarker {
+class KaraokeMarker final : public AudioMarker {
 	int position;
 	Pen *pen;
 	FeetStyle style;
@@ -81,7 +81,7 @@ 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 : public AudioTimingController {
+class AudioTimingControllerKaraoke final : public AudioTimingController {
 	std::deque<agi::signal::Connection> slots;
 	agi::signal::Connection& file_changed_slot;
 
diff --git a/src/auto4_base.h b/src/auto4_base.h
index 6a11997efb8f090b5a5e596ba78d9860d2af0ff2..e7818d1d88ea5274390721bb5bf15878711daf0b 100644
--- a/src/auto4_base.h
+++ b/src/auto4_base.h
@@ -123,7 +123,7 @@ namespace Automation4 {
 
 	/// A wrapper around agi::ProgressSink which adds the ability to open
 	/// dialogs on the GUI thread
-	class ProgressSink : public agi::ProgressSink {
+	class ProgressSink final : public agi::ProgressSink {
 		agi::ProgressSink *impl;
 		BackgroundScriptRunner *bsr;
 		int trace_level;
@@ -218,7 +218,7 @@ namespace Automation4 {
 	};
 
 	/// Manager for scripts specified by a subtitle file
-	class LocalScriptManager : public ScriptManager {
+	class LocalScriptManager final : public ScriptManager {
 		std::deque<agi::signal::Connection> slots;
 		agi::Context *context;
 
@@ -229,7 +229,7 @@ namespace Automation4 {
 	};
 
 	/// Manager for scripts in the autoload directory
-	class AutoloadScriptManager : public ScriptManager {
+	class AutoloadScriptManager final : public ScriptManager {
 		std::string path;
 	public:
 		AutoloadScriptManager(std::string path);
@@ -281,7 +281,7 @@ namespace Automation4 {
 
 	/// A script which represents a file not recognized by any registered
 	/// automation engines
-	class UnknownScript : public Script {
+	class UnknownScript final : public Script {
 	public:
 		UnknownScript(agi::fs::path const& filename) : Script(filename) { }
 
diff --git a/src/auto4_lua.cpp b/src/auto4_lua.cpp
index df3968ae4dfa3ebbbe6e95f89043a7f979f6d289..987371495a11b620b70e2172d96726ddf5f7b788 100644
--- a/src/auto4_lua.cpp
+++ b/src/auto4_lua.cpp
@@ -300,7 +300,7 @@ int luaopen_lpeg (lua_State *L);
 namespace Automation4 {
 	int regex_init(lua_State *L);
 
-	class LuaScript : public Script {
+	class LuaScript final : public Script {
 		lua_State *L;
 
 		std::string name;
diff --git a/src/auto4_lua.h b/src/auto4_lua.h
index ec87b420c9276d620fffa9a9dfd48fd388d5ab41..6a5273d3b406c32c4e107ce7da17b925b1b68677 100644
--- a/src/auto4_lua.h
+++ b/src/auto4_lua.h
@@ -200,7 +200,7 @@ namespace Automation4 {
 	};
 
 	/// A lua-generated dialog or panel in the export options dialog
-	class LuaDialog : public ScriptDialog {
+	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
@@ -250,7 +250,7 @@ namespace Automation4 {
 	/// @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 : public cmd::Command, private LuaFeature {
+	class LuaCommand final : public cmd::Command, private LuaFeature {
 		std::string cmd_name;
 		wxString display;
 		wxString help;
@@ -274,7 +274,7 @@ namespace Automation4 {
 		static int LuaRegister(lua_State *L);
 	};
 
-	class LuaExportFilter : public ExportFilter, private LuaFeature {
+	class LuaExportFilter final : public ExportFilter, private LuaFeature {
 		bool has_config;
 		LuaDialog *config_dialog;
 
diff --git a/src/auto4_lua_dialog.cpp b/src/auto4_lua_dialog.cpp
index 2ae6d3fd2eca82a4e2cccb124754d0e74d6a1189..c35038540c541827b25d6f6a1d3e5f90e3d86345 100644
--- a/src/auto4_lua_dialog.cpp
+++ b/src/auto4_lua_dialog.cpp
@@ -141,7 +141,7 @@ namespace Automation4 {
 
 	namespace LuaControl {
 		/// A static text label
-		class Label : public LuaDialogControl {
+		class Label final : public LuaDialogControl {
 			std::string label;
 		public:
 			Label(lua_State *L) : LuaDialogControl(L), label(get_field(L, "label")) { }
@@ -193,7 +193,7 @@ namespace Automation4 {
 		};
 
 		/// A color-picker button
-		class Color : public LuaDialogControl {
+		class Color final : public LuaDialogControl {
 			agi::Color color;
 			bool alpha;
 
@@ -221,7 +221,7 @@ namespace Automation4 {
 		};
 
 		/// A multiline text edit control
-		class Textbox : public Edit {
+		class Textbox final : public Edit {
 		public:
 			Textbox(lua_State *L) : Edit(L) { }
 
@@ -236,7 +236,7 @@ namespace Automation4 {
 
 
 		/// Integer only edit
-		class IntEdit : public Edit {
+		class IntEdit final : public Edit {
 			wxSpinCtrl *cw;
 			int value;
 			int min, max;
@@ -272,14 +272,14 @@ namespace Automation4 {
 		};
 
 		// Float only edit
-		class FloatEdit : public Edit {
+		class FloatEdit final : public Edit {
 			double value;
 			double min;
 			double max;
 			double step;
 			wxSpinCtrlDouble *scd;
 
-			struct DoubleValidator : public wxValidator {
+			struct DoubleValidator final : public wxValidator {
 				double *value;
 				DoubleValidator(double *value) : value(value) { }
 				wxValidator *Clone() const override { return new DoubleValidator(value); }
@@ -341,7 +341,7 @@ namespace Automation4 {
 		};
 
 		/// A dropdown list
-		class Dropdown : public LuaDialogControl {
+		class Dropdown final : public LuaDialogControl {
 			std::vector<std::string> items;
 			std::string value;
 			wxComboBox *cw;
@@ -371,7 +371,7 @@ namespace Automation4 {
 			}
 		};
 
-		class Checkbox : public LuaDialogControl {
+		class Checkbox final : public LuaDialogControl {
 			std::string label;
 			bool value;
 			wxCheckBox *cw;
diff --git a/src/auto4_lua_factory.h b/src/auto4_lua_factory.h
index 60d7b948f46fd881f64e343f3d369ca23761fe98..f800d896e2c0f475b0b7d067093d55d070f39212 100644
--- a/src/auto4_lua_factory.h
+++ b/src/auto4_lua_factory.h
@@ -35,7 +35,7 @@
 #include "auto4_base.h"
 
 namespace Automation4 {
-	class LuaScriptFactory : public ScriptFactory {
+	class LuaScriptFactory final : public ScriptFactory {
 		std::unique_ptr<Script> Produce(agi::fs::path const& filename) const override;
 	public:
 		LuaScriptFactory();
diff --git a/src/base_grid.h b/src/base_grid.h
index 476690a702de6e376cb58672770b080d11335b4c..89ec1c12743d4495052624d5f7c8e5b678b21255 100644
--- a/src/base_grid.h
+++ b/src/base_grid.h
@@ -49,7 +49,7 @@ namespace agi {
 }
 class AssDialogue;
 
-class BaseGrid : public wxWindow, public SubtitleSelectionController {
+class BaseGrid final : public wxWindow, public SubtitleSelectionController {
 	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
diff --git a/src/colour_button.h b/src/colour_button.h
index ea8ce5c45acebf3d6aa6877d39d3f747b315f661..6d84c324b794f31e04d3391a8a2d59913cc7a2bc 100644
--- a/src/colour_button.h
+++ b/src/colour_button.h
@@ -43,7 +43,7 @@ public:
 	agi::Color GetColor() { return colour; }
 };
 
-struct ColorValidator : public wxValidator {
+struct ColorValidator final : public wxValidator {
 	agi::Color *color;
 	ColorValidator(agi::Color *color) : color(color) { }
 	wxValidator *Clone() const override { return new ColorValidator(color); }
diff --git a/src/command/app.cpp b/src/command/app.cpp
index 988d0efce1b8d905ddf4fe1e733ecdc8bc753b82..3242f66761ea1c3560da0eb4ce8134948bd51270 100644
--- a/src/command/app.cpp
+++ b/src/command/app.cpp
@@ -57,7 +57,7 @@
 namespace {
 	using cmd::Command;
 
-struct app_about : public Command {
+struct app_about final : public Command {
 	CMD_NAME("app/about")
 	CMD_ICON(about_menu)
 	STR_MENU("&About")
@@ -69,7 +69,7 @@ struct app_about : public Command {
 	}
 };
 
-struct app_display_audio_subs : public Command {
+struct app_display_audio_subs final : public Command {
 	CMD_NAME("app/display/audio_subs")
 	STR_MENU("&Audio+Subs View")
 	STR_DISP("Audio+Subs View")
@@ -89,7 +89,7 @@ struct app_display_audio_subs : public Command {
 	}
 };
 
-struct app_display_full : public Command {
+struct app_display_full final : public Command {
 	CMD_NAME("app/display/full")
 	STR_MENU("&Full view")
 	STR_DISP("Full view")
@@ -109,7 +109,7 @@ struct app_display_full : public Command {
 	}
 };
 
-struct app_display_subs : public Command {
+struct app_display_subs final : public Command {
 	CMD_NAME("app/display/subs")
 	STR_MENU("S&ubs Only View")
 	STR_DISP("Subs Only View")
@@ -125,7 +125,7 @@ struct app_display_subs : public Command {
 	}
 };
 
-struct app_display_video_subs : public Command {
+struct app_display_video_subs final : public Command {
 	CMD_NAME("app/display/video_subs")
 	STR_MENU("&Video+Subs View")
 	STR_DISP("Video+Subs View")
@@ -145,7 +145,7 @@ struct app_display_video_subs : public Command {
 	}
 };
 
-struct app_exit : public Command {
+struct app_exit final : public Command {
 	CMD_NAME("app/exit")
 	STR_MENU("E&xit")
 	STR_DISP("Exit")
@@ -156,7 +156,7 @@ struct app_exit : public Command {
 	}
 };
 
-struct app_language : public Command {
+struct app_language final : public Command {
 	CMD_NAME("app/language")
 	CMD_ICON(languages_menu)
 	STR_MENU("&Language...")
@@ -181,7 +181,7 @@ struct app_language : public Command {
 	}
 };
 
-struct app_log : public Command {
+struct app_log final : public Command {
 	CMD_NAME("app/log")
 	CMD_ICON(about_menu)
 	STR_MENU("&Log window")
@@ -193,7 +193,7 @@ struct app_log : public Command {
 	}
 };
 
-struct app_new_window : public Command {
+struct app_new_window final : public Command {
 	CMD_NAME("app/new_window")
 	CMD_ICON(new_window_menu)
 	STR_MENU("New &Window")
@@ -205,7 +205,7 @@ struct app_new_window : public Command {
 	}
 };
 
-struct app_options : public Command {
+struct app_options final : public Command {
 	CMD_NAME("app/options")
 	CMD_ICON(options_button)
 	STR_MENU("&Options...")
@@ -221,7 +221,7 @@ struct app_options : public Command {
 	}
 };
 
-struct app_toggle_global_hotkeys : public Command {
+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")
@@ -239,7 +239,7 @@ struct app_toggle_global_hotkeys : public Command {
 	}
 };
 
-struct app_toggle_toolbar : public Command {
+struct app_toggle_toolbar final : public Command {
 	CMD_NAME("app/toggle/toolbar")
 	STR_HELP("Toggle the main toolbar")
 	CMD_TYPE(COMMAND_DYNAMIC_NAME)
@@ -260,7 +260,7 @@ struct app_toggle_toolbar : public Command {
 	}
 };
 
-struct app_updates : public Command {
+struct app_updates final : public Command {
 	CMD_NAME("app/updates")
 	STR_MENU("&Check for Updates...")
 	STR_DISP("Check for Updates")
diff --git a/src/command/audio.cpp b/src/command/audio.cpp
index 7cf8f82fa8914c5ba48e736c790113ab0cde0cc4..32d5c80ffeb885f5e0f5a4a608ff2793c07b2a76 100644
--- a/src/command/audio.cpp
+++ b/src/command/audio.cpp
@@ -60,7 +60,7 @@ namespace {
 		}
 	};
 
-struct audio_close : public validate_audio_open {
+struct audio_close final : public validate_audio_open {
 	CMD_NAME("audio/close")
 	CMD_ICON(close_audio_menu)
 	STR_MENU("&Close Audio")
@@ -72,7 +72,7 @@ struct audio_close : public validate_audio_open {
 	}
 };
 
-struct audio_open : public Command {
+struct audio_open final : public Command {
 	CMD_NAME("audio/open")
 	CMD_ICON(open_audio_menu)
 	STR_MENU("&Open Audio File...")
@@ -96,7 +96,7 @@ struct audio_open : public Command {
 	}
 };
 
-struct audio_open_blank : public Command {
+struct audio_open_blank final : public Command {
 	CMD_NAME("audio/open/blank")
 	STR_MENU("Open 2h30 Blank Audio")
 	STR_DISP("Open 2h30 Blank Audio")
@@ -112,7 +112,7 @@ struct audio_open_blank : public Command {
 	}
 };
 
-struct audio_open_noise : public Command {
+struct audio_open_noise final : public Command {
 	CMD_NAME("audio/open/noise")
 	STR_MENU("Open 2h30 Noise Audio")
 	STR_DISP("Open 2h30 Noise Audio")
@@ -128,7 +128,7 @@ struct audio_open_noise : public Command {
 	}
 };
 
-struct audio_open_video : public Command {
+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")
@@ -151,7 +151,7 @@ struct audio_open_video : public Command {
 	}
 };
 
-struct audio_view_spectrum : public Command {
+struct audio_view_spectrum final : public Command {
 	CMD_NAME("audio/view/spectrum")
 	STR_MENU("&Spectrum Display")
 	STR_DISP("Spectrum Display")
@@ -167,7 +167,7 @@ struct audio_view_spectrum : public Command {
 	}
 };
 
-struct audio_view_waveform : public Command {
+struct audio_view_waveform final : public Command {
 	CMD_NAME("audio/view/waveform")
 	STR_MENU("&Waveform Display")
 	STR_DISP("Waveform Display")
@@ -183,7 +183,7 @@ struct audio_view_waveform : public Command {
 	}
 };
 
-struct audio_save_clip : public Command {
+struct audio_save_clip final : public Command {
 	CMD_NAME("audio/save/clip")
 	STR_MENU("Create audio clip")
 	STR_DISP("Create audio clip")
@@ -210,7 +210,7 @@ struct audio_save_clip : public Command {
 	}
 };
 
-struct audio_play_current_selection : public validate_audio_open {
+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")
@@ -222,7 +222,7 @@ struct audio_play_current_selection : public validate_audio_open {
 	}
 };
 
-struct audio_play_current_line : public validate_audio_open {
+struct audio_play_current_line final : public validate_audio_open {
 	CMD_NAME("audio/play/line")
 	CMD_ICON(button_playline)
 	STR_MENU("Play current line")
@@ -237,7 +237,7 @@ struct audio_play_current_line : public validate_audio_open {
 	}
 };
 
-struct audio_play_selection : public validate_audio_open {
+struct audio_play_selection final : public validate_audio_open {
 	CMD_NAME("audio/play/selection")
 	CMD_ICON(button_playsel)
 	STR_MENU("Play audio selection")
@@ -250,7 +250,7 @@ struct audio_play_selection : public validate_audio_open {
 	}
 };
 
-struct audio_play_toggle : public validate_audio_open {
+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")
@@ -266,7 +266,7 @@ struct audio_play_toggle : public validate_audio_open {
 	}
 };
 
-struct audio_stop : public Command {
+struct audio_stop final : public Command {
 	CMD_NAME("audio/stop")
 	CMD_ICON(button_stop)
 	STR_MENU("Stop playing")
@@ -284,7 +284,7 @@ struct audio_stop : public Command {
 	}
 };
 
-struct audio_play_before : public validate_audio_open {
+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")
@@ -298,7 +298,7 @@ struct audio_play_before : public validate_audio_open {
 	}
 };
 
-struct audio_play_after : public validate_audio_open {
+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")
@@ -312,7 +312,7 @@ struct audio_play_after : public validate_audio_open {
 	}
 };
 
-struct audio_play_end : public validate_audio_open {
+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")
@@ -326,7 +326,7 @@ struct audio_play_end : public validate_audio_open {
 	}
 };
 
-struct audio_play_begin : public validate_audio_open {
+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")
@@ -342,7 +342,7 @@ struct audio_play_begin : public validate_audio_open {
 	}
 };
 
-struct audio_play_to_end : public validate_audio_open {
+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")
@@ -355,7 +355,7 @@ struct audio_play_to_end : public validate_audio_open {
 	}
 };
 
-struct audio_commit : public validate_audio_open {
+struct audio_commit final : public validate_audio_open {
 	CMD_NAME("audio/commit")
 	CMD_ICON(button_audio_commit)
 	STR_MENU("Commit")
@@ -372,7 +372,7 @@ struct audio_commit : public validate_audio_open {
 	}
 };
 
-struct audio_commit_default : public validate_audio_open {
+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")
@@ -387,7 +387,7 @@ struct audio_commit_default : public validate_audio_open {
 	}
 };
 
-struct audio_commit_next : public validate_audio_open {
+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")
@@ -402,7 +402,7 @@ struct audio_commit_next : public validate_audio_open {
 	}
 };
 
-struct audio_commit_stay : public validate_audio_open {
+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")
@@ -414,7 +414,7 @@ struct audio_commit_stay : public validate_audio_open {
 	}
 };
 
-struct audio_go_to : public validate_audio_open {
+struct audio_go_to final : public validate_audio_open {
 	CMD_NAME("audio/go_to")
 	CMD_ICON(button_audio_goto)
 	STR_MENU("Go to selection")
@@ -426,7 +426,7 @@ struct audio_go_to : public validate_audio_open {
 	}
 };
 
-struct audio_scroll_left : public validate_audio_open {
+struct audio_scroll_left final : public validate_audio_open {
 	CMD_NAME("audio/scroll/left")
 		STR_MENU("Scroll left")
 		STR_DISP("Scroll left")
@@ -437,7 +437,7 @@ struct audio_scroll_left : public validate_audio_open {
 	}
 };
 
-struct audio_scroll_right : public validate_audio_open {
+struct audio_scroll_right final : public validate_audio_open {
 	CMD_NAME("audio/scroll/right")
 		STR_MENU("Scroll right")
 		STR_DISP("Scroll right")
@@ -452,7 +452,7 @@ static inline void toggle(const char *opt) {
 	OPT_SET(opt)->SetBool(!OPT_GET(opt)->GetBool());
 }
 
-struct audio_autoscroll : public Command {
+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")
@@ -469,7 +469,7 @@ struct audio_autoscroll : public Command {
 	}
 };
 
-struct audio_autocommit : public Command {
+struct audio_autocommit final : public Command {
 	CMD_NAME("audio/opt/autocommit")
 	CMD_ICON(toggle_audio_autocommit)
 	STR_MENU("Automatically commit all changes")
@@ -486,7 +486,7 @@ struct audio_autocommit : public Command {
 	}
 };
 
-struct audio_autonext : public Command {
+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")
@@ -503,7 +503,7 @@ struct audio_autonext : public Command {
 	}
 };
 
-struct audio_toggle_spectrum : public Command {
+struct audio_toggle_spectrum final : public Command {
 	CMD_NAME("audio/opt/spectrum")
 	CMD_ICON(toggle_audio_spectrum)
 	STR_MENU("Spectrum analyzer mode")
@@ -520,7 +520,7 @@ struct audio_toggle_spectrum : public Command {
 	}
 };
 
-struct audio_vertical_link : public Command {
+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")
@@ -537,7 +537,7 @@ struct audio_vertical_link : public Command {
 	}
 };
 
-struct audio_karaoke : public Command {
+struct audio_karaoke final : public Command {
 	CMD_NAME("audio/karaoke")
 	CMD_ICON(kara_mode)
 	STR_MENU("Toggle karaoke mode")
diff --git a/src/command/automation.cpp b/src/command/automation.cpp
index 8ad1ee2f395956e48b7ed884b36396a7853c186f..abdedbf1175b3a01a03ef32cd3cecc8b88749906 100644
--- a/src/command/automation.cpp
+++ b/src/command/automation.cpp
@@ -49,7 +49,7 @@
 namespace {
 	using cmd::Command;
 
-struct reload_all : public Command {
+struct reload_all final : public Command {
 	CMD_NAME("am/reload")
 	STR_MENU("&Reload Automation scripts")
 	STR_DISP("Reload Automation scripts")
@@ -62,7 +62,7 @@ struct reload_all : public Command {
 	}
 };
 
-struct reload_autoload : public Command {
+struct reload_autoload final : public Command {
 	CMD_NAME("am/reload/autoload")
 	STR_MENU("R&eload autoload Automation scripts")
 	STR_DISP("Reload autoload Automation scripts")
@@ -74,7 +74,7 @@ struct reload_autoload : public Command {
 	}
 };
 
-struct open_manager : public Command {
+struct open_manager final : public Command {
 	CMD_NAME("am/manager")
 	CMD_ICON(automation_toolbutton)
 	STR_MENU("&Automation...")
@@ -86,7 +86,7 @@ struct open_manager : public Command {
 	}
 };
 
-struct meta : public Command {
+struct meta final : public Command {
 	CMD_NAME("am/meta")
 	CMD_ICON(automation_toolbutton)
 	STR_MENU("&Automation...")
diff --git a/src/command/command.h b/src/command/command.h
index 9da61b492d61d217505f4f7389f58eae0deacf36..b43ba7bec6ec94b4ffcb55a079ddf591f9c4ed6b 100644
--- a/src/command/command.h
+++ b/src/command/command.h
@@ -43,7 +43,7 @@ namespace agi { struct Context; }
 }
 
 #define COMMAND_GROUP(cname, cmdname, menu, disp, help) \
-struct cname : public Command {                         \
+struct cname final : public Command {                         \
 	CMD_NAME(cmdname)                                   \
 	STR_MENU(menu)                                      \
 	STR_DISP(disp)                                      \
diff --git a/src/command/edit.cpp b/src/command/edit.cpp
index f0973d9d55c9cf8e7846e6437be4e28f1feb4d5e..228c752ac0639cd9ffdbe936ae03615266b9419f 100644
--- a/src/command/edit.cpp
+++ b/src/command/edit.cpp
@@ -331,7 +331,7 @@ void show_color_picker(const agi::Context *c, agi::Color (AssStyle::*field), con
 	}
 }
 
-struct edit_color_primary : public Command {
+struct edit_color_primary final : public Command {
 	CMD_NAME("edit/color/primary")
 	CMD_ICON(button_color_one)
 	STR_MENU("Primary Color...")
@@ -343,7 +343,7 @@ struct edit_color_primary : public Command {
 	}
 };
 
-struct edit_color_secondary : public Command {
+struct edit_color_secondary final : public Command {
 	CMD_NAME("edit/color/secondary")
 	CMD_ICON(button_color_two)
 	STR_MENU("Secondary Color...")
@@ -355,7 +355,7 @@ struct edit_color_secondary : public Command {
 	}
 };
 
-struct edit_color_outline : public Command {
+struct edit_color_outline final : public Command {
 	CMD_NAME("edit/color/outline")
 	CMD_ICON(button_color_three)
 	STR_MENU("Outline Color...")
@@ -367,7 +367,7 @@ struct edit_color_outline : public Command {
 	}
 };
 
-struct edit_color_shadow : public Command {
+struct edit_color_shadow final : public Command {
 	CMD_NAME("edit/color/shadow")
 	CMD_ICON(button_color_four)
 	STR_MENU("Shadow Color...")
@@ -379,7 +379,7 @@ struct edit_color_shadow : public Command {
 	}
 };
 
-struct edit_style_bold : public Command {
+struct edit_style_bold final : public Command {
 	CMD_NAME("edit/style/bold")
 	CMD_ICON(button_bold)
 	STR_MENU("Toggle Bold")
@@ -391,7 +391,7 @@ struct edit_style_bold : public Command {
 	}
 };
 
-struct edit_style_italic : public Command {
+struct edit_style_italic final : public Command {
 	CMD_NAME("edit/style/italic")
 	CMD_ICON(button_italics)
 	STR_MENU("Toggle Italics")
@@ -403,7 +403,7 @@ struct edit_style_italic : public Command {
 	}
 };
 
-struct edit_style_underline : public Command {
+struct edit_style_underline final : public Command {
 	CMD_NAME("edit/style/underline")
 	CMD_ICON(button_underline)
 	STR_MENU("Toggle Underline")
@@ -415,7 +415,7 @@ struct edit_style_underline : public Command {
 	}
 };
 
-struct edit_style_strikeout : public Command {
+struct edit_style_strikeout final : public Command {
 	CMD_NAME("edit/style/strikeout")
 	CMD_ICON(button_strikeout)
 	STR_MENU("Toggle Strikeout")
@@ -427,7 +427,7 @@ struct edit_style_strikeout : public Command {
 	}
 };
 
-struct edit_font : public Command {
+struct edit_font final : public Command {
 	CMD_NAME("edit/font")
 	CMD_ICON(button_fontname)
 	STR_MENU("Font Face...")
@@ -473,7 +473,7 @@ struct edit_font : public Command {
 	}
 };
 
-struct edit_find_replace : public Command {
+struct edit_find_replace final : public Command {
 	CMD_NAME("edit/find_replace")
 	CMD_ICON(find_replace_menu)
 	STR_MENU("Find and R&eplace...")
@@ -539,7 +539,7 @@ static void delete_lines(agi::Context *c, wxString const& commit_message) {
 	c->selectionController->SetSelectionAndActive({ new_active }, new_active);
 }
 
-struct edit_line_copy : public validate_sel_nonempty {
+struct edit_line_copy final : public validate_sel_nonempty {
 	CMD_NAME("edit/line/copy")
 	CMD_ICON(copy_button)
 	STR_MENU("&Copy Lines")
@@ -579,7 +579,7 @@ struct edit_line_cut: public validate_sel_nonempty {
 	}
 };
 
-struct edit_line_delete : public validate_sel_nonempty {
+struct edit_line_delete final : public validate_sel_nonempty {
 	CMD_NAME("edit/line/delete")
 	CMD_ICON(delete_button)
 	STR_MENU("De&lete Lines")
@@ -657,7 +657,7 @@ static void duplicate_lines(agi::Context *c, int shift) {
 	c->selectionController->SetSelectionAndActive(std::move(new_sel), new_active);
 }
 
-struct edit_line_duplicate : public validate_sel_nonempty {
+struct edit_line_duplicate final : public validate_sel_nonempty {
 	CMD_NAME("edit/line/duplicate")
 	STR_MENU("&Duplicate Lines")
 	STR_DISP("Duplicate Lines")
@@ -668,7 +668,7 @@ struct edit_line_duplicate : public validate_sel_nonempty {
 	}
 };
 
-struct edit_line_duplicate_shift : public validate_video_and_sel_nonempty {
+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")
@@ -680,7 +680,7 @@ struct edit_line_duplicate_shift : public validate_video_and_sel_nonempty {
 	}
 };
 
-struct edit_line_duplicate_shift_back : public validate_video_and_sel_nonempty {
+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")
@@ -724,7 +724,7 @@ static void combine_concat(AssDialogue *first, AssDialogue *second) {
 
 static void combine_drop(AssDialogue *, AssDialogue *) { }
 
-struct edit_line_join_as_karaoke : public validate_sel_multiple {
+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")
@@ -735,7 +735,7 @@ struct edit_line_join_as_karaoke : public validate_sel_multiple {
 	}
 };
 
-struct edit_line_join_concatenate : public validate_sel_multiple {
+struct edit_line_join_concatenate final : public validate_sel_multiple {
 	CMD_NAME("edit/line/join/concatenate")
 	STR_MENU("&Concatenate")
 	STR_DISP("Concatenate")
@@ -746,7 +746,7 @@ struct edit_line_join_concatenate : public validate_sel_multiple {
 	}
 };
 
-struct edit_line_join_keep_first : public validate_sel_multiple {
+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")
@@ -788,7 +788,7 @@ static bool try_paste_lines(agi::Context *c) {
 	return true;
 }
 
-struct edit_line_paste : public Command {
+struct edit_line_paste final : public Command {
 	CMD_NAME("edit/line/paste")
 	CMD_ICON(paste_button)
 	STR_MENU("&Paste Lines")
@@ -820,7 +820,7 @@ struct edit_line_paste : public Command {
 	}
 };
 
-struct edit_line_paste_over : public Command {
+struct edit_line_paste_over final : public Command {
 	CMD_NAME("edit/line/paste/over")
 	STR_MENU("Paste Lines &Over...")
 	STR_DISP("Paste Lines Over")
@@ -913,7 +913,7 @@ bool check_end(AssDialogue *d1, AssDialogue *d2) {
 
 }
 
-struct edit_line_recombine : public validate_sel_multiple {
+struct edit_line_recombine final : public validate_sel_multiple {
 	CMD_NAME("edit/line/recombine")
 	STR_MENU("Recom&bine Lines")
 	STR_DISP("Recombine Lines")
@@ -991,7 +991,7 @@ struct edit_line_recombine : public validate_sel_multiple {
 	}
 };
 
-struct edit_line_split_by_karaoke : public validate_sel_nonempty {
+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)")
@@ -1019,7 +1019,7 @@ void split_lines(agi::Context *c, Func&& set_time) {
 	c->ass->Commit(_("split"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL);
 }
 
-struct edit_line_split_estimate : public validate_video_and_sel_nonempty {
+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)")
@@ -1035,7 +1035,7 @@ struct edit_line_split_estimate : public validate_video_and_sel_nonempty {
 	}
 };
 
-struct edit_line_split_preserve : public validate_sel_nonempty {
+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)")
@@ -1046,7 +1046,7 @@ struct edit_line_split_preserve : public validate_sel_nonempty {
 	}
 };
 
-struct edit_line_split_video : public validate_video_and_sel_nonempty {
+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)")
@@ -1063,7 +1063,7 @@ struct edit_line_split_video : public validate_video_and_sel_nonempty {
 	}
 };
 
-struct edit_redo : public Command {
+struct edit_redo final : public Command {
 	CMD_NAME("edit/redo")
 	CMD_ICON(redo_button)
 	STR_HELP("Redo last undone action")
@@ -1089,7 +1089,7 @@ struct edit_redo : public Command {
 	}
 };
 
-struct edit_undo : public Command {
+struct edit_undo final : public Command {
 	CMD_NAME("edit/undo")
 	CMD_ICON(undo_button)
 	STR_HELP("Undo last action")
@@ -1115,7 +1115,7 @@ struct edit_undo : public Command {
 	}
 };
 
-struct edit_revert : public Command {
+struct edit_revert final : public Command {
 	CMD_NAME("edit/revert")
 	STR_DISP("Revert")
 	STR_MENU("Revert")
@@ -1128,7 +1128,7 @@ struct edit_revert : public Command {
 	}
 };
 
-struct edit_clear : public Command {
+struct edit_clear final : public Command {
 	CMD_NAME("edit/clear")
 	STR_DISP("Clear")
 	STR_MENU("Clear")
@@ -1142,7 +1142,7 @@ struct edit_clear : public Command {
 };
 
 std::string get_text(AssDialogueBlock &d) { return d.GetText(); }
-struct edit_clear_text : public Command {
+struct edit_clear_text final : public Command {
 	CMD_NAME("edit/clear/text")
 	STR_DISP("Clear Text")
 	STR_MENU("Clear Text")
@@ -1159,7 +1159,7 @@ struct edit_clear_text : public Command {
 	}
 };
 
-struct edit_insert_original : public Command {
+struct edit_insert_original final : public Command {
 	CMD_NAME("edit/insert_original")
 	STR_DISP("Insert Original")
 	STR_MENU("Insert Original")
diff --git a/src/command/grid.cpp b/src/command/grid.cpp
index 8bd96ead2ccd74097cff373c606f68d0f0f36b7f..278635ebc9300479ba1a540e28ea7900230487f2 100644
--- a/src/command/grid.cpp
+++ b/src/command/grid.cpp
@@ -48,7 +48,7 @@
 namespace {
 	using cmd::Command;
 
-struct grid_line_next : public Command {
+struct grid_line_next final : public Command {
 	CMD_NAME("grid/line/next")
 	STR_MENU("Next Line")
 	STR_DISP("Next Line")
@@ -59,7 +59,7 @@ struct grid_line_next : public Command {
 	}
 };
 
-struct grid_line_next_create : public Command {
+struct grid_line_next_create final : public Command {
 	CMD_NAME("grid/line/next/create")
 	CMD_ICON(button_audio_commit)
 	STR_MENU("Next Line")
@@ -87,7 +87,7 @@ struct grid_line_next_create : public Command {
 	}
 };
 
-struct grid_line_prev : public Command {
+struct grid_line_prev final : public Command {
 	CMD_NAME("grid/line/prev")
 	STR_MENU("Previous Line")
 	STR_DISP("Previous Line")
@@ -98,7 +98,7 @@ struct grid_line_prev : public Command {
 	}
 };
 
-struct grid_sort_actor : public Command {
+struct grid_sort_actor final : public Command {
 	CMD_NAME("grid/sort/actor")
 	STR_MENU("&Actor Name")
 	STR_DISP("Actor Name")
@@ -118,7 +118,7 @@ struct validate_sel_multiple : public Command {
 	}
 };
 
-struct grid_sort_actor_selected : public validate_sel_multiple {
+struct grid_sort_actor_selected final : public validate_sel_multiple {
 	CMD_NAME("grid/sort/actor/selected")
 	STR_MENU("&Actor Name")
 	STR_DISP("Actor Name")
@@ -130,7 +130,7 @@ struct grid_sort_actor_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_sort_effect : public Command {
+struct grid_sort_effect final : public Command {
 	CMD_NAME("grid/sort/effect")
 	STR_MENU("&Effect")
 	STR_DISP("Effect")
@@ -142,7 +142,7 @@ struct grid_sort_effect : public Command {
 	}
 };
 
-struct grid_sort_effect_selected : public validate_sel_multiple {
+struct grid_sort_effect_selected final : public validate_sel_multiple {
 	CMD_NAME("grid/sort/effect/selected")
 	STR_MENU("&Effect")
 	STR_DISP("Effect")
@@ -154,7 +154,7 @@ struct grid_sort_effect_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_sort_end : public Command {
+struct grid_sort_end final : public Command {
 	CMD_NAME("grid/sort/end")
 	STR_MENU("&End Time")
 	STR_DISP("End Time")
@@ -166,7 +166,7 @@ struct grid_sort_end : public Command {
 	}
 };
 
-struct grid_sort_end_selected : public validate_sel_multiple {
+struct grid_sort_end_selected final : public validate_sel_multiple {
 	CMD_NAME("grid/sort/end/selected")
 	STR_MENU("&End Time")
 	STR_DISP("End Time")
@@ -178,7 +178,7 @@ struct grid_sort_end_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_sort_layer : public Command {
+struct grid_sort_layer final : public Command {
 	CMD_NAME("grid/sort/layer")
 	STR_MENU("&Layer")
 	STR_DISP("Layer")
@@ -190,7 +190,7 @@ struct grid_sort_layer : public Command {
 	}
 };
 
-struct grid_sort_layer_selected : public validate_sel_multiple {
+struct grid_sort_layer_selected final : public validate_sel_multiple {
 	CMD_NAME("grid/sort/layer/selected")
 	STR_MENU("&Layer")
 	STR_DISP("Layer")
@@ -202,7 +202,7 @@ struct grid_sort_layer_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_sort_start : public Command {
+struct grid_sort_start final : public Command {
 	CMD_NAME("grid/sort/start")
 	STR_MENU("&Start Time")
 	STR_DISP("Start Time")
@@ -214,7 +214,7 @@ struct grid_sort_start : public Command {
 	}
 };
 
-struct grid_sort_start_selected : public validate_sel_multiple {
+struct grid_sort_start_selected final : public validate_sel_multiple {
 	CMD_NAME("grid/sort/start/selected")
 	STR_MENU("&Start Time")
 	STR_DISP("Start Time")
@@ -226,7 +226,7 @@ struct grid_sort_start_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_sort_style : public Command {
+struct grid_sort_style final : public Command {
 	CMD_NAME("grid/sort/style")
 	STR_MENU("St&yle Name")
 	STR_DISP("Style Name")
@@ -238,7 +238,7 @@ struct grid_sort_style : public Command {
 	}
 };
 
-struct grid_sort_style_selected : public validate_sel_multiple {
+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")
@@ -250,7 +250,7 @@ struct grid_sort_style_selected : public validate_sel_multiple {
 	}
 };
 
-struct grid_tag_cycle_hiding : public Command {
+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")
@@ -275,7 +275,7 @@ struct grid_tag_cycle_hiding : public Command {
 	}
 };
 
-struct grid_tags_hide : public Command {
+struct grid_tags_hide final : public Command {
 	CMD_NAME("grid/tags/hide")
 	STR_MENU("&Hide Tags")
 	STR_DISP("Hide Tags")
@@ -291,7 +291,7 @@ struct grid_tags_hide : public Command {
 	}
 };
 
-struct grid_tags_show : public Command {
+struct grid_tags_show final : public Command {
 	CMD_NAME("grid/tags/show")
 	STR_MENU("Sh&ow Tags")
 	STR_DISP("Show Tags")
@@ -307,7 +307,7 @@ struct grid_tags_show : public Command {
 	}
 };
 
-struct grid_tags_simplify : public Command {
+struct grid_tags_simplify final : public Command {
 	CMD_NAME("grid/tags/simplify")
 	STR_MENU("S&implify Tags")
 	STR_DISP("Simplify Tags")
@@ -342,7 +342,7 @@ static bool move_one(T begin, T end, U const& to_move, int step) {
 	return move_count > 0;
 }
 
-struct grid_move_up : public Command {
+struct grid_move_up final : public Command {
 	CMD_NAME("grid/move/up")
 	STR_MENU("Move line up")
 	STR_DISP("Move line up")
@@ -359,7 +359,7 @@ struct grid_move_up : public Command {
 	}
 };
 
-struct grid_move_down : public Command {
+struct grid_move_down final : public Command {
 	CMD_NAME("grid/move/down")
 	STR_MENU("Move line down")
 	STR_DISP("Move line down")
@@ -376,7 +376,7 @@ struct grid_move_down : public Command {
 	}
 };
 
-struct grid_swap : public Command {
+struct grid_swap final : public Command {
 	CMD_NAME("grid/swap")
 	CMD_ICON(arrow_sort)
 	STR_MENU("Swap Lines")
diff --git a/src/command/help.cpp b/src/command/help.cpp
index 9d5fc33f5e0cd3d6a2133bd69277d18d5f983f42..1ac636d9b9d320520838c3f87d84aa0457233e8d 100644
--- a/src/command/help.cpp
+++ b/src/command/help.cpp
@@ -45,7 +45,7 @@
 namespace {
 	using cmd::Command;
 
-struct help_bugs : public Command {
+struct help_bugs final : public Command {
 	CMD_NAME("help/bugs")
 	CMD_ICON(bugtracker_button)
 	STR_MENU("&Bug Tracker...")
@@ -66,7 +66,7 @@ struct help_bugs : public Command {
 	}
 };
 
-struct help_contents : public Command {
+struct help_contents final : public Command {
 	CMD_NAME("help/contents")
 	CMD_ICON(contents_button)
 	STR_MENU("&Contents")
@@ -78,7 +78,7 @@ struct help_contents : public Command {
 	}
 };
 
-struct help_forums : public Command {
+struct help_forums final : public Command {
 	CMD_NAME("help/forums")
 	CMD_ICON(forums_button)
 	STR_MENU("&Forums")
@@ -90,7 +90,7 @@ struct help_forums : public Command {
 	}
 };
 
-struct help_irc : public Command {
+struct help_irc final : public Command {
 	CMD_NAME("help/irc")
 	CMD_ICON(irc_button)
 	STR_MENU("&IRC Channel")
@@ -102,7 +102,7 @@ struct help_irc : public Command {
 	}
 };
 
-struct help_video : public Command {
+struct help_video final : public Command {
 	CMD_NAME("help/video")
 	CMD_ICON(visual_help)
 	STR_MENU("&Visual Typesetting")
@@ -114,7 +114,7 @@ struct help_video : public Command {
 	}
 };
 
-struct help_website : public Command {
+struct help_website final : public Command {
 	CMD_NAME("help/website")
 	CMD_ICON(website_button)
 	STR_MENU("&Website")
diff --git a/src/command/keyframe.cpp b/src/command/keyframe.cpp
index 219ee968b9e54f4de6290fcef92d61462dca8283..73d5c4db298688c8790752889a2d5c8d96853616 100644
--- a/src/command/keyframe.cpp
+++ b/src/command/keyframe.cpp
@@ -44,7 +44,7 @@
 namespace {
 	using cmd::Command;
 
-struct keyframe_close : public Command {
+struct keyframe_close final : public Command {
 	CMD_NAME("keyframe/close")
 	CMD_ICON(close_keyframes_menu)
 	STR_MENU("Close Keyframes")
@@ -61,7 +61,7 @@ struct keyframe_close : public Command {
 	}
 };
 
-struct keyframe_open : public Command {
+struct keyframe_open final : public Command {
 	CMD_NAME("keyframe/open")
 	CMD_ICON(open_keyframes_menu)
 	STR_MENU("Open Keyframes...")
@@ -80,7 +80,7 @@ struct keyframe_open : public Command {
 	}
 };
 
-struct keyframe_save : public Command {
+struct keyframe_save final : public Command {
 	CMD_NAME("keyframe/save")
 	CMD_ICON(save_keyframes_menu)
 	STR_MENU("Save Keyframes...")
diff --git a/src/command/recent.cpp b/src/command/recent.cpp
index 98616a7105d68b0e14e89a7dafb500c2e60498c8..fccb28004c3b611a9cbf302ee4ffdd54979b7190 100644
--- a/src/command/recent.cpp
+++ b/src/command/recent.cpp
@@ -117,7 +117,7 @@ struct recent_video_entry : public Command {
 };
 
 template<class T>
-class mru_wrapper : public T {
+class mru_wrapper final : public T {
 	int id;
 	std::string full_name;
 public:
diff --git a/src/command/subtitle.cpp b/src/command/subtitle.cpp
index 97213c6629e8cd29a5bf215de39e6c1d216a35e3..6d8cd32b2b64061dd54dc4abbf7d650f89198220 100644
--- a/src/command/subtitle.cpp
+++ b/src/command/subtitle.cpp
@@ -78,7 +78,7 @@ struct validate_nonempty_selection_video_loaded : public Command {
 	}
 };
 
-struct subtitle_attachment : public Command {
+struct subtitle_attachment final : public Command {
 	CMD_NAME("subtitle/attachment")
 	CMD_ICON(attach_button)
 	STR_MENU("A&ttachments...")
@@ -91,7 +91,7 @@ struct subtitle_attachment : public Command {
 	}
 };
 
-struct subtitle_find : public Command {
+struct subtitle_find final : public Command {
 	CMD_NAME("subtitle/find")
 	CMD_ICON(find_button)
 	STR_MENU("&Find...")
@@ -104,7 +104,7 @@ struct subtitle_find : public Command {
 	}
 };
 
-struct subtitle_find_next : public Command {
+struct subtitle_find_next final : public Command {
 	CMD_NAME("subtitle/find/next")
 	CMD_ICON(find_next_menu)
 	STR_MENU("Find &Next")
@@ -134,7 +134,7 @@ static void insert_subtitle_at_video(agi::Context *c, bool after) {
 	c->selectionController->SetSelectionAndActive({ def }, def);
 }
 
-struct subtitle_insert_after : public validate_nonempty_selection {
+struct subtitle_insert_after final : public validate_nonempty_selection {
 	CMD_NAME("subtitle/insert/after")
 	STR_MENU("&After Current")
 	STR_DISP("After Current")
@@ -168,7 +168,7 @@ struct subtitle_insert_after : public validate_nonempty_selection {
 	}
 };
 
-struct subtitle_insert_after_videotime : public validate_nonempty_selection_video_loaded {
+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")
@@ -179,7 +179,7 @@ struct subtitle_insert_after_videotime : public validate_nonempty_selection_vide
 	}
 };
 
-struct subtitle_insert_before : public validate_nonempty_selection {
+struct subtitle_insert_before final : public validate_nonempty_selection {
 	CMD_NAME("subtitle/insert/before")
 	STR_MENU("&Before Current")
 	STR_DISP("Before Current")
@@ -210,7 +210,7 @@ struct subtitle_insert_before : public validate_nonempty_selection {
 	}
 };
 
-struct subtitle_insert_before_videotime : public validate_nonempty_selection_video_loaded {
+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")
@@ -221,7 +221,7 @@ struct subtitle_insert_before_videotime : public validate_nonempty_selection_vid
 	}
 };
 
-struct subtitle_new : public Command {
+struct subtitle_new final : public Command {
 	CMD_NAME("subtitle/new")
 	CMD_ICON(new_toolbutton)
 	STR_MENU("&New Subtitles")
@@ -234,7 +234,7 @@ struct subtitle_new : public Command {
 	}
 };
 
-struct subtitle_open : public Command {
+struct subtitle_open final : public Command {
 	CMD_NAME("subtitle/open")
 	CMD_ICON(open_toolbutton)
 	STR_MENU("&Open Subtitles...")
@@ -249,7 +249,7 @@ struct subtitle_open : public Command {
 	}
 };
 
-struct subtitle_open_autosave : public Command {
+struct subtitle_open_autosave final : public Command {
 	CMD_NAME("subtitle/open/autosave")
 	STR_MENU("Open A&utosaved Subtitles...")
 	STR_DISP("Open Autosaved Subtitles")
@@ -263,7 +263,7 @@ struct subtitle_open_autosave : public Command {
 	}
 };
 
-struct subtitle_open_charset : public Command {
+struct subtitle_open_charset final : public Command {
 	CMD_NAME("subtitle/open/charset")
 	CMD_ICON(open_with_toolbutton)
 	STR_MENU("Open Subtitles with &Charset...")
@@ -283,7 +283,7 @@ struct subtitle_open_charset : public Command {
 	}
 };
 
-struct subtitle_open_video : public Command {
+struct subtitle_open_video final : public Command {
 	CMD_NAME("subtitle/open/video")
 	STR_MENU("Open Subtitles from &Video")
 	STR_DISP("Open Subtitles from Video")
@@ -300,7 +300,7 @@ struct subtitle_open_video : public Command {
 	}
 };
 
-struct subtitle_properties : public Command {
+struct subtitle_properties final : public Command {
 	CMD_NAME("subtitle/properties")
 	CMD_ICON(properties_toolbutton)
 	STR_MENU("&Properties...")
@@ -336,7 +336,7 @@ static void save_subtitles(agi::Context *c, agi::fs::path filename) {
 	}
 }
 
-struct subtitle_save : public Command {
+struct subtitle_save final : public Command {
 	CMD_NAME("subtitle/save")
 	CMD_ICON(save_toolbutton)
 	STR_MENU("&Save Subtitles")
@@ -353,7 +353,7 @@ struct subtitle_save : public Command {
 	}
 };
 
-struct subtitle_save_as : public Command {
+struct subtitle_save_as final : public Command {
 	CMD_NAME("subtitle/save/as")
 	CMD_ICON(save_as_toolbutton)
 	STR_MENU("Save Subtitles &as...")
@@ -365,7 +365,7 @@ struct subtitle_save_as : public Command {
 	}
 };
 
-struct subtitle_select_all : public Command {
+struct subtitle_select_all final : public Command {
 	CMD_NAME("subtitle/select/all")
 	STR_MENU("Select &All")
 	STR_DISP("Select All")
@@ -378,7 +378,7 @@ struct subtitle_select_all : public Command {
 	}
 };
 
-struct subtitle_select_visible : public Command {
+struct subtitle_select_visible final : public Command {
 	CMD_NAME("subtitle/select/visible")
 	CMD_ICON(select_visible_button)
 	STR_MENU("Select Visible")
@@ -411,7 +411,7 @@ struct subtitle_select_visible : public Command {
 	}
 };
 
-struct subtitle_spellcheck : public Command {
+struct subtitle_spellcheck final : public Command {
 	CMD_NAME("subtitle/spellcheck")
 	CMD_ICON(spellcheck_toolbutton)
 	STR_MENU("Spell &Checker...")
diff --git a/src/command/time.cpp b/src/command/time.cpp
index b3abb49567c2cd55dab2f64cb23b787d32e3f342..fc8a3f0fd702f4c548afe52f39f92a1b986c972b 100644
--- a/src/command/time.cpp
+++ b/src/command/time.cpp
@@ -106,7 +106,7 @@ static void adjoin_lines(agi::Context *c, bool set_start) {
 	c->ass->Commit(_("adjoin"), AssFile::COMMIT_DIAG_TIME);
 }
 
-struct time_continuous_end : public validate_adjoinable {
+struct time_continuous_end final : public validate_adjoinable {
 	CMD_NAME("time/continuous/end")
 	STR_MENU("Change &End")
 	STR_DISP("Change End")
@@ -117,7 +117,7 @@ struct time_continuous_end : public validate_adjoinable {
 	}
 };
 
-struct time_continuous_start : public validate_adjoinable {
+struct time_continuous_start final : public validate_adjoinable {
 	CMD_NAME("time/continuous/start")
 	STR_MENU("Change &Start")
 	STR_DISP("Change Start")
@@ -128,7 +128,7 @@ struct time_continuous_start : public validate_adjoinable {
 	}
 };
 
-struct time_frame_current : public validate_video_loaded {
+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")
@@ -155,7 +155,7 @@ struct time_frame_current : public validate_video_loaded {
 	}
 };
 
-struct time_shift : public Command {
+struct time_shift final : public Command {
 	CMD_NAME("time/shift")
 	CMD_ICON(shift_times_toolbutton)
 	STR_MENU("S&hift Times...")
@@ -185,7 +185,7 @@ static void snap_subs_video(agi::Context *c, bool set_start) {
 	c->ass->Commit(_("timing"), AssFile::COMMIT_DIAG_TIME);
 }
 
-struct time_snap_end_video : public validate_video_loaded {
+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")
@@ -197,7 +197,7 @@ struct time_snap_end_video : public validate_video_loaded {
 	}
 };
 
-struct time_snap_scene : public validate_video_loaded {
+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")
@@ -243,7 +243,7 @@ struct time_snap_scene : public validate_video_loaded {
 	}
 };
 
-struct time_add_lead_both : public Command {
+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")
@@ -256,7 +256,7 @@ struct time_add_lead_both : public Command {
 	}
 };
 
-struct time_add_lead_in : public Command {
+struct time_add_lead_in final : public Command {
 	CMD_NAME("time/lead/in")
 	CMD_ICON(button_leadin)
 	STR_MENU("Add lead in")
@@ -268,7 +268,7 @@ struct time_add_lead_in : public Command {
 	}
 };
 
-struct time_add_lead_out : public Command {
+struct time_add_lead_out final : public Command {
 	CMD_NAME("time/lead/out")
 	CMD_ICON(button_leadout)
 	STR_MENU("Add lead out")
@@ -280,7 +280,7 @@ struct time_add_lead_out : public Command {
 	}
 };
 
-struct time_length_increase : public Command {
+struct time_length_increase final : public Command {
 	CMD_NAME("time/length/increase")
 	STR_MENU("Increase length")
 	STR_DISP("Increase length")
@@ -291,7 +291,7 @@ struct time_length_increase : public Command {
 	}
 };
 
-struct time_length_increase_shift : public Command {
+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")
@@ -302,7 +302,7 @@ struct time_length_increase_shift : public Command {
 	}
 };
 
-struct time_length_decrease : public Command {
+struct time_length_decrease final : public Command {
 	CMD_NAME("time/length/decrease")
 	STR_MENU("Decrease length")
 	STR_DISP("Decrease length")
@@ -313,7 +313,7 @@ struct time_length_decrease : public Command {
 	}
 };
 
-struct time_length_decrease_shift : public Command {
+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")
@@ -324,7 +324,7 @@ struct time_length_decrease_shift : public Command {
 	}
 };
 
-struct time_start_increase : public Command {
+struct time_start_increase final : public Command {
 	CMD_NAME("time/start/increase")
 	STR_MENU("Shift start time forward")
 	STR_DISP("Shift start time forward")
@@ -335,7 +335,7 @@ struct time_start_increase : public Command {
 	}
 };
 
-struct time_start_decrease : public Command {
+struct time_start_decrease final : public Command {
 	CMD_NAME("time/start/decrease")
 	STR_MENU("Shift start time backward")
 	STR_DISP("Shift start time backward")
@@ -346,7 +346,7 @@ struct time_start_decrease : public Command {
 	}
 };
 
-struct time_snap_start_video : public validate_video_loaded {
+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")
@@ -358,7 +358,7 @@ struct time_snap_start_video : public validate_video_loaded {
 	}
 };
 
-struct time_next : public Command {
+struct time_next final : public Command {
 	CMD_NAME("time/next")
 	CMD_ICON(button_next)
 	STR_MENU("Next Line")
@@ -370,7 +370,7 @@ struct time_next : public Command {
 	}
 };
 
-struct time_prev : public Command {
+struct time_prev final : public Command {
 	CMD_NAME("time/prev")
 	CMD_ICON(button_prev)
 	STR_MENU("Previous Line")
diff --git a/src/command/timecode.cpp b/src/command/timecode.cpp
index 653dc201e25733b8e225f9c088ae5f8308b3ac34..74cc30252da7480fb3e5dc5e1400c73e6f4bc373 100644
--- a/src/command/timecode.cpp
+++ b/src/command/timecode.cpp
@@ -44,7 +44,7 @@
 namespace {
 	using cmd::Command;
 
-struct timecode_close : public Command {
+struct timecode_close final : public Command {
 	CMD_NAME("timecode/close")
 	CMD_ICON(close_timecodes_menu)
 	STR_MENU("Close Timecodes File")
@@ -61,7 +61,7 @@ struct timecode_close : public Command {
 	}
 };
 
-struct timecode_open : public Command {
+struct timecode_open final : public Command {
 	CMD_NAME("timecode/open")
 	CMD_ICON(open_timecodes_menu)
 	STR_MENU("Open Timecodes File...")
@@ -76,7 +76,7 @@ struct timecode_open : public Command {
 	}
 };
 
-struct timecode_save : public Command {
+struct timecode_save final : public Command {
 	CMD_NAME("timecode/save")
 	CMD_ICON(save_timecodes_menu)
 	STR_MENU("Save Timecodes File...")
diff --git a/src/command/tool.cpp b/src/command/tool.cpp
index b6cecc8bf8c4d4f1a0ed52c4c74a1174596985d2..7034f424b15056e2124863216c05da27df909ce8 100644
--- a/src/command/tool.cpp
+++ b/src/command/tool.cpp
@@ -60,7 +60,7 @@
 namespace {
 	using cmd::Command;
 
-struct tool_assdraw : public Command {
+struct tool_assdraw final : public Command {
 	CMD_NAME("tool/assdraw")
 	CMD_ICON(assdraw)
 	STR_MENU("ASSDraw3...")
@@ -72,7 +72,7 @@ struct tool_assdraw : public Command {
 	}
 };
 
-struct tool_export : public Command {
+struct tool_export final : public Command {
 	CMD_NAME("tool/export")
 	CMD_ICON(export_menu)
 	STR_MENU("&Export Subtitles...")
@@ -85,7 +85,7 @@ struct tool_export : public Command {
 	}
 };
 
-struct tool_font_collector : public Command {
+struct tool_font_collector final : public Command {
 	CMD_NAME("tool/font_collector")
 	CMD_ICON(font_collector_button)
 	STR_MENU("&Fonts Collector...")
@@ -97,7 +97,7 @@ struct tool_font_collector : public Command {
 	}
 };
 
-struct tool_line_select : public Command {
+struct tool_line_select final : public Command {
 	CMD_NAME("tool/line/select")
 	CMD_ICON(select_lines_button)
 	STR_MENU("S&elect Lines...")
@@ -109,7 +109,7 @@ struct tool_line_select : public Command {
 	}
 };
 
-struct tool_resampleres : public Command {
+struct tool_resampleres final : public Command {
 	CMD_NAME("tool/resampleres")
 	CMD_ICON(resample_toolbutton)
 	STR_MENU("&Resample Resolution...")
@@ -124,7 +124,7 @@ struct tool_resampleres : public Command {
 	}
 };
 
-struct tool_style_assistant : public Command {
+struct tool_style_assistant final : public Command {
 	CMD_NAME("tool/style/assistant")
 	CMD_ICON(styling_toolbutton)
 	STR_MENU("St&yling Assistant...")
@@ -144,7 +144,7 @@ struct tool_styling_assistant_validator : public Command {
 	}
 };
 
-struct tool_styling_assistant_commit : public tool_styling_assistant_validator {
+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")
@@ -155,7 +155,7 @@ struct tool_styling_assistant_commit : public tool_styling_assistant_validator {
 	}
 };
 
-struct tool_styling_assistant_preview : public tool_styling_assistant_validator {
+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")
@@ -166,7 +166,7 @@ struct tool_styling_assistant_preview : public tool_styling_assistant_validator
 	}
 };
 
-struct tool_style_manager : public Command {
+struct tool_style_manager final : public Command {
 	CMD_NAME("tool/style/manager")
 	CMD_ICON(style_toolbutton)
 	STR_MENU("&Styles Manager...")
@@ -178,7 +178,7 @@ struct tool_style_manager : public Command {
 	}
 };
 
-struct tool_time_kanji : public Command {
+struct tool_time_kanji final : public Command {
 	CMD_NAME("tool/time/kanji")
 	CMD_ICON(kara_timing_copier)
 	STR_MENU("&Kanji Timer...")
@@ -190,7 +190,7 @@ struct tool_time_kanji : public Command {
 	}
 };
 
-struct tool_time_postprocess : public Command {
+struct tool_time_postprocess final : public Command {
 	CMD_NAME("tool/time/postprocess")
 	CMD_ICON(timing_processor_toolbutton)
 	STR_MENU("&Timing Post-Processor...")
@@ -202,7 +202,7 @@ struct tool_time_postprocess : public Command {
 	}
 };
 
-struct tool_translation_assistant : public Command {
+struct tool_translation_assistant final : public Command {
 	CMD_NAME("tool/translation_assistant")
 	CMD_ICON(translation_toolbutton)
 	STR_MENU("&Translation Assistant...")
@@ -228,7 +228,7 @@ struct tool_translation_assistant_validator : public Command {
 	}
 };
 
-struct tool_translation_assistant_commit : public tool_translation_assistant_validator {
+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")
@@ -239,7 +239,7 @@ struct tool_translation_assistant_commit : public tool_translation_assistant_val
 	}
 };
 
-struct tool_translation_assistant_preview : public tool_translation_assistant_validator {
+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")
@@ -250,7 +250,7 @@ struct tool_translation_assistant_preview : public tool_translation_assistant_va
 	}
 };
 
-struct tool_translation_assistant_next : public tool_translation_assistant_validator {
+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")
@@ -261,7 +261,7 @@ struct tool_translation_assistant_next : public tool_translation_assistant_valid
 	}
 };
 
-struct tool_translation_assistant_prev : public tool_translation_assistant_validator {
+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")
@@ -273,7 +273,7 @@ struct tool_translation_assistant_prev : public tool_translation_assistant_valid
 };
 }
 
-struct tool_translation_assistant_insert : public tool_translation_assistant_validator {
+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")
diff --git a/src/command/video.cpp b/src/command/video.cpp
index 47bf259bc6bb3335db7359fd8899921c5a66d129..5cb4d99106f9a3226d6213b2df2556e9f0409c85 100644
--- a/src/command/video.cpp
+++ b/src/command/video.cpp
@@ -85,7 +85,7 @@ struct validator_video_attached : public Command {
 	}
 };
 
-struct video_aspect_cinematic : public validator_video_loaded {
+struct video_aspect_cinematic final : public validator_video_loaded {
 	CMD_NAME("video/aspect/cinematic")
 	STR_MENU("&Cinematic (2.35)")
 	STR_DISP("Cinematic (2.35)")
@@ -103,7 +103,7 @@ struct video_aspect_cinematic : public validator_video_loaded {
 	}
 };
 
-struct video_aspect_custom : public validator_video_loaded {
+struct video_aspect_custom final : public validator_video_loaded {
 	CMD_NAME("video/aspect/custom")
 	STR_MENU("C&ustom...")
 	STR_DISP("Custom")
@@ -146,7 +146,7 @@ struct video_aspect_custom : public validator_video_loaded {
 	}
 };
 
-struct video_aspect_default : public validator_video_loaded {
+struct video_aspect_default final : public validator_video_loaded {
 	CMD_NAME("video/aspect/default")
 	STR_MENU("&Default")
 	STR_DISP("Default")
@@ -164,7 +164,7 @@ struct video_aspect_default : public validator_video_loaded {
 	}
 };
 
-struct video_aspect_full : public validator_video_loaded {
+struct video_aspect_full final : public validator_video_loaded {
 	CMD_NAME("video/aspect/full")
 	STR_MENU("&Fullscreen (4:3)")
 	STR_DISP("Fullscreen (4:3)")
@@ -182,7 +182,7 @@ struct video_aspect_full : public validator_video_loaded {
 	}
 };
 
-struct video_aspect_wide : public validator_video_loaded {
+struct video_aspect_wide final : public validator_video_loaded {
 	CMD_NAME("video/aspect/wide")
 	STR_MENU("&Widescreen (16:9)")
 	STR_DISP("Widescreen (16:9)")
@@ -200,7 +200,7 @@ struct video_aspect_wide : public validator_video_loaded {
 	}
 };
 
-struct video_close : public validator_video_loaded {
+struct video_close final : public validator_video_loaded {
 	CMD_NAME("video/close")
 	CMD_ICON(close_video_menu)
 	STR_MENU("&Close Video")
@@ -212,7 +212,7 @@ struct video_close : public validator_video_loaded {
 	}
 };
 
-struct video_copy_coordinates : public validator_video_loaded {
+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")
@@ -223,7 +223,7 @@ struct video_copy_coordinates : public validator_video_loaded {
 	}
 };
 
-struct video_cycle_subtitles_provider : public cmd::Command {
+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")
@@ -242,7 +242,7 @@ struct video_cycle_subtitles_provider : public cmd::Command {
 	}
 };
 
-struct video_detach : public validator_video_loaded {
+struct video_detach final : public validator_video_loaded {
 	CMD_NAME("video/detach")
 	CMD_ICON(detach_video_menu)
 	STR_MENU("&Detach Video")
@@ -262,7 +262,7 @@ struct video_detach : public validator_video_loaded {
 	}
 };
 
-struct video_details : public validator_video_loaded {
+struct video_details final : public validator_video_loaded {
 	CMD_NAME("video/details")
 	CMD_ICON(show_video_details_menu)
 	STR_MENU("Show &Video Details")
@@ -275,7 +275,7 @@ struct video_details : public validator_video_loaded {
 	}
 };
 
-struct video_focus_seek : public validator_video_loaded {
+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")
@@ -293,7 +293,7 @@ struct video_focus_seek : public validator_video_loaded {
 	}
 };
 
-struct video_frame_copy : public validator_video_loaded {
+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")
@@ -304,7 +304,7 @@ struct video_frame_copy : public validator_video_loaded {
 	}
 };
 
-struct video_frame_copy_raw : public validator_video_loaded {
+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)")
@@ -315,7 +315,7 @@ struct video_frame_copy_raw : public validator_video_loaded {
 	}
 };
 
-struct video_frame_next : public validator_video_loaded {
+struct video_frame_next final : public validator_video_loaded {
 	CMD_NAME("video/frame/next")
 	STR_MENU("Next Frame")
 	STR_DISP("Next Frame")
@@ -326,7 +326,7 @@ struct video_frame_next : public validator_video_loaded {
 	}
 };
 
-struct video_frame_next_boundary : public validator_video_loaded {
+struct video_frame_next_boundary final : public validator_video_loaded {
 	CMD_NAME("video/frame/next/boundary")
 	STR_MENU("Next Boundary")
 	STR_DISP("Next Boundary")
@@ -355,7 +355,7 @@ struct video_frame_next_boundary : public validator_video_loaded {
 	}
 };
 
-struct video_frame_next_keyframe : public validator_video_loaded {
+struct video_frame_next_keyframe final : public validator_video_loaded {
 	CMD_NAME("video/frame/next/keyframe")
 	STR_MENU("Next Keyframe")
 	STR_DISP("Next Keyframe")
@@ -369,7 +369,7 @@ struct video_frame_next_keyframe : public validator_video_loaded {
 	}
 };
 
-struct video_frame_next_large : public validator_video_loaded {
+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")
@@ -382,7 +382,7 @@ struct video_frame_next_large : public validator_video_loaded {
 	}
 };
 
-struct video_frame_prev : public validator_video_loaded {
+struct video_frame_prev final : public validator_video_loaded {
 	CMD_NAME("video/frame/prev")
 	STR_MENU("Previous Frame")
 	STR_DISP("Previous Frame")
@@ -393,7 +393,7 @@ struct video_frame_prev : public validator_video_loaded {
 	}
 };
 
-struct video_frame_prev_boundary : public validator_video_loaded {
+struct video_frame_prev_boundary final : public validator_video_loaded {
 	CMD_NAME("video/frame/prev/boundary")
 	STR_MENU("Previous Boundary")
 	STR_DISP("Previous Boundary")
@@ -422,7 +422,7 @@ struct video_frame_prev_boundary : public validator_video_loaded {
 	}
 };
 
-struct video_frame_prev_keyframe : public validator_video_loaded {
+struct video_frame_prev_keyframe final : public validator_video_loaded {
 	CMD_NAME("video/frame/prev/keyframe")
 	STR_MENU("Previous Keyframe")
 	STR_DISP("Previous Keyframe")
@@ -444,7 +444,7 @@ struct video_frame_prev_keyframe : public validator_video_loaded {
 	}
 };
 
-struct video_frame_prev_large : public validator_video_loaded {
+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")
@@ -495,7 +495,7 @@ static void save_snapshot(agi::Context *c, bool raw) {
 	GetImage(*c->videoController->GetFrame(c->videoController->GetFrameN(), raw)).SaveFile(to_wx(path), wxBITMAP_TYPE_PNG);
 }
 
-struct video_frame_save : public validator_video_loaded {
+struct video_frame_save final : public validator_video_loaded {
 	CMD_NAME("video/frame/save")
 	STR_MENU("Save PNG snapshot")
 	STR_DISP("Save PNG snapshot")
@@ -506,7 +506,7 @@ struct video_frame_save : public validator_video_loaded {
 	}
 };
 
-struct video_frame_save_raw : public validator_video_loaded {
+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)")
@@ -517,7 +517,7 @@ struct video_frame_save_raw : public validator_video_loaded {
 	}
 };
 
-struct video_jump : public validator_video_loaded {
+struct video_jump final : public validator_video_loaded {
 	CMD_NAME("video/jump")
 	CMD_ICON(jumpto_button)
 	STR_MENU("&Jump to...")
@@ -533,7 +533,7 @@ struct video_jump : public validator_video_loaded {
 	}
 };
 
-struct video_jump_end : public validator_video_loaded {
+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")
@@ -547,7 +547,7 @@ struct video_jump_end : public validator_video_loaded {
 	}
 };
 
-struct video_jump_start : public validator_video_loaded {
+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")
@@ -560,7 +560,7 @@ struct video_jump_start : public validator_video_loaded {
 	}
 };
 
-struct video_open : public Command {
+struct video_open final : public Command {
 	CMD_NAME("video/open")
 	CMD_ICON(open_video_menu)
 	STR_MENU("&Open Video...")
@@ -576,7 +576,7 @@ struct video_open : public Command {
 	}
 };
 
-struct video_open_dummy : public Command {
+struct video_open_dummy final : public Command {
 	CMD_NAME("video/open/dummy")
 	CMD_ICON(use_dummy_video_menu)
 	STR_MENU("&Use Dummy Video...")
@@ -590,7 +590,7 @@ struct video_open_dummy : public Command {
 	}
 };
 
-struct video_opt_autoscroll : public Command {
+struct video_opt_autoscroll final : public Command {
 	CMD_NAME("video/opt/autoscroll")
 	CMD_ICON(toggle_video_autoscroll)
 	STR_MENU("Toggle autoscroll of video")
@@ -607,7 +607,7 @@ struct video_opt_autoscroll : public Command {
 	}
 };
 
-struct video_play : public validator_video_loaded {
+struct video_play final : public validator_video_loaded {
 	CMD_NAME("video/play")
 	CMD_ICON(button_play)
 	STR_MENU("Play")
@@ -619,7 +619,7 @@ struct video_play : public validator_video_loaded {
 	}
 };
 
-struct video_play_line : public validator_video_loaded {
+struct video_play_line final : public validator_video_loaded {
 	CMD_NAME("video/play/line")
 	CMD_ICON(button_playline)
 	STR_MENU("Play line")
@@ -631,7 +631,7 @@ struct video_play_line : public validator_video_loaded {
 	}
 };
 
-struct video_show_overscan : public validator_video_loaded {
+struct video_show_overscan final : public validator_video_loaded {
 	CMD_NAME("video/show_overscan")
 	STR_MENU("Show &Overscan Mask")
 	STR_DISP("Show Overscan Mask")
@@ -715,7 +715,7 @@ public:
 	}
 };
 
-struct video_zoom_in : public validator_video_attached {
+struct video_zoom_in final : public validator_video_attached {
 	CMD_NAME("video/zoom/in")
 	CMD_ICON(zoom_in_button)
 	STR_MENU("Zoom In")
@@ -727,7 +727,7 @@ struct video_zoom_in : public validator_video_attached {
 	}
 };
 
-struct video_zoom_out : public validator_video_attached {
+struct video_zoom_out final : public validator_video_attached {
 	CMD_NAME("video/zoom/out")
 	CMD_ICON(zoom_out_button)
 	STR_MENU("Zoom Out")
diff --git a/src/command/vis_tool.cpp b/src/command/vis_tool.cpp
index af6999f292ad23569113230aee0e3deeffc9f3b1..2d3b7413fbdee5f4671d86b4eaec58d8d8ddbc1f 100644
--- a/src/command/vis_tool.cpp
+++ b/src/command/vis_tool.cpp
@@ -37,7 +37,7 @@ namespace {
 	using cmd::Command;
 
 	template<class T>
-	struct visual_tool_command : public Command {
+	struct visual_tool_command final : public Command {
 		CMD_TYPE(COMMAND_VALIDATE | COMMAND_RADIO)
 
 		bool Validate(const agi::Context *c) override {
@@ -53,7 +53,7 @@ namespace {
 		}
 	};
 
-	struct visual_mode_cross : public visual_tool_command<VisualToolCross> {
+	struct visual_mode_cross final : public visual_tool_command<VisualToolCross> {
 		CMD_NAME("video/tool/cross")
 		CMD_ICON(visual_standard)
 		STR_MENU("Standard")
@@ -61,7 +61,7 @@ namespace {
 		STR_HELP("Standard mode, double click sets position")
 	};
 
-	struct visual_mode_drag : public visual_tool_command<VisualToolDrag> {
+	struct visual_mode_drag final : public visual_tool_command<VisualToolDrag> {
 		CMD_NAME("video/tool/drag")
 		CMD_ICON(visual_move)
 		STR_MENU("Drag")
@@ -69,7 +69,7 @@ namespace {
 		STR_HELP("Drag subtitles")
 	};
 
-	struct visual_mode_rotate_z : public visual_tool_command<VisualToolRotateZ> {
+	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")
@@ -77,7 +77,7 @@ namespace {
 		STR_HELP("Rotate subtitles on their Z axis")
 	};
 
-	struct visual_mode_rotate_xy : public visual_tool_command<VisualToolRotateXY> {
+	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")
@@ -85,7 +85,7 @@ namespace {
 		STR_HELP("Rotate subtitles on their X and Y axes")
 	};
 
-	struct visual_mode_scale : public visual_tool_command<VisualToolScale> {
+	struct visual_mode_scale final : public visual_tool_command<VisualToolScale> {
 		CMD_NAME("video/tool/scale")
 		CMD_ICON(visual_scale)
 		STR_MENU("Scale")
@@ -93,7 +93,7 @@ namespace {
 		STR_HELP("Scale subtitles on X and Y axes")
 	};
 
-	struct visual_mode_clip : public visual_tool_command<VisualToolClip> {
+	struct visual_mode_clip final : public visual_tool_command<VisualToolClip> {
 		CMD_NAME("video/tool/clip")
 		CMD_ICON(visual_clip)
 		STR_MENU("Clip")
@@ -101,7 +101,7 @@ namespace {
 		STR_HELP("Clip subtitles to a rectangle")
 	};
 
-	struct visual_mode_vector_clip : public visual_tool_command<VisualToolVectorClip> {
+	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")
diff --git a/src/dialog_attachments.h b/src/dialog_attachments.h
index 3a444c9bb39a9d7c4d6408e2f0e9bdbdf2648e67..4329a9d896088196e24eeb9fcdfdda1806d67362 100644
--- a/src/dialog_attachments.h
+++ b/src/dialog_attachments.h
@@ -39,7 +39,7 @@ class wxListEvent;
 
 #include <wx/dialog.h>
 
-class DialogAttachments : public wxDialog {
+class DialogAttachments final : public wxDialog {
 	AssFile *ass;
 
 	wxListView *listView;
diff --git a/src/dialog_automation.h b/src/dialog_automation.h
index a9b7d2c783bbf47243418c5684e07b785efaf2f6..fc4c5fa615cb1eafdbe604bfe6d91312bd2446e7 100644
--- a/src/dialog_automation.h
+++ b/src/dialog_automation.h
@@ -50,7 +50,7 @@ class wxButton;
 class wxListEvent;
 class wxListView;
 
-class DialogAutomation : public wxDialog {
+class DialogAutomation final : public wxDialog {
 	agi::Context *context;
 
 	/// Struct to attach a flag for global/local to scripts
diff --git a/src/dialog_autosave.h b/src/dialog_autosave.h
index 6be7932a62bfb7332b3455982e135fd80fa0c1fd..cc5969f3fd49973f8d115ca021eefac70588a77b 100644
--- a/src/dialog_autosave.h
+++ b/src/dialog_autosave.h
@@ -24,7 +24,7 @@
 
 class wxListBox;
 
-class DialogAutosave : public wxDialog {
+class DialogAutosave final : public wxDialog {
 	struct Version {
 		wxString filename;
 		wxDateTime date;
diff --git a/src/dialog_colorpicker.cpp b/src/dialog_colorpicker.cpp
index 742339b24ae8c3d1b947bf0752dc295262e59ba0..bc56bd9b3f0b966b721f6a673b7ccd8b06cabf61 100644
--- a/src/dialog_colorpicker.cpp
+++ b/src/dialog_colorpicker.cpp
@@ -86,7 +86,7 @@ static const int spectrum_horz_vert_arrow_size = 4;
 
 wxDEFINE_EVENT(EVT_SPECTRUM_CHANGE, wxCommandEvent);
 
-class ColorPickerSpectrum : public wxControl {
+class ColorPickerSpectrum final : public wxControl {
 	int x;
 	int y;
 
@@ -251,7 +251,7 @@ wxDEFINE_EVENT(EVT_RECENT_SELECT, wxThreadEvent);
 
 /// @class ColorPickerRecent
 /// @brief A grid of recently used colors which can be selected by clicking on them
-class ColorPickerRecent : public wxStaticBitmap {
+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
@@ -344,7 +344,7 @@ public:
 
 wxDEFINE_EVENT(EVT_DROPPER_SELECT, wxThreadEvent);
 
-class ColorPickerScreenDropper : public wxControl {
+class ColorPickerScreenDropper final : public wxControl {
 	wxBitmap capture;
 
 	int resx, resy;
@@ -434,7 +434,7 @@ void ColorPickerScreenDropper::DropFromScreenXY(int x, int y) {
 }
 
 
-class DialogColorPicker : public wxDialog {
+class DialogColorPicker final : public wxDialog {
 	std::unique_ptr<PersistLocation> persist;
 
 	agi::Color cur_color; ///< Currently selected colour
diff --git a/src/dialog_detached_video.h b/src/dialog_detached_video.h
index b0933578a1b2cfa57f6829625ab14c27cb9ed0da..ec8e17fe3d969337d3db67ebcf9e5027e1050704 100644
--- a/src/dialog_detached_video.h
+++ b/src/dialog_detached_video.h
@@ -42,7 +42,7 @@ class PersistLocation;
 class VideoBox;
 class VideoDisplay;
 
-class DialogDetachedVideo : public wxDialog {
+class DialogDetachedVideo final : public wxDialog {
 	agi::Context *context;
 	VideoDisplay *old_display;
 	wxWindow *old_slider;
diff --git a/src/dialog_dummy_video.h b/src/dialog_dummy_video.h
index 667d4904520c4d86dd6860599abaa41f3809c43b..7739ff7888b0360ac752c6692c03b7788b879445 100644
--- a/src/dialog_dummy_video.h
+++ b/src/dialog_dummy_video.h
@@ -26,7 +26,7 @@
 class wxFlexGridSizer;
 class wxStaticText;
 
-class DialogDummyVideo : public wxDialog {
+class DialogDummyVideo final : public wxDialog {
 	DialogDummyVideo(wxWindow *parent);
 
 	double fps;
diff --git a/src/dialog_export.h b/src/dialog_export.h
index ee9def6a58bc065dda44a982432accb9c396513d..0ce3645e403f4ebd9f62b09dbfaf2c0ded46bae3 100644
--- a/src/dialog_export.h
+++ b/src/dialog_export.h
@@ -42,7 +42,7 @@ class wxChoice;
 class wxSizer;
 class wxTextCtrl;
 
-class DialogExport : public wxDialog {
+class DialogExport final : public wxDialog {
 	agi::Context *c;
 
 	/// The export transform engine
diff --git a/src/dialog_export_ebu3264.cpp b/src/dialog_export_ebu3264.cpp
index 4a33b43d192172aafe93820f184097f81b8cd3f5..a7d627db20cb0853db05be6000c2ac6cff0e3428 100644
--- a/src/dialog_export_ebu3264.cpp
+++ b/src/dialog_export_ebu3264.cpp
@@ -48,7 +48,7 @@ namespace {
 	const boost::regex timecode_regex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2})");
 
 	/// Validator for SMPTE timecodes
-	class TimecodeValidator : public wxValidator {
+	class TimecodeValidator final : public wxValidator {
 		EbuTimecode *value;
 
 		wxTextCtrl *GetCtrl() const { return dynamic_cast<wxTextCtrl*>(GetWindow()); }
diff --git a/src/dialog_export_ebu3264.h b/src/dialog_export_ebu3264.h
index dbc1533629a6d3a6c29d8d10cdafbde5a1d3ee9c..1ab54f000911f89268f41150d025f9a36faa14d2 100644
--- a/src/dialog_export_ebu3264.h
+++ b/src/dialog_export_ebu3264.h
@@ -112,7 +112,7 @@ public:
 };
 
 /// Dialog box for getting an export configuration for EBU Tech 3264-1991
-class EbuExportConfigurationDialog : public wxDialog {
+class EbuExportConfigurationDialog final : public wxDialog {
 public:
 	/// Constructor
 	/// @param owner Parent window of the dialog
diff --git a/src/dialog_fonts_collector.h b/src/dialog_fonts_collector.h
index 930c41e70031d77f63231a3e569cea8f083d7a00..72409bf8731477988076d2fd6d390ab418c56fb0 100644
--- a/src/dialog_fonts_collector.h
+++ b/src/dialog_fonts_collector.h
@@ -31,7 +31,7 @@ class wxStaticText;
 class wxTextCtrl;
 class wxThreadEvent;
 
-class DialogFontsCollector : public wxDialog {
+class DialogFontsCollector final : public wxDialog {
 	AssFile *subs;
 
 	ScintillaTextCtrl *collection_log;
diff --git a/src/dialog_jumpto.h b/src/dialog_jumpto.h
index ce244e1cd8fee8d042d5671981b9fb26289f4221..b4a9612ae8c39b7fa60c092f242ddb8fd11be64c 100644
--- a/src/dialog_jumpto.h
+++ b/src/dialog_jumpto.h
@@ -38,7 +38,7 @@ namespace agi { struct Context; }
 class TimeEdit;
 class wxTextCtrl;
 
-class DialogJumpTo : public wxDialog {
+class DialogJumpTo final : public wxDialog {
 	agi::Context *c;       ///< Project context
 	long jumpframe;        ///< Target frame to jump to
 	TimeEdit *JumpTime;    ///< Target time edit control
diff --git a/src/dialog_kara_timing_copy.cpp b/src/dialog_kara_timing_copy.cpp
index 030bff8e3b8fa49d187add30e7604d8c6319d174..7bb615cc374339e7e3179c3b7cc9e5819393f472 100644
--- a/src/dialog_kara_timing_copy.cpp
+++ b/src/dialog_kara_timing_copy.cpp
@@ -69,7 +69,7 @@
 #define TEXT_LABEL_SOURCE _("Source: ")
 #define TEXT_LABEL_DEST _("Dest: ")
 
-class KaraokeLineMatchDisplay : public wxControl {
+class KaraokeLineMatchDisplay final : public wxControl {
 	typedef AssKaraoke::Syllable MatchSyllable;
 
 	struct MatchGroup {
diff --git a/src/dialog_kara_timing_copy.h b/src/dialog_kara_timing_copy.h
index ab61b3ba8d193887082ea0e44377605dee069fba..2431944dc5770b3a1eebabe68357ec5407daab79 100644
--- a/src/dialog_kara_timing_copy.h
+++ b/src/dialog_kara_timing_copy.h
@@ -44,7 +44,7 @@ class KaraokeLineMatchDisplay;
 class wxComboBox;
 class wxCheckBox;
 
-class DialogKanjiTimer : public wxDialog {
+class DialogKanjiTimer final : public wxDialog {
 	AssFile *subs;
 
 	KaraokeLineMatchDisplay *display;
diff --git a/src/dialog_log.cpp b/src/dialog_log.cpp
index 88d4bd31521479fc341e179858b529dfcabcbf1a..7ff9169c8f0e9aec62e988401be93245434edde4 100644
--- a/src/dialog_log.cpp
+++ b/src/dialog_log.cpp
@@ -52,7 +52,7 @@
 #include <wx/stattext.h>
 #include <wx/textctrl.h>
 
-class EmitLog : public agi::log::Emitter {
+class EmitLog final : public agi::log::Emitter {
 	wxTextCtrl *text_ctrl;
 public:
 	EmitLog(wxTextCtrl *t)
diff --git a/src/dialog_paste_over.h b/src/dialog_paste_over.h
index 652dad418623e12d37606bee9c16a02c372c5f8d..26d4afe348415cfc611c4ceb087d01a1179a4f2c 100644
--- a/src/dialog_paste_over.h
+++ b/src/dialog_paste_over.h
@@ -36,7 +36,7 @@
 
 class wxCheckListBox;
 
-class DialogPasteOver : public wxDialog {
+class DialogPasteOver final : public wxDialog {
 	wxCheckListBox *ListBox;
 
 	void CheckAll(bool check);
diff --git a/src/dialog_progress.cpp b/src/dialog_progress.cpp
index 9f2ed6233d28f819b039f250b22d0885c51c701c..48321132a49a60494cab70ab0a9448c0b41bbd69 100644
--- a/src/dialog_progress.cpp
+++ b/src/dialog_progress.cpp
@@ -72,7 +72,7 @@ namespace {
 	}
 }
 
-class DialogProgressSink : public agi::ProgressSink {
+class DialogProgressSink final : public agi::ProgressSink {
 	DialogProgress *dialog;
 	std::atomic<bool> cancelled;
 
diff --git a/src/dialog_progress.h b/src/dialog_progress.h
index 14b1c31a90493ecddfcd2c3e303ed682164421b9..d6891b8b0f173394e78e776c3a653a8002ccb8af 100644
--- a/src/dialog_progress.h
+++ b/src/dialog_progress.h
@@ -31,7 +31,7 @@ class wxTextCtrl;
 
 /// @class DialogProgress
 /// @brief Progress-bar dialog box for displaying during long operations
-class DialogProgress : public wxDialog, public agi::BackgroundRunner {
+class DialogProgress final : public wxDialog, public agi::BackgroundRunner {
 	friend class DialogProgressSink;
 	DialogProgressSink *ps;
 
diff --git a/src/dialog_properties.h b/src/dialog_properties.h
index 562db12727b978a9b2137033bc8308711e3cafde..1b4b1900e89784cf5cc8543ac16e940fea0749b2 100644
--- a/src/dialog_properties.h
+++ b/src/dialog_properties.h
@@ -41,7 +41,7 @@ class wxCheckBox;
 class wxComboBox;
 class wxTextCtrl;
 
-class DialogProperties : public wxDialog {
+class DialogProperties final : public wxDialog {
 	agi::Context *c; ///< Project this dialog is adjusting the properties of
 
 	/// Pairs of a script property and a text control for that property
diff --git a/src/dialog_resample.h b/src/dialog_resample.h
index 3b55ef22719691d63e83d5d7527fddac61b16edb..ce973a64ca68286eae04f43348dcac4b2532d111 100644
--- a/src/dialog_resample.h
+++ b/src/dialog_resample.h
@@ -29,7 +29,7 @@ struct ResampleSettings;
 /// @brief Configuration dialog for resolution resampling
 ///
 /// Populate a ResampleSettings structure with data from the user
-class DialogResample : public wxDialog {
+class DialogResample final : public wxDialog {
 	agi::Context *c; ///< Project context
 
 	wxSpinCtrl *res_x;
diff --git a/src/dialog_search_replace.h b/src/dialog_search_replace.h
index bed377c802762a8a26bba627e25e48e887ea5e2c..2d231e7ec5528c0accd1d73d0a2c31b686ae9e3d 100644
--- a/src/dialog_search_replace.h
+++ b/src/dialog_search_replace.h
@@ -28,7 +28,7 @@ class SearchReplaceEngine;
 struct SearchReplaceSettings;
 class wxComboBox;
 
-class DialogSearchReplace : public wxDialog {
+class DialogSearchReplace final : public wxDialog {
 	agi::Context *c;
 	std::unique_ptr<SearchReplaceSettings> settings;
 	bool has_replace;
diff --git a/src/dialog_selected_choices.h b/src/dialog_selected_choices.h
index 7671b86e5cbc265b310da8bbbd8c0beea4e738a2..cf46bef58c5cffe8d23623ed07be6cabe0c993dd 100644
--- a/src/dialog_selected_choices.h
+++ b/src/dialog_selected_choices.h
@@ -23,7 +23,7 @@
 
 /// @class SelectedChoicesDialog
 /// @brief wxMultiChoiceDialog with Select All and Select None
-class SelectedChoicesDialog : public wxMultiChoiceDialog {
+class SelectedChoicesDialog final : public wxMultiChoiceDialog {
 	SelectedChoicesDialog(SelectedChoicesDialog const&);
 	SelectedChoicesDialog& operator=(SelectedChoicesDialog const&);
 
diff --git a/src/dialog_selection.h b/src/dialog_selection.h
index 947261a63d5d1ea13c3ec56d70b97be9e3330f0a..7389b67d9ebec70d23a61b0ae4b69525cbc41f43 100644
--- a/src/dialog_selection.h
+++ b/src/dialog_selection.h
@@ -28,7 +28,7 @@ class wxRadioBox;
 class wxRadioButton;
 class wxTextCtrl;
 
-class DialogSelection : public wxDialog {
+class DialogSelection final : public wxDialog {
 	agi::Context *con; ///< Project context
 
 	wxTextCtrl *match_text; ///< Text to search for
diff --git a/src/dialog_shift_times.h b/src/dialog_shift_times.h
index 6bb78597d8b6decc7538aac661f569c511814275..a55677acb56fb44f9090857ac5f7ae0994352665 100644
--- a/src/dialog_shift_times.h
+++ b/src/dialog_shift_times.h
@@ -43,7 +43,7 @@ namespace json {
 	typedef std::deque<UnknownElement> Array;
 }
 
-class DialogShiftTimes : public wxDialog {
+class DialogShiftTimes final : public wxDialog {
 	wxDECLARE_NO_COPY_CLASS(DialogShiftTimes); // clang + libc++ herps a derp without this
 	agi::Context *context;
 
diff --git a/src/dialog_spellchecker.h b/src/dialog_spellchecker.h
index d295442e929c596ad9121ac7ba23dff605ac72f3..f11cb67d123621125b60dce5dd119e361337f1f8 100644
--- a/src/dialog_spellchecker.h
+++ b/src/dialog_spellchecker.h
@@ -35,7 +35,7 @@ class wxComboBox;
 class wxListBox;
 class wxTextCtrl;
 
-class DialogSpellChecker : public wxDialog {
+class DialogSpellChecker final : public wxDialog {
 	agi::Context *context; ///< The project context
 	std::unique_ptr<agi::SpellChecker> spellchecker; ///< The spellchecking engine
 
diff --git a/src/dialog_style_editor.h b/src/dialog_style_editor.h
index 76e21b995b24b05e249be185dfd683b2f7c0d688..2aec2ee496ac696f922c295e8f51345d8fd05371 100644
--- a/src/dialog_style_editor.h
+++ b/src/dialog_style_editor.h
@@ -47,7 +47,7 @@ class AssStyleStorage;
 class PersistLocation;
 class SubtitlesPreview;
 
-class DialogStyleEditor : public wxDialog {
+class DialogStyleEditor final : public wxDialog {
 	agi::Context *c;
 	std::unique_ptr<PersistLocation> persist;
 
diff --git a/src/dialog_style_manager.h b/src/dialog_style_manager.h
index 6a848d852b04381019b4e2bdbf2664a60ea9e7c4..ed04d627ab04399c6cd6091906a1ac5fbf83de83 100644
--- a/src/dialog_style_manager.h
+++ b/src/dialog_style_manager.h
@@ -52,7 +52,7 @@ class AssStyle;
 class DialogStyleEditor;
 class PersistLocation;
 
-class DialogStyleManager : public wxDialog {
+class DialogStyleManager final : public wxDialog {
 	agi::Context *c; ///< Project context
 	std::unique_ptr<PersistLocation> persist;
 
diff --git a/src/dialog_styling_assistant.h b/src/dialog_styling_assistant.h
index 74820308eb87ba71410a7a01b45a95bf4c309a7c..8e3cb539a767cf0dfc517e3ef24f81ea36961fd4 100644
--- a/src/dialog_styling_assistant.h
+++ b/src/dialog_styling_assistant.h
@@ -33,7 +33,7 @@ class wxCheckBox;
 class wxListBox;
 class wxTextCtrl;
 
-class DialogStyling : public wxDialog {
+class DialogStyling final : public wxDialog {
 	agi::Context *c;
 	agi::signal::Connection active_line_connection;
 
diff --git a/src/dialog_text_import.h b/src/dialog_text_import.h
index 8588d2119e8eb614cd4ffb195e587dbd1b840f4f..e3078a6ba55e83fd86c979889e27c46e52fc926f 100644
--- a/src/dialog_text_import.h
+++ b/src/dialog_text_import.h
@@ -39,7 +39,7 @@
 ///
 /// A simple dialog to let the user select the format of a plain text file
 /// being imported into Aegisub
-class DialogTextImport : public wxDialog {
+class DialogTextImport final : public wxDialog {
 	std::string seperator;
 	std::string comment;
 	bool include_blank;
diff --git a/src/dialog_timing_processor.h b/src/dialog_timing_processor.h
index 827bf4d88d879aea695f2e0db0cfc2e8e500bbe4..166a074a136aaf6c601345a4dbe6d9ceea70b56a 100644
--- a/src/dialog_timing_processor.h
+++ b/src/dialog_timing_processor.h
@@ -45,7 +45,7 @@ class wxSlider;
 
 /// @class DialogTimingProcessor
 /// @brief Automatic postprocessor for correcting common timing issues
-class DialogTimingProcessor : public wxDialog {
+class DialogTimingProcessor final : public wxDialog {
 	agi::Context *c; ///< Project context
 
 	int leadIn;      ///< Lead-in to add in milliseconds
diff --git a/src/dialog_translation.h b/src/dialog_translation.h
index 70202f212019e7ef6b3fd69f6d17ff0e333fe071..538920b8093e15977f0644beaa4212be490c43b5 100644
--- a/src/dialog_translation.h
+++ b/src/dialog_translation.h
@@ -36,7 +36,7 @@ class wxStaticText;
 class wxCheckBox;
 
 /// Assistant for translating subtitles in one language to another language
-class DialogTranslation : public wxDialog {
+class DialogTranslation final : public wxDialog {
 	agi::Context *c;
 
 	agi::signal::Connection file_change_connection;
diff --git a/src/dialog_version_check.cpp b/src/dialog_version_check.cpp
index c4fbef19e590f61b8d27980a7bba548f11e81327..f99c3155795d66b4caf3b6af3e29e8b104eb6c4d 100644
--- a/src/dialog_version_check.cpp
+++ b/src/dialog_version_check.cpp
@@ -93,7 +93,7 @@ struct AegisubUpdateDescription {
 	: url(std::move(url)), friendly_name(std::move(friendly_name)), description(std::move(description)) { }
 };
 
-class VersionCheckerResultDialog : public wxDialog {
+class VersionCheckerResultDialog final : public wxDialog {
 	void OnCloseButton(wxCommandEvent &evt);
 	void OnRemindMeLater(wxCommandEvent &evt);
 	void OnClose(wxCloseEvent &evt);
diff --git a/src/dialog_video_details.h b/src/dialog_video_details.h
index dedff350df0e2e63c9b74ac1d0ad53948bda1dfd..f1b693b7f986b69b803428c3e570b91940c8d6c6 100644
--- a/src/dialog_video_details.h
+++ b/src/dialog_video_details.h
@@ -38,7 +38,7 @@ namespace agi { struct Context; }
 
 /// @class DialogVideoDetails
 /// @brief Display information about the video in a dialog
-struct DialogVideoDetails : public wxDialog {
+struct DialogVideoDetails final : public wxDialog {
 	/// Constructor
 	/// @param c Project context
 	DialogVideoDetails(agi::Context *c);
diff --git a/src/export_fixstyle.h b/src/export_fixstyle.h
index 6de4d5a885cfaeca9ffaeee2066264671a1a849d..343092c5d4c782e56ea436de05cab800f2eefec9 100644
--- a/src/export_fixstyle.h
+++ b/src/export_fixstyle.h
@@ -36,7 +36,7 @@
 
 /// @class AssFixStylesFilter
 /// @brief Fixes styles by replacing any style that isn't available on file with Default
-class AssFixStylesFilter : public AssExportFilter {
+class AssFixStylesFilter final : public AssExportFilter {
 public:
 	void ProcessSubs(AssFile *subs, wxWindow *) override;
 	AssFixStylesFilter();
diff --git a/src/export_framerate.h b/src/export_framerate.h
index 803cdfe850bd6f7bf5de2dec88e3fd0f402897ce..84c49d9e4b8949502d9389cdcee785bf25319f22 100644
--- a/src/export_framerate.h
+++ b/src/export_framerate.h
@@ -44,7 +44,7 @@ class wxTextCtrl;
 
 /// @class AssTransformFramerateFilter
 /// @brief Transform subtitle times, including those in override tags, from an input framerate to an output framerate
-class AssTransformFramerateFilter : public AssExportFilter {
+class AssTransformFramerateFilter final : public AssExportFilter {
 	agi::Context *c = nullptr;
 	AssDialogue *line = nullptr;
 	int newStart = 0;
diff --git a/src/font_file_lister_fontconfig.h b/src/font_file_lister_fontconfig.h
index a9d3542b0f6ff3c8ae6905c07fecaac03b7f8cda..9ff2d2a4968313c52d2bf950c61b99d3b75a1bd7 100644
--- a/src/font_file_lister_fontconfig.h
+++ b/src/font_file_lister_fontconfig.h
@@ -28,7 +28,7 @@ typedef struct _FcFontSet FcFontSet;
 
 /// @class FontConfigFontFileLister
 /// @brief fontconfig powered font lister
-class FontConfigFontFileLister : public FontFileLister {
+class FontConfigFontFileLister final : public FontFileLister {
 	agi::scoped_holder<FcConfig*> config;
 
 	/// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
diff --git a/src/frame_main.cpp b/src/frame_main.cpp
index c3c7ff0c9e053aeb6a4baec81da025a4695796eb..952e43cb106538d6c78933e716cb2430df6c8160 100644
--- a/src/frame_main.cpp
+++ b/src/frame_main.cpp
@@ -162,7 +162,7 @@ static void get_files_to_load(wxArrayString const& list, std::string &subs, std:
 }
 
 /// Handle files drag and dropped onto Aegisub
-class AegisubFileDropTarget : public wxFileDropTarget {
+class AegisubFileDropTarget final : public wxFileDropTarget {
 	FrameMain *parent;
 public:
 	AegisubFileDropTarget(FrameMain *parent) : parent(parent) { }
diff --git a/src/gl_text.cpp b/src/gl_text.cpp
index 82d63cecca51a17ec18cb2cee5b14f987be547f6..bef359c160da08204a5c08ac36879632ab00abab 100644
--- a/src/gl_text.cpp
+++ b/src/gl_text.cpp
@@ -114,7 +114,7 @@ struct OpenGLTextGlyph {
 
 /// @class OpenGLTextTexture
 /// @brief OpenGL texture which stores one or more glyphs as sprites
-class OpenGLTextTexture : boost::noncopyable {
+class OpenGLTextTexture final : boost::noncopyable {
 	int x;      ///< Next x coordinate at which a glyph can be inserted
 	int y;      ///< Next y coordinate at which a glyph can be inserted
 	int nextY;  ///< Y coordinate of the next line; tracked due to that lines
diff --git a/src/help_button.h b/src/help_button.h
index e9323e1f557b786c0fff00517c2ede012153de3b..143e45c3bec479fa0f39b73be6570bb50f4a941f 100644
--- a/src/help_button.h
+++ b/src/help_button.h
@@ -34,7 +34,7 @@
 
 #include <wx/button.h>
 
-class HelpButton : public wxButton {
+class HelpButton final : public wxButton {
 public:
 	HelpButton(wxWindow *parent, wxString const& page="", wxPoint position=wxDefaultPosition, wxSize size=wxDefaultSize);
 
diff --git a/src/hotkey_data_view_model.cpp b/src/hotkey_data_view_model.cpp
index b090f85dfe14c8ce995366b39b6ebe9bd2f80090..e9d6861a481f166f0b82b3720f0afcd9e392b516 100644
--- a/src/hotkey_data_view_model.cpp
+++ b/src/hotkey_data_view_model.cpp
@@ -60,7 +60,7 @@ class HotkeyModelCategory;
 /// @brief A single hotkey exposed in the data view
 ///
 /// All actual mutation of hotkeys happens through this class
-class HotkeyModelCombo : public HotkeyModelItem {
+class HotkeyModelCombo final : public HotkeyModelItem {
 	HotkeyModelCategory *parent; ///< The containing category
 	Combo combo;                 ///< The actual hotkey
 	wxString cmd_name;
@@ -136,7 +136,7 @@ public:
 };
 
 /// A hotkey context exposed in the data view
-class HotkeyModelCategory : public HotkeyModelItem {
+class HotkeyModelCategory final : public HotkeyModelItem {
 	std::list<HotkeyModelCombo> children;
 	wxDataViewModel *model;
 	wxString name;
@@ -216,7 +216,7 @@ public:
 };
 
 /// The root containing the hotkey contexts
-class HotkeyModelRoot : public HotkeyModelItem {
+class HotkeyModelRoot final : public HotkeyModelItem {
 	std::list<HotkeyModelCategory> categories;
 public:
 	HotkeyModelRoot(wxDataViewModel *model) {
diff --git a/src/hotkey_data_view_model.h b/src/hotkey_data_view_model.h
index e0c7bfecc144b5c97316fe9edf72719fff6aa933..236971e9cdad72ea40a4ba0a7e2d1b493ef699f9 100644
--- a/src/hotkey_data_view_model.h
+++ b/src/hotkey_data_view_model.h
@@ -28,7 +28,7 @@ class Preferences;
 
 /// @class HotkeyDataViewModel
 /// @brief A wxDataViewModel for hotkeys
-class HotkeyDataViewModel : public wxDataViewModel {
+class HotkeyDataViewModel final : public wxDataViewModel {
 	std::unique_ptr<HotkeyModelRoot> root;
 	Preferences *parent;
 	bool has_pending_changes = false;
diff --git a/src/include/aegisub/audio_player.h b/src/include/aegisub/audio_player.h
index 01c9075d37f063f1114021a0b3d5366645a11448..cab2f2b5ea67a3292dce6bd78cd074dc9789ccc0 100644
--- a/src/include/aegisub/audio_player.h
+++ b/src/include/aegisub/audio_player.h
@@ -57,7 +57,7 @@ public:
 	virtual void SetEndPosition(int64_t pos)=0;
 };
 
-class AudioPlayerFactory : public Factory<AudioPlayer, AudioProvider*> {
+class AudioPlayerFactory final : public Factory<AudioPlayer, AudioProvider*> {
 public:
 	static void RegisterProviders();
 	static std::unique_ptr<AudioPlayer> GetAudioPlayer(AudioProvider *provider);
diff --git a/src/include/aegisub/audio_provider.h b/src/include/aegisub/audio_provider.h
index 9de5f377d63da441ec12301120c2f4eb8f3c1a38..fc1c22aaa6a0f41a4d939d3dfde24daef72a801b 100644
--- a/src/include/aegisub/audio_provider.h
+++ b/src/include/aegisub/audio_provider.h
@@ -90,7 +90,7 @@ public:
 	}
 };
 
-class AudioProviderFactory : public Factory<AudioProvider, agi::fs::path> {
+class AudioProviderFactory final : public Factory<AudioProvider, agi::fs::path> {
 public:
 	static void RegisterProviders();
 
diff --git a/src/include/aegisub/spellchecker.h b/src/include/aegisub/spellchecker.h
index 953a40667dc1c5aaeb1160f4d16abe621823003d..6d952edb9f4da27307c14c348f0d064a52af8623 100644
--- a/src/include/aegisub/spellchecker.h
+++ b/src/include/aegisub/spellchecker.h
@@ -23,7 +23,7 @@
 
 namespace agi { class SpellChecker; }
 
-class SpellCheckerFactory : public Factory<agi::SpellChecker> {
+class SpellCheckerFactory final : public Factory<agi::SpellChecker> {
 public:
 	static std::unique_ptr<agi::SpellChecker> GetSpellChecker();
 	static void RegisterProviders();
diff --git a/src/include/aegisub/subtitles_provider.h b/src/include/aegisub/subtitles_provider.h
index 34d8d300b308eed97116d9b06e9a6846d0876951..4a7a71c0a8fb1f584eb50a643824be86a8ea40de 100644
--- a/src/include/aegisub/subtitles_provider.h
+++ b/src/include/aegisub/subtitles_provider.h
@@ -47,7 +47,7 @@ public:
 	virtual void DrawSubtitles(VideoFrame &dst,double time)=0;
 };
 
-class SubtitlesProviderFactory : public Factory<SubtitlesProvider, std::string> {
+class SubtitlesProviderFactory final : public Factory<SubtitlesProvider, std::string> {
 public:
 	static std::unique_ptr<SubtitlesProvider> GetProvider();
 	static void RegisterProviders();
diff --git a/src/menu.cpp b/src/menu.cpp
index 711fcc886240dc12703f13ee151c56e68bd6dc05..017c4ba10d7af56ba592cb052212d20dd1935039 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -52,7 +52,7 @@ static const int MENU_ID_BASE = 10000;
 using std::placeholders::_1;
 using std::bind;
 
-class MruMenu : public wxMenu {
+class MruMenu final : public wxMenu {
 	std::string type;
 	std::vector<wxMenuItem *> items;
 	std::vector<std::string> *cmds;
@@ -262,13 +262,13 @@ public:
 };
 
 /// Wrapper for wxMenu to add a command manager
-struct CommandMenu : public wxMenu {
+struct CommandMenu final : public wxMenu {
 	CommandManager cm;
 	CommandMenu(agi::Context *c) : cm(c) { }
 };
 
 /// Wrapper for wxMenuBar to add a command manager
-struct CommandMenuBar : public wxMenuBar {
+struct CommandMenuBar final : public wxMenuBar {
 	CommandManager cm;
 	CommandMenuBar(agi::Context *c) : cm(c) { }
 };
@@ -396,7 +396,7 @@ struct comp_str_menu {
 	}
 };
 
-class AutomationMenu : public wxMenu {
+class AutomationMenu final : public wxMenu {
 	agi::Context *c;
 	CommandManager *cm;
 	agi::signal::Connection global_slot;
diff --git a/src/mkv_wrap.cpp b/src/mkv_wrap.cpp
index 3534682768a9e1b95a36e548172a165d1fb13242..fd7e77422de9cfa346270d3237ff23d9e983eb67 100644
--- a/src/mkv_wrap.cpp
+++ b/src/mkv_wrap.cpp
@@ -61,7 +61,7 @@
 
 #include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
 
-class MkvStdIO : public InputStream {
+class MkvStdIO final : public InputStream {
 public:
 	MkvStdIO(agi::fs::path const& filename);
 	~MkvStdIO() { if (fp) fclose(fp); }
diff --git a/src/placeholder_ctrl.h b/src/placeholder_ctrl.h
index 122d78aec1bd83d21539b067db4a1688cf80ff9c..34caa115c21451a5047bb0255b7d07b3e8088f8f 100644
--- a/src/placeholder_ctrl.h
+++ b/src/placeholder_ctrl.h
@@ -28,7 +28,7 @@
 /// is removed when the control is focused to begin typing in it, and restored
 /// when the control loses focus and the value is empty
 template<class BaseCtrl>
-class Placeholder : public BaseCtrl {
+class Placeholder final : public BaseCtrl {
 	wxString placeholder; ///< Placeholder string
 	bool is_placeholder;  ///< Should the value be cleared on focus?
 
diff --git a/src/preferences.cpp b/src/preferences.cpp
index 7768928c95d3a8a786a3c7a3913b13407baa3985..d7590ecf026319cfa3f36e245b394b9b1f0e86d2 100644
--- a/src/preferences.cpp
+++ b/src/preferences.cpp
@@ -77,7 +77,7 @@ CLASS_PAGE(Advanced)
 CLASS_PAGE(Advanced_Audio)
 CLASS_PAGE(Advanced_Video)
 
-class Interface_Hotkeys : public OptionPage {
+class Interface_Hotkeys final : public OptionPage {
 	wxDataViewCtrl *dvc;
 	wxObjectDataPtr<HotkeyDataViewModel> model;
 	wxSearchCtrl *quick_search;
@@ -263,7 +263,7 @@ Interface_Colours::Interface_Colours(wxTreebook *book, Preferences *parent): Opt
 }
 
 /// wxDataViewIconTextRenderer with command name autocompletion
-class CommandRenderer : public wxDataViewCustomRenderer {
+class CommandRenderer final : public wxDataViewCustomRenderer {
 	wxArrayString autocomplete;
 	wxDataViewIconText value;
 	static const int icon_width = 20;
@@ -327,7 +327,7 @@ public:
 	bool HasEditorCtrl() const override { return true; }
 };
 
-class HotkeyRenderer : public wxDataViewCustomRenderer {
+class HotkeyRenderer final : public wxDataViewCustomRenderer {
 	wxString value;
 	wxTextCtrl *ctrl;
 
diff --git a/src/preferences.h b/src/preferences.h
index 0bf1754edf968df9905730eeaef7d6c29c622f42..d8fb308c2ab3a23a365541bda74d9fe36c6dd45d 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -34,7 +34,7 @@ DEFINE_BASE_EXCEPTION_NOINNER(PreferencesError, agi::Exception)
 DEFINE_SIMPLE_EXCEPTION_NOINNER(PreferenceIncorrectType, PreferencesError, "preferences/incorrect_type")
 DEFINE_SIMPLE_EXCEPTION_NOINNER(PreferenceNotSupported, PreferencesError, "preferences/not_supported")
 
-class Preferences : public wxDialog {
+class Preferences final : public wxDialog {
 public:
 	typedef std::function<void ()> Thunk;
 private:
diff --git a/src/scintilla_text_selection_controller.h b/src/scintilla_text_selection_controller.h
index e040e1f11e60e3b7be0f7b698cdf4e38d44cbe35..d481dd0148041872556d1530e7f450b511e7f0b6 100644
--- a/src/scintilla_text_selection_controller.h
+++ b/src/scintilla_text_selection_controller.h
@@ -18,7 +18,7 @@
 
 class ScintillaTextCtrl;
 
-class ScintillaTextSelectionController : public TextSelectionController {
+class ScintillaTextSelectionController final : public TextSelectionController {
 	ScintillaTextCtrl *ctrl;
 
 public:
diff --git a/src/spellchecker_hunspell.h b/src/spellchecker_hunspell.h
index 4789cbc219cd65e08a57e5431b6c7987cc8689c9..1c204fd2f5e5e1190924aed34de9b60f1375065c 100644
--- a/src/spellchecker_hunspell.h
+++ b/src/spellchecker_hunspell.h
@@ -33,7 +33,7 @@ namespace agi { namespace charset { class IconvWrapper; } }
 class Hunspell;
 
 /// @brief Hunspell-based spell checker implementation
-class HunspellSpellChecker : public agi::SpellChecker {
+class HunspellSpellChecker final : public agi::SpellChecker {
 	/// Hunspell instance
 	std::unique_ptr<Hunspell> hunspell;
 
diff --git a/src/spline.h b/src/spline.h
index 9ce54de9d8811264ad52dbe41af140221017576e..55efb346bf55443a34a9cbdb31a0256c3a1f7086 100644
--- a/src/spline.h
+++ b/src/spline.h
@@ -39,7 +39,7 @@
 
 class VisualToolBase;
 
-class Spline : private std::list<SplineCurve> {
+class Spline final : private std::list<SplineCurve> {
 	/// Visual tool to do the conversion between script and video pixels
 	const VisualToolBase &coord_translator;
 	/// Spline scale
diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h
index 47b5661d5ae270eaf643585495353ff687a54eab..2415cf153d05a96e7f41b665a2c72606245d6c61 100644
--- a/src/subs_edit_box.h
+++ b/src/subs_edit_box.h
@@ -69,7 +69,7 @@ template<class Base> class Placeholder;
 /// @brief Main subtitle edit box
 ///
 /// Controls the text edit and all surrounding controls
-class SubsEditBox : public wxPanel {
+class SubsEditBox final : public wxPanel {
 	enum TimeField {
 		TIME_START = 0,
 		TIME_END,
diff --git a/src/subs_edit_ctrl.h b/src/subs_edit_ctrl.h
index 84b611f4db13ecdbf070106d906c07d626b0cf6c..4ce1734a07ea51c08489183a04c1149ecf2e8522 100644
--- a/src/subs_edit_ctrl.h
+++ b/src/subs_edit_ctrl.h
@@ -49,7 +49,7 @@ namespace agi {
 
 /// @class SubsTextEditCtrl
 /// @brief A Scintilla control with spell checking and syntax highlighting
-class SubsTextEditCtrl : public ScintillaTextCtrl {
+class SubsTextEditCtrl final : public ScintillaTextCtrl {
 	/// Backend spellchecker to use
 	std::unique_ptr<agi::SpellChecker> spellchecker;
 
diff --git a/src/subs_preview.h b/src/subs_preview.h
index a7ddda8ca83c42d9061aa85be5489175c0b1d704..3f3077e201fb5835a7fcf50b2a34f250bda2e378 100644
--- a/src/subs_preview.h
+++ b/src/subs_preview.h
@@ -42,7 +42,7 @@ class SubtitlesProvider;
 class VideoProvider;
 
 /// Preview window to show a short string with a given ass style
-class SubtitlesPreview : public wxWindow {
+class SubtitlesPreview final : public wxWindow {
 	/// The subtitle provider used to render the string
 	std::unique_ptr<SubtitlesProvider> provider;
 	/// Bitmap to render into
diff --git a/src/subtitle_format_ass.h b/src/subtitle_format_ass.h
index b3fbda8b9661741c59765970b54ee03345bbd5f1..536722fb4c4cf9dcb9ebc888e25107f17d736095 100644
--- a/src/subtitle_format_ass.h
+++ b/src/subtitle_format_ass.h
@@ -34,7 +34,7 @@
 
 #include "subtitle_format.h"
 
-class AssSubtitleFormat : public SubtitleFormat {
+class AssSubtitleFormat final : public SubtitleFormat {
 public:
 	AssSubtitleFormat();
 
diff --git a/src/subtitle_format_ebu3264.h b/src/subtitle_format_ebu3264.h
index 72b480e35f8ac78df410c4ffb03472df4ef32eb1..7cbfdb9df0518c4acb4b94624f31505a0a652666 100644
--- a/src/subtitle_format_ebu3264.h
+++ b/src/subtitle_format_ebu3264.h
@@ -24,7 +24,7 @@
 ///
 /// Based on specifications obtained at <http://tech.ebu.ch/docs/tech/tech3264.pdf>
 /// Work on support for this format was sponsored by Bandai.
-class Ebu3264SubtitleFormat : public SubtitleFormat {
+class Ebu3264SubtitleFormat final : public SubtitleFormat {
 public:
 	Ebu3264SubtitleFormat();
 	std::vector<std::string> GetWriteWildcards() const override;
diff --git a/src/subtitle_format_encore.h b/src/subtitle_format_encore.h
index 5d500838641eeaa04076a13763081b0e7ab06272..91e90609f0d2165a29396db94b37c2ae0c298d81 100644
--- a/src/subtitle_format_encore.h
+++ b/src/subtitle_format_encore.h
@@ -34,7 +34,7 @@
 
 #include "subtitle_format.h"
 
-class EncoreSubtitleFormat : public SubtitleFormat {
+class EncoreSubtitleFormat final : public SubtitleFormat {
 public:
 	EncoreSubtitleFormat();
 	std::vector<std::string> GetWriteWildcards() const override;
diff --git a/src/subtitle_format_microdvd.h b/src/subtitle_format_microdvd.h
index b8ad7c51fc2f70fc3d8d04b130e2d407f39d7709..a06d54db07da4fcb1c62b80f13c25f3c0d6b7c20 100644
--- a/src/subtitle_format_microdvd.h
+++ b/src/subtitle_format_microdvd.h
@@ -34,7 +34,7 @@
 
 #include "subtitle_format.h"
 
-class MicroDVDSubtitleFormat : public SubtitleFormat {
+class MicroDVDSubtitleFormat final : public SubtitleFormat {
 public:
 	MicroDVDSubtitleFormat();
 
diff --git a/src/subtitle_format_mkv.h b/src/subtitle_format_mkv.h
index 31a1630bdd608c2d72e5f292d2ff1f9c29bb1d54..8d58d35a244c7019610ce1f1f702f78f8539c24c 100644
--- a/src/subtitle_format_mkv.h
+++ b/src/subtitle_format_mkv.h
@@ -34,7 +34,7 @@
 
 #include "subtitle_format.h"
 
-class MKVSubtitleFormat : public SubtitleFormat {
+class MKVSubtitleFormat final : public SubtitleFormat {
 public:
 	MKVSubtitleFormat();
 	std::vector<std::string> GetReadWildcards() const override;
diff --git a/src/subtitle_format_srt.h b/src/subtitle_format_srt.h
index 70bb027eaec7cf63f8af4b3178cd621649f995ef..f9d90b2a79e39ad1b46e6b3fdca51f95b20c452a 100644
--- a/src/subtitle_format_srt.h
+++ b/src/subtitle_format_srt.h
@@ -36,7 +36,7 @@
 
 class AssDialogue;
 
-class SRTSubtitleFormat : public SubtitleFormat {
+class SRTSubtitleFormat final : public SubtitleFormat {
 	std::string ConvertTags(const AssDialogue *diag) const;
 public:
 	SRTSubtitleFormat();
diff --git a/src/subtitle_format_transtation.h b/src/subtitle_format_transtation.h
index 5740ce405b0bb652fcf7b7dd13c5385cffb6276c..831d6b2cd3cb47b43295e97f50f19c9403954645 100644
--- a/src/subtitle_format_transtation.h
+++ b/src/subtitle_format_transtation.h
@@ -37,7 +37,7 @@
 class AssDialogue;
 class SmpteFormatter;
 
-class TranStationSubtitleFormat : public SubtitleFormat {
+class TranStationSubtitleFormat final : public SubtitleFormat {
 	std::string ConvertLine(AssFile *file, const AssDialogue *line, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const;
 
 public:
diff --git a/src/subtitle_format_ttxt.h b/src/subtitle_format_ttxt.h
index 111a87b187e82d01f87bb639e2c64deaa8aeff54..2b1e29511ce8faa4536eee47d4b60142f4894da9 100644
--- a/src/subtitle_format_ttxt.h
+++ b/src/subtitle_format_ttxt.h
@@ -37,7 +37,7 @@
 class AssDialogue;
 class wxXmlNode;
 
-class TTXTSubtitleFormat : public SubtitleFormat {
+class TTXTSubtitleFormat final : public SubtitleFormat {
 	AssDialogue *ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const;
 	void ProcessHeader(wxXmlNode *node) const;
 
diff --git a/src/subtitle_format_txt.h b/src/subtitle_format_txt.h
index 0c6c103599d06a131b081255279f522f741958cf..bcab957930c9d4d8ac8a741ecadab6c0fa0c3353 100644
--- a/src/subtitle_format_txt.h
+++ b/src/subtitle_format_txt.h
@@ -34,7 +34,7 @@
 
 #include "subtitle_format.h"
 
-class TXTSubtitleFormat : public SubtitleFormat {
+class TXTSubtitleFormat final : public SubtitleFormat {
 public:
 	TXTSubtitleFormat();
 	std::vector<std::string> GetReadWildcards() const override;
diff --git a/src/subtitles_provider_csri.h b/src/subtitles_provider_csri.h
index e620f938501b46b998afdfb3781c437cd6614445..0052fed0928d9b0a7497432ab0242ba0c79715a7 100644
--- a/src/subtitles_provider_csri.h
+++ b/src/subtitles_provider_csri.h
@@ -45,7 +45,7 @@
 typedef void csri_rend;
 typedef void csri_inst;
 
-class CSRISubtitlesProvider : public SubtitlesProvider {
+class CSRISubtitlesProvider final : public SubtitlesProvider {
 	agi::scoped_holder<csri_inst*> instance;
 	csri_rend *renderer;
 
diff --git a/src/subtitles_provider_libass.h b/src/subtitles_provider_libass.h
index cbce7873db264a5d5dda10fbfed30b4e2bfffd6e..1c497c92cd2849b9d9471f29ee8043c8d43ae855 100644
--- a/src/subtitles_provider_libass.h
+++ b/src/subtitles_provider_libass.h
@@ -38,7 +38,7 @@ extern "C" {
 #include <ass/ass.h>
 }
 
-class LibassSubtitlesProvider : public SubtitlesProvider {
+class LibassSubtitlesProvider final : public SubtitlesProvider {
 	ASS_Renderer* ass_renderer = nullptr;
 	ASS_Track* ass_track = nullptr;
 
diff --git a/src/threaded_frame_source.h b/src/threaded_frame_source.h
index fbb08c914862b96fcd372d5f2b612d41d9f91b2a..f7f366833c62403e622db62fdac897596418a9a3 100644
--- a/src/threaded_frame_source.h
+++ b/src/threaded_frame_source.h
@@ -111,7 +111,7 @@ public:
 };
 
 /// Event which signals that a requested frame is ready
-struct FrameReadyEvent : public wxEvent {
+struct FrameReadyEvent final : public wxEvent {
 	/// Frame which is ready
 	std::shared_ptr<VideoFrame> frame;
 	/// Time which was used for subtitle rendering
@@ -123,14 +123,14 @@ struct FrameReadyEvent : public wxEvent {
 
 // These exceptions are wxEvents so that they can be passed directly back to
 // the parent thread as events
-struct VideoProviderErrorEvent : public wxEvent, public agi::Exception {
+struct VideoProviderErrorEvent final : public wxEvent, public agi::Exception {
 	const char * GetName() const override { return "video/error"; }
 	wxEvent *Clone() const override { return new VideoProviderErrorEvent(*this); };
 	agi::Exception *Copy() const override { return new VideoProviderErrorEvent(*this); };
 	VideoProviderErrorEvent(VideoProviderError const& err);
 };
 
-struct SubtitlesProviderErrorEvent : public wxEvent, public agi::Exception {
+struct SubtitlesProviderErrorEvent final : public wxEvent, public agi::Exception {
 	const char * GetName() const override { return "subtitles/error"; }
 	wxEvent *Clone() const override { return new SubtitlesProviderErrorEvent(*this); };
 	agi::Exception *Copy() const override { return new SubtitlesProviderErrorEvent(*this); };
diff --git a/src/timeedit_ctrl.h b/src/timeedit_ctrl.h
index 9347c85041878affe4b35db6f092e97f88c32869..92527022380b6142e35641e0a4fcc7c233b79305 100644
--- a/src/timeedit_ctrl.h
+++ b/src/timeedit_ctrl.h
@@ -47,7 +47,7 @@ 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 : public wxTextCtrl {
+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?
diff --git a/src/toggle_bitmap.h b/src/toggle_bitmap.h
index d14d263893ae459756f1b425536b0dfe7439b489..fec8fa31638b994a4ee07a47f56b0a48f8e83701 100644
--- a/src/toggle_bitmap.h
+++ b/src/toggle_bitmap.h
@@ -38,7 +38,7 @@
 namespace agi { struct Context; }
 namespace cmd { class Command; }
 
-class ToggleBitmap : public wxControl {
+class ToggleBitmap final : public wxControl {
 	agi::Context *context;
 	cmd::Command &command;
 	wxBitmap img;
diff --git a/src/toolbar.cpp b/src/toolbar.cpp
index d1570b6bdf3ec8c3923d19384a8c9219dfda2622..b57ceebd7b2696107e91a24157cd9398fc30ee57 100644
--- a/src/toolbar.cpp
+++ b/src/toolbar.cpp
@@ -50,7 +50,7 @@ namespace {
 		return root;
 	}
 
-	class Toolbar : public wxToolBar {
+	class Toolbar final : public wxToolBar {
 		/// Window ID of first toolbar control
 		static const int TOOL_ID_BASE = 5000;
 
diff --git a/src/validators.h b/src/validators.h
index d4c5bd121e8f5eb0e7f251cdc4cf3b31f89a91c3..44b6ffd27a7179adca7e3e793c70537d640e86d2 100644
--- a/src/validators.h
+++ b/src/validators.h
@@ -21,7 +21,7 @@
 #include <wx/radiobox.h>
 #include <wx/validate.h>
 
-class IntValidator : public wxValidator {
+class IntValidator final : public wxValidator {
 	int value;
 	bool allow_negative;
 
@@ -39,7 +39,7 @@ public:
 	explicit IntValidator(int val=0, bool allow_negative=false);
 };
 
-class DoubleValidator : public wxValidator {
+class DoubleValidator final : public wxValidator {
 	double *value;
 	double min;
 	double max;
@@ -61,7 +61,7 @@ public:
 };
 
 template<typename T>
-class EnumBinder : public wxValidator {
+class EnumBinder final : public wxValidator {
 	T *value;
 
 	wxObject *Clone() const override { return new EnumBinder<T>(value); }
@@ -92,7 +92,7 @@ EnumBinder<T> MakeEnumBinder(T *value) {
 	return EnumBinder<T>(value);
 }
 
-class StringBinder : public wxValidator {
+class StringBinder final : public wxValidator {
 	std::string *value;
 
 	wxObject* Clone() const override { return new StringBinder(value); }
diff --git a/src/video_box.h b/src/video_box.h
index 175e36e23bed3bb3a8d716bf29044a5cedded0fc..29815ddfd79c3d6ffd067e3ba5f23bba31ff93a6 100644
--- a/src/video_box.h
+++ b/src/video_box.h
@@ -45,7 +45,7 @@ class wxTextCtrl;
 
 /// @class VideoBox
 /// @brief The box containing the video display and associated controls
-class VideoBox : public wxPanel {
+class VideoBox final : public wxPanel {
 	std::deque<agi::signal::Connection> slots;
 	agi::Context *context;     ///< Project context
 	wxTextCtrl *VideoPosition; ///< Current frame/time
diff --git a/src/video_context.h b/src/video_context.h
index 3c1a8cb07507d19c9bf3e9e231de8c6cb276f742..f5efbfdf3a6b088463962ae10aa5c5ce67dae2b8 100644
--- a/src/video_context.h
+++ b/src/video_context.h
@@ -71,7 +71,7 @@ enum class AspectRatio {
 /// VideoContext's core responsibility is opening and playing videos. Along
 /// with that, it also manages video timecodes and keyframes, and some
 /// video-related UI properties
-class VideoContext : public wxEvtHandler {
+class VideoContext final : public wxEvtHandler {
 	/// Current frame number changed (new frame number)
 	agi::signal::Signal<int> Seek;
 	/// A new video was opened
diff --git a/src/video_display.cpp b/src/video_display.cpp
index a076a190351efdbd6d761a3e6c5bc4c8570e4c18..0dd2a94dd92c13333028b6fa1b0ed215c6e17132 100644
--- a/src/video_display.cpp
+++ b/src/video_display.cpp
@@ -76,7 +76,7 @@ int attribList[] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 }
 /// @class VideoOutRenderException
 /// @extends VideoOutException
 /// @brief An OpenGL error occurred while uploading or displaying a frame
-class OpenGlException : public agi::Exception {
+class OpenGlException final : public agi::Exception {
 public:
 	OpenGlException(const char *func, int err)
 	: agi::Exception(from_wx(wxString::Format("%s failed with error code %d", func, err)))
diff --git a/src/video_display.h b/src/video_display.h
index 46cdd6952cfc1da62c7e90bf1b85261eb53eac25..04b30444cd4391e81a36c7ea5dbbbc03d5640d32 100644
--- a/src/video_display.h
+++ b/src/video_display.h
@@ -57,7 +57,7 @@ namespace agi {
 	class OptionValue;
 }
 
-class VideoDisplay : public wxGLCanvas {
+class VideoDisplay final : public wxGLCanvas {
 	/// Signals the display is connected to
 	std::deque<agi::signal::Connection> slots;
 
diff --git a/src/video_out_gl.h b/src/video_out_gl.h
index be690eae17420648fc209d4a6b09e72be7a9e113..885160050a061ff19f10031ce4ac11236eb0eff5 100644
--- a/src/video_out_gl.h
+++ b/src/video_out_gl.h
@@ -89,7 +89,7 @@ DEFINE_BASE_EXCEPTION_NOINNER(VideoOutException, agi::Exception)
 /// @class VideoOutRenderException
 /// @extends VideoOutException
 /// @brief An OpenGL error occurred while uploading or displaying a frame
-class VideoOutRenderException : public VideoOutException {
+class VideoOutRenderException final : public VideoOutException {
 public:
 	VideoOutRenderException(const char *func, int err)
 	: VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
@@ -100,7 +100,7 @@ public:
 /// @class VideoOutOpenGLException
 /// @extends VideoOutException
 /// @brief An OpenGL error occurred while setting up the video display
-class VideoOutInitException : public VideoOutException {
+class VideoOutInitException final : public VideoOutException {
 public:
 	VideoOutInitException(const char *func, int err)
 	: VideoOutException(std::string(func) + " failed with error code " + std::to_string(err))
diff --git a/src/video_provider_cache.cpp b/src/video_provider_cache.cpp
index 5779b4a2d0f15f2282ced927f88197a5aadc831e..a56e552fd1c89de3f44ce434d3d5f85448fc8ad4 100644
--- a/src/video_provider_cache.cpp
+++ b/src/video_provider_cache.cpp
@@ -36,7 +36,7 @@ static bool operator==(VideoFrame const& a, VideoFrame const& b) {
 #endif
 
 /// A video frame and its frame number
-struct CachedFrame : public VideoFrame {
+struct CachedFrame final : public VideoFrame {
 	int frame_number;
 
 	CachedFrame(int frame_number, VideoFrame const& frame)
diff --git a/src/video_provider_cache.h b/src/video_provider_cache.h
index ad56fccebdac8b95d453de0ab84ae70b737109de..8fe033087fb66c125cc47a9363a792172dd68b86 100644
--- a/src/video_provider_cache.h
+++ b/src/video_provider_cache.h
@@ -22,7 +22,7 @@ struct CachedFrame;
 
 /// @class VideoProviderCache
 /// @brief A wrapper around a video provider which provides LRU caching
-class VideoProviderCache : public VideoProvider {
+class VideoProviderCache final : public VideoProvider {
 	/// The source provider to get frames from
 	std::unique_ptr<VideoProvider> master;
 
diff --git a/src/video_provider_dummy.h b/src/video_provider_dummy.h
index 0e265f8599c856e62937c6b72901d7c8943e723a..97d31e602b8f25279a850a66fa299a8101f20804 100644
--- a/src/video_provider_dummy.h
+++ b/src/video_provider_dummy.h
@@ -41,7 +41,7 @@ namespace agi { struct Color; }
 ///
 /// This simply returns a single constant frame, which can either be a flat
 /// color or a checkerboard pattern
-class DummyVideoProvider : public VideoProvider {
+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
diff --git a/src/video_provider_ffmpegsource.h b/src/video_provider_ffmpegsource.h
index f9793fbec999fc215c12c20ebc6147b067ba011a..76139b47338b9678e71ed9308210b9f0a932269b 100644
--- a/src/video_provider_ffmpegsource.h
+++ b/src/video_provider_ffmpegsource.h
@@ -38,7 +38,7 @@
 
 /// @class FFmpegSourceVideoProvider
 /// @brief Implements video loading through the FFMS library.
-class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
+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
diff --git a/src/video_provider_manager.h b/src/video_provider_manager.h
index 595dac07c4ee18007d137dd2129fc6380de8bccb..6632f75ef14daf652aae2f15e271358874710e1f 100644
--- a/src/video_provider_manager.h
+++ b/src/video_provider_manager.h
@@ -19,7 +19,7 @@
 
 #include <libaegisub/fs_fwd.h>
 
-class VideoProviderFactory : public Factory<VideoProvider, agi::fs::path, std::string> {
+class VideoProviderFactory final : public Factory<VideoProvider, agi::fs::path, std::string> {
 public:
 	static std::unique_ptr<VideoProvider> GetProvider(agi::fs::path const& video_file, std::string const& colormatrix);
 	static void RegisterProviders();
diff --git a/src/video_provider_yuv4mpeg.h b/src/video_provider_yuv4mpeg.h
index d6017085392332d641e2b6d9e51ac06121d19a45..3c8c9e250a967c6b3c8eaae9050f1586e5ea5fc3 100644
--- a/src/video_provider_yuv4mpeg.h
+++ b/src/video_provider_yuv4mpeg.h
@@ -42,7 +42,7 @@
 
 /// @class YUV4MPEGVideoProvider
 /// @brief Implements reading of YUV4MPEG uncompressed video files
-class YUV4MPEGVideoProvider : public VideoProvider {
+class YUV4MPEGVideoProvider final : public VideoProvider {
 	/// Pixel formats
 	enum Y4M_PixelFormat {
 		Y4M_PIXFMT_NONE		= -1,	/// not set/unknown
diff --git a/src/visual_tool.h b/src/visual_tool.h
index f5122e555536ef276add2c3f814febb05fca10ea..99f4266fc5c7540f65e3c72070f0fe49b829c9b8 100644
--- a/src/visual_tool.h
+++ b/src/visual_tool.h
@@ -148,7 +148,7 @@ public:
 
 /// Visual tool base class containing all common feature-related functionality
 template<class FeatureType>
-class VisualTool : public VisualToolBase {
+class VisualTool final : public VisualToolBase {
 protected:
 	typedef FeatureType Feature;
 	typedef agi::owning_intrusive_list<FeatureType> feature_list;
diff --git a/src/visual_tool_clip.h b/src/visual_tool_clip.h
index 651657eacf00c601b8643a5243d95c6181319ff9..409d375ef0142f6ba9d0646e00f90c61f8bd7839 100644
--- a/src/visual_tool_clip.h
+++ b/src/visual_tool_clip.h
@@ -23,13 +23,13 @@
 #include "visual_tool.h"
 
 /// VisualDraggableFeature with siblings
-struct ClipCorner : public VisualDraggableFeature {
+struct ClipCorner final : public VisualDraggableFeature {
 	ClipCorner *horiz; ///< Other corner on this corner's horizontal line
 	ClipCorner *vert;  ///< Other corner on this corner's vertical line
 	ClipCorner() : VisualDraggableFeature() , horiz(nullptr) , vert(nullptr) { type = DRAG_SMALL_CIRCLE; }
 };
 
-class VisualToolClip : public VisualTool<ClipCorner> {
+class VisualToolClip final : public VisualTool<ClipCorner> {
 	Vector2D cur_1;
 	Vector2D cur_2;
 
diff --git a/src/visual_tool_cross.h b/src/visual_tool_cross.h
index 0657d6e37c827368f3bf366653b5436bc422b657..637532c6697b4dae43f6d8ba92c68c5f69d3739e 100644
--- a/src/visual_tool_cross.h
+++ b/src/visual_tool_cross.h
@@ -29,7 +29,7 @@ class OpenGLText;
 /// @class VisualToolCross
 /// @brief A crosshair which shows the current mouse position and on double-click
 ///        shifts the selected lines to the clicked point
-class VisualToolCross : public VisualTool<VisualDraggableFeature> {
+class VisualToolCross final : public VisualTool<VisualDraggableFeature> {
 	std::unique_ptr<OpenGLText> gl_text;
 
 	void OnDoubleClick() override;
diff --git a/src/visual_tool_drag.h b/src/visual_tool_drag.h
index a8cfe8af9a846d500e18f73d98a334ceba6b5c6a..8aa14a36278c921a2e0296a225312093f25432d6 100644
--- a/src/visual_tool_drag.h
+++ b/src/visual_tool_drag.h
@@ -24,7 +24,7 @@
 
 /// @class VisualToolDragDraggableFeature
 /// @brief VisualDraggableFeature with a time value
-class VisualToolDragDraggableFeature : public VisualDraggableFeature {
+class VisualToolDragDraggableFeature final : public VisualDraggableFeature {
 public:
 	int time;
 	VisualToolDragDraggableFeature *parent;
@@ -36,7 +36,7 @@ class wxToolBar;
 
 /// @class VisualToolDrag
 /// @brief Moveable features for the positions of each visible line
-class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> {
+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
diff --git a/src/visual_tool_rotatexy.h b/src/visual_tool_rotatexy.h
index 36be33120e7d463589dc0f120441e6b94d389563..a4c00a3732c6a60ac40372302124a916c317a808 100644
--- a/src/visual_tool_rotatexy.h
+++ b/src/visual_tool_rotatexy.h
@@ -22,7 +22,7 @@
 #include "visual_feature.h"
 #include "visual_tool.h"
 
-class VisualToolRotateXY : public VisualTool<VisualDraggableFeature> {
+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
diff --git a/src/visual_tool_rotatez.h b/src/visual_tool_rotatez.h
index b1083947cfaa96756afdc7d32b5e262cb67fa873..a7bb08cbf4e1c5cf710930c319b4960561910bea 100644
--- a/src/visual_tool_rotatez.h
+++ b/src/visual_tool_rotatez.h
@@ -22,7 +22,7 @@
 #include "visual_feature.h"
 #include "visual_tool.h"
 
-class VisualToolRotateZ : public VisualTool<VisualDraggableFeature> {
+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
diff --git a/src/visual_tool_scale.h b/src/visual_tool_scale.h
index 623f73ce4c22192c8e8600205c7eeddbebc02f21..27a6557f373647c874da183dba8edd6722ae1912 100644
--- a/src/visual_tool_scale.h
+++ b/src/visual_tool_scale.h
@@ -22,7 +22,7 @@
 #include "visual_feature.h"
 #include "visual_tool.h"
 
-class VisualToolScale : public VisualTool<VisualDraggableFeature> {
+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
diff --git a/src/visual_tool_vector_clip.h b/src/visual_tool_vector_clip.h
index 761a262197a0007588c41447207ae1640f922dc2..703363dd53b3be98cdd1b5cea4dfd43e3c5e4143 100644
--- a/src/visual_tool_vector_clip.h
+++ b/src/visual_tool_vector_clip.h
@@ -28,7 +28,7 @@ class wxToolBar;
 /// @class VisualToolVectorClipDraggableFeature
 /// @brief VisualDraggableFeature with information about a feature's location
 ///        in the spline
-struct VisualToolVectorClipDraggableFeature : public VisualDraggableFeature {
+struct VisualToolVectorClipDraggableFeature final : public VisualDraggableFeature {
 	/// Which curve in the spline this feature is a point on
 	Spline::iterator curve;
 	/// 0-3; indicates which part of the curve this point is
@@ -40,7 +40,7 @@ struct VisualToolVectorClipDraggableFeature : public VisualDraggableFeature {
 	{ }
 };
 
-class VisualToolVectorClip : public VisualTool<VisualToolVectorClipDraggableFeature> {
+class VisualToolVectorClip final : public VisualTool<VisualToolVectorClipDraggableFeature> {
 	Spline spline; /// The current spline
 	wxToolBar *toolBar = nullptr; /// The subtoolbar
 	int mode = 0; /// 0-7