From 3b34ed9a77e07072e67569af98280d031e3aa489 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sun, 6 Jul 2014 07:28:58 -0700 Subject: [PATCH] Move AssTime to libaegisub and add tests --- build/Aegisub/Aegisub.vcxproj | 2 - build/Aegisub/Aegisub.vcxproj.filters | 6 -- build/libaegisub/libaegisub.vcxproj | 3 + build/libaegisub/libaegisub.vcxproj.filters | 9 ++ build/tests/tests.vcxproj | 1 + build/tests/tests.vcxproj.filters | 3 + libaegisub/Makefile | 1 + src/ass_time.cpp => libaegisub/ass/time.cpp | 48 +++++------ libaegisub/include/libaegisub/ass/smpte.h | 40 +++++++++ .../include/libaegisub/ass/time.h | 36 ++------ src/Makefile | 1 - src/ass_dialogue.cpp | 4 +- src/ass_dialogue.h | 12 +-- src/ass_override.cpp | 6 +- src/audio_display.cpp | 6 +- src/audio_timing_dialogue.cpp | 2 +- src/command/audio.cpp | 2 +- src/command/video.cpp | 2 +- src/dialog_dummy_video.cpp | 4 +- src/dialog_jumpto.cpp | 5 +- src/dialog_shift_times.cpp | 4 +- src/dialog_style_manager.cpp | 3 +- src/dialog_timing_processor.cpp | 2 +- src/dialog_video_details.cpp | 5 +- src/grid_column.cpp | 4 +- src/mkv_wrap.cpp | 14 ++-- src/resolution_resampler.cpp | 1 + src/search_replace_engine.cpp | 1 + src/subs_edit_box.h | 4 +- src/subtitle_format.cpp | 1 + src/subtitle_format_encore.cpp | 3 +- src/subtitle_format_microdvd.cpp | 3 +- src/subtitle_format_srt.cpp | 2 +- src/subtitle_format_ssa.cpp | 2 +- src/subtitle_format_transtation.cpp | 16 ++-- src/subtitle_format_transtation.h | 9 +- src/subtitle_format_ttxt.cpp | 11 +-- src/subtitles_provider_libass.cpp | 1 + src/timeedit_ctrl.cpp | 18 ++-- src/timeedit_ctrl.h | 20 ++--- src/video_box.cpp | 2 +- src/video_controller.cpp | 3 +- src/visual_tool.cpp | 2 +- tests/tests/time.cpp | 84 +++++++++++++++++++ 44 files changed, 251 insertions(+), 157 deletions(-) rename src/ass_time.cpp => libaegisub/ass/time.cpp (66%) create mode 100644 libaegisub/include/libaegisub/ass/smpte.h rename src/ass_time.h => libaegisub/include/libaegisub/ass/time.h (66%) create mode 100644 tests/tests/time.cpp diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj index 7a8fce47f..69909ccaa 100644 --- a/build/Aegisub/Aegisub.vcxproj +++ b/build/Aegisub/Aegisub.vcxproj @@ -117,7 +117,6 @@ - @@ -257,7 +256,6 @@ - diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters index 1ea6273f1..9e8c86650 100644 --- a/build/Aegisub/Aegisub.vcxproj.filters +++ b/build/Aegisub/Aegisub.vcxproj.filters @@ -150,9 +150,6 @@ - - ASS - ASS @@ -578,9 +575,6 @@ ASS - - ASS - Audio\Providers diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj index a5e51e28b..d46c88919 100644 --- a/build/libaegisub/libaegisub.vcxproj +++ b/build/libaegisub/libaegisub.vcxproj @@ -61,6 +61,8 @@ + + @@ -118,6 +120,7 @@ lagi_pre.h + diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters index a9dfeba9c..5dee77b43 100644 --- a/build/libaegisub/libaegisub.vcxproj.filters +++ b/build/libaegisub/libaegisub.vcxproj.filters @@ -125,6 +125,12 @@ ASS + + ASS + + + ASS + Header Files @@ -265,6 +271,9 @@ ASS + + ASS + Source Files\Common diff --git a/build/tests/tests.vcxproj b/build/tests/tests.vcxproj index 30895e7ac..7a60299d0 100644 --- a/build/tests/tests.vcxproj +++ b/build/tests/tests.vcxproj @@ -57,6 +57,7 @@ + diff --git a/build/tests/tests.vcxproj.filters b/build/tests/tests.vcxproj.filters index b5a4d942c..c25a592f9 100644 --- a/build/tests/tests.vcxproj.filters +++ b/build/tests/tests.vcxproj.filters @@ -65,6 +65,9 @@ Tests + + Tests + Tests diff --git a/libaegisub/Makefile b/libaegisub/Makefile index 52c55290c..1492a6f79 100644 --- a/libaegisub/Makefile +++ b/libaegisub/Makefile @@ -3,6 +3,7 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../header.mk aegisub_OBJ := \ $(d)common/parser.o \ $(d)ass/dialogue_parser.o \ + $(d)ass/time.o \ $(subst .cpp,.o,$(wildcard $(d)common/cajun/*.cpp)) \ $(subst .cpp,.o,$(wildcard $(d)lua/modules/*.cpp)) \ $(subst .c,.o,$(wildcard $(d)lua/modules/*.c)) \ diff --git a/src/ass_time.cpp b/libaegisub/ass/time.cpp similarity index 66% rename from src/ass_time.cpp rename to libaegisub/ass/time.cpp index 585fa7cf7..0d05cd4c8 100644 --- a/src/ass_time.cpp +++ b/libaegisub/ass/time.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2013, Thomas Goyne +// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -14,14 +14,8 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file ass_time.cpp -/// @brief Class for managing timestamps in subtitles -/// @ingroup subs_storage -/// - -#include "ass_time.h" - -#include "utils.h" +#include +#include #include #include @@ -31,9 +25,10 @@ #include #include -AssTime::AssTime(int time) : time(mid(0, time, 10 * 60 * 60 * 1000 - 1)) { } +namespace agi { +Time::Time(int time) : time(util::mid(0, time, 10 * 60 * 60 * 1000 - 1)) { } -AssTime::AssTime(std::string const& text) { +Time::Time(std::string const& text) { int after_decimal = -1; int current = 0; for (char c : text | boost::adaptors::filtered(boost::is_any_of(",.0123456789:"))) { @@ -61,10 +56,10 @@ AssTime::AssTime(std::string const& text) { time = (time * 60 + current) * 1000; // Limit to the valid range - time = mid(0, time, 10 * 60 * 60 * 1000 - 1); + time = util::mid(0, time, 10 * 60 * 60 * 1000 - 1); } -std::string AssTime::GetAssFormated(bool msPrecision) const { +std::string Time::GetAssFormatted(bool msPrecision) const { std::string ret(10 + msPrecision, ':'); ret[0] = '0' + GetTimeHours(); ret[2] = '0' + (time % (60 * 60 * 1000)) / (60 * 1000 * 10); @@ -79,33 +74,34 @@ std::string AssTime::GetAssFormated(bool msPrecision) const { return ret; } -int AssTime::GetTimeHours() const { return time / 3600000; } -int AssTime::GetTimeMinutes() const { return (time % 3600000) / 60000; } -int AssTime::GetTimeSeconds() const { return (time % 60000) / 1000; } -int AssTime::GetTimeMiliseconds() const { return (time % 1000); } -int AssTime::GetTimeCentiseconds() const { return (time % 1000) / 10; } +int Time::GetTimeHours() const { return time / 3600000; } +int Time::GetTimeMinutes() const { return (time % 3600000) / 60000; } +int Time::GetTimeSeconds() const { return (time % 60000) / 1000; } +int Time::GetTimeMiliseconds() const { return (time % 1000); } +int Time::GetTimeCentiseconds() const { return (time % 1000) / 10; } -SmpteFormatter::SmpteFormatter(agi::vfr::Framerate fps, std::string sep) +SmpteFormatter::SmpteFormatter(vfr::Framerate fps, std::string sep) : fps(std::move(fps)) , sep(std::move(sep)) { } -std::string SmpteFormatter::ToSMPTE(AssTime time) const { +std::string SmpteFormatter::ToSMPTE(Time time) const { int h=0, m=0, s=0, f=0; fps.SmpteAtTime(time, &h, &m, &s, &f); - return agi::format("%02d%s%02d%s%02d%c%02d", h, sep, m, sep, s, sep, f); + return format("%02d%s%02d%s%02d%s%02d", h, sep, m, sep, s, sep, f); } -AssTime SmpteFormatter::FromSMPTE(std::string const& str) const { +Time SmpteFormatter::FromSMPTE(std::string const& str) const { std::vector toks; boost::split(toks, str, boost::is_any_of(sep)); if (toks.size() != 4) return 0; int h, m, s, f; - agi::util::try_parse(toks[0], &h); - agi::util::try_parse(toks[1], &m); - agi::util::try_parse(toks[2], &s); - agi::util::try_parse(toks[3], &f); + util::try_parse(toks[0], &h); + util::try_parse(toks[1], &m); + util::try_parse(toks[2], &s); + util::try_parse(toks[3], &f); return fps.TimeAtSmpte(h, m, s, f); } +} diff --git a/libaegisub/include/libaegisub/ass/smpte.h b/libaegisub/include/libaegisub/ass/smpte.h new file mode 100644 index 000000000..c6bed8034 --- /dev/null +++ b/libaegisub/include/libaegisub/ass/smpte.h @@ -0,0 +1,40 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include + +#include + +namespace agi { +class Time; + +/// @class SmpteFormatter +/// @brief Convert times to and from SMPTE timecodes +class SmpteFormatter { + /// Frame rate to use + vfr::Framerate fps; + /// Separator character + std::string sep; + +public: + SmpteFormatter(vfr::Framerate fps, std::string sep=":"); + + /// Convert an Time to a SMPTE timecode + std::string ToSMPTE(Time time) const; + /// Convert a SMPTE timecode to an Time + Time FromSMPTE(std::string const& str) const; +}; +} diff --git a/src/ass_time.h b/libaegisub/include/libaegisub/ass/time.h similarity index 66% rename from src/ass_time.h rename to libaegisub/include/libaegisub/ass/time.h index 2e04a7f66..018b5b94a 100644 --- a/src/ass_time.h +++ b/libaegisub/include/libaegisub/ass/time.h @@ -1,4 +1,4 @@ -// Copyright (c) 2013, Thomas Goyne +// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -14,24 +14,18 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file ass_time.h -/// @see ass_time.cpp -/// @ingroup subs_storage -/// - #pragma once #include -#include - -class AssTime { +namespace agi { +class Time { /// Time in milliseconds int time = 0; public: - AssTime(int ms = 0); - AssTime(std::string const& text); + Time(int ms = 0); + Time(std::string const& text); /// Get millisecond, rounded to centisecond precision operator int() const { return time / 10 * 10; } @@ -44,22 +38,6 @@ public: /// Return the time as a string /// @param ms Use milliseconds precision, for non-ASS formats - std::string GetAssFormated(bool ms=false) const; -}; - -/// @class SmpteFormatter -/// @brief Convert times to and from SMPTE timecodes -class SmpteFormatter { - /// Frame rate to use - agi::vfr::Framerate fps; - /// Separator character - std::string sep; - -public: - SmpteFormatter(agi::vfr::Framerate fps, std::string sep=":"); - - /// Convert an AssTime to a SMPTE timecode - std::string ToSMPTE(AssTime time) const; - /// Convert a SMPTE timecode to an AssTime - AssTime FromSMPTE(std::string const& str) const; + std::string GetAssFormatted(bool ms=false) const; }; +} diff --git a/src/Makefile b/src/Makefile index 7914944c9..76bf2127d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -28,7 +28,6 @@ src_OBJ := \ $(d)ass_parser.o \ $(d)ass_style.o \ $(d)ass_style_storage.o \ - $(d)ass_time.o \ $(d)async_video_provider.o \ $(d)audio_box.o \ $(d)audio_colorscheme.o \ diff --git a/src/ass_dialogue.cpp b/src/ass_dialogue.cpp index 80fe5618d..40d2599fa 100644 --- a/src/ass_dialogue.cpp +++ b/src/ass_dialogue.cpp @@ -168,8 +168,8 @@ std::string AssDialogue::GetEntryData() const { str.reserve(51 + Style.get().size() + Actor.get().size() + Effect.get().size() + Text.get().size()); append_int(str, Layer); - append_str(str, Start.GetAssFormated()); - append_str(str, End.GetAssFormated()); + append_str(str, Start.GetAssFormatted()); + append_str(str, End.GetAssFormatted()); append_unsafe_str(str, Style); append_unsafe_str(str, Actor); for (auto margin : Margin) diff --git a/src/ass_dialogue.h b/src/ass_dialogue.h index a008b1516..e47466ed1 100644 --- a/src/ass_dialogue.h +++ b/src/ass_dialogue.h @@ -27,14 +27,10 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file ass_dialogue.h -/// @see ass_dialogue.cpp -/// @ingroup subs_storage -/// - #include "ass_entry.h" #include "ass_override.h" -#include "ass_time.h" + +#include #include #include @@ -135,9 +131,9 @@ struct AssDialogueBase { /// Margins: 0 = Left, 1 = Right, 2 = Top (Vertical) std::array Margin = {{0, 0, 0}}; /// Starting time - AssTime Start = 0; + agi::Time Start = 0; /// Ending time - AssTime End = 5000; + agi::Time End = 5000; /// Style name boost::flyweight Style{ "Default" }; /// Actor name diff --git a/src/ass_override.cpp b/src/ass_override.cpp index ad11d0728..94cc5ccf3 100644 --- a/src/ass_override.cpp +++ b/src/ass_override.cpp @@ -28,16 +28,12 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file ass_override.cpp -/// @brief Parse and modify ASSA style overrides -/// @ingroup subs_storage -/// - #include "ass_dialogue.h" #include "utils.h" #include +#include #include #include diff --git a/src/audio_display.cpp b/src/audio_display.cpp index 5c9135143..dfb6cb9ad 100644 --- a/src/audio_display.cpp +++ b/src/audio_display.cpp @@ -30,7 +30,6 @@ #include "audio_display.h" -#include "ass_time.h" #include "audio_controller.h" #include "audio_renderer.h" #include "audio_renderer_spectrum.h" @@ -46,6 +45,7 @@ #include "utils.h" #include "video_controller.h" +#include #include #include @@ -977,8 +977,8 @@ void AudioDisplay::SetTrackCursor(int new_pos, bool show_time) if (show_time) { - AssTime new_label_time = TimeFromAbsoluteX(track_cursor_pos); - track_cursor_label = to_wx(new_label_time.GetAssFormated()); + agi::Time new_label_time = TimeFromAbsoluteX(track_cursor_pos); + track_cursor_label = to_wx(new_label_time.GetAssFormatted()); track_cursor_label_rect.x += new_pos - old_pos; RefreshRect(track_cursor_label_rect, false); } diff --git a/src/audio_timing_dialogue.cpp b/src/audio_timing_dialogue.cpp index 6a1bf2197..c211e8e92 100644 --- a/src/audio_timing_dialogue.cpp +++ b/src/audio_timing_dialogue.cpp @@ -29,7 +29,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "audio_marker.h" #include "audio_rendering_style.h" #include "audio_timing.h" @@ -40,6 +39,7 @@ #include "selection_controller.h" #include "utils.h" +#include #include #include diff --git a/src/command/audio.cpp b/src/command/audio.cpp index 2f8bbe173..cc5e2f7bd 100644 --- a/src/command/audio.cpp +++ b/src/command/audio.cpp @@ -200,7 +200,7 @@ struct audio_save_clip final : public Command { auto filename = SaveFileSelector(_("Save audio clip"), "", "", "wav", "", c->parent); if (filename.empty()) return; - AssTime start = INT_MAX, end = 0; + agi::Time start = INT_MAX, end = 0; for (auto line : sel) { start = std::min(start, line->Start); end = std::max(end, line->End); diff --git a/src/command/video.cpp b/src/command/video.cpp index 04ec49b5c..a7600176d 100644 --- a/src/command/video.cpp +++ b/src/command/video.cpp @@ -32,7 +32,6 @@ #include "command.h" #include "../ass_dialogue.h" -#include "../ass_time.h" #include "../async_video_provider.h" #include "../compat.h" #include "../dialog_detached_video.h" @@ -51,6 +50,7 @@ #include "../video_display.h" #include "../video_frame.h" +#include #include #include #include diff --git a/src/dialog_dummy_video.cpp b/src/dialog_dummy_video.cpp index 9338a54a9..90023864b 100644 --- a/src/dialog_dummy_video.cpp +++ b/src/dialog_dummy_video.cpp @@ -14,7 +14,6 @@ // // Aegisub Project http://www.aegisub.org/ -#include "ass_time.h" #include "colour_button.h" #include "format.h" #include "help_button.h" @@ -23,6 +22,7 @@ #include "validators.h" #include "video_provider_dummy.h" +#include #include #include @@ -161,7 +161,7 @@ void DialogDummyVideo::OnResolutionShortcut(wxCommandEvent &e) { } void DialogDummyVideo::UpdateLengthDisplay() { - length_display->SetLabel(fmt_tl("Resulting duration: %s", AssTime(length / fps * 1000).GetAssFormated(true))); + length_display->SetLabel(fmt_tl("Resulting duration: %s", agi::Time(length / fps * 1000).GetAssFormatted(true))); } } diff --git a/src/dialog_jumpto.cpp b/src/dialog_jumpto.cpp index 4f8e7bcf1..8503240f8 100644 --- a/src/dialog_jumpto.cpp +++ b/src/dialog_jumpto.cpp @@ -27,7 +27,6 @@ // // Aegisub Project http://www.aegisub.org/ -#include "ass_time.h" #include "async_video_provider.h" #include "format.h" #include "include/aegisub/context.h" @@ -37,6 +36,8 @@ #include "validators.h" #include "video_controller.h" +#include + #include #include #include @@ -74,7 +75,7 @@ DialogJumpTo::DialogJumpTo(agi::Context *c) JumpFrame = new wxTextCtrl(&d,-1,"",wxDefaultPosition,wxSize(-1,-1),wxTE_PROCESS_ENTER, IntValidator((int)jumpframe)); JumpFrame->SetMaxLength(std::to_string(c->project->VideoProvider()->GetFrameCount() - 1).size()); - JumpTime = new TimeEdit(&d, -1, c, AssTime(c->videoController->TimeAtFrame(jumpframe)).GetAssFormated(), wxSize(-1,-1)); + JumpTime = new TimeEdit(&d, -1, c, agi::Time(c->videoController->TimeAtFrame(jumpframe)).GetAssFormatted(), wxSize(-1,-1)); auto TimesSizer = new wxGridSizer(2, 5, 5); diff --git a/src/dialog_shift_times.cpp b/src/dialog_shift_times.cpp index c30c86f6a..ae0bf242a 100644 --- a/src/dialog_shift_times.cpp +++ b/src/dialog_shift_times.cpp @@ -16,7 +16,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "compat.h" #include "dialog_manager.h" #include "format.h" @@ -29,6 +28,7 @@ #include "subs_controller.h" #include "timeedit_ctrl.h" +#include #include #include #include @@ -286,7 +286,7 @@ void DialogShiftTimes::OnHistoryClick(wxCommandEvent &evt) { json::Object& obj = history[entry]; if (obj["is by time"]) { - shift_time->SetTime(AssTime((std::string)obj["amount"])); + shift_time->SetTime(agi::Time((std::string)obj["amount"])); shift_by_time->SetValue(true); OnByTime(evt); } diff --git a/src/dialog_style_manager.cpp b/src/dialog_style_manager.cpp index 6231b4c4f..4a60ac086 100644 --- a/src/dialog_style_manager.cpp +++ b/src/dialog_style_manager.cpp @@ -46,10 +46,11 @@ #include "subtitle_format.h" #include +#include #include #include #include -#include +#include #include #include diff --git a/src/dialog_timing_processor.cpp b/src/dialog_timing_processor.cpp index e03c4b34a..00719c51c 100644 --- a/src/dialog_timing_processor.cpp +++ b/src/dialog_timing_processor.cpp @@ -29,7 +29,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "async_video_provider.h" #include "compat.h" #include "format.h" @@ -42,6 +41,7 @@ #include "utils.h" #include +#include #include #include diff --git a/src/dialog_video_details.cpp b/src/dialog_video_details.cpp index 277928dd8..84dab5462 100644 --- a/src/dialog_video_details.cpp +++ b/src/dialog_video_details.cpp @@ -27,13 +27,14 @@ // // Aegisub Project http://www.aegisub.org/ -#include "ass_time.h" #include "async_video_provider.h" #include "compat.h" #include "format.h" #include "include/aegisub/context.h" #include "project.h" +#include + #include #include #include @@ -59,7 +60,7 @@ void ShowVideoDetailsDialog(agi::Context *c) { make_field(_("FPS:"), fmt_wx("%.3f", fps.FPS())); make_field(_("Resolution:"), fmt_wx("%dx%d (%d:%d)", width, height, ar.numerator(), ar.denominator())); make_field(_("Length:"), fmt_plural(framecount, "1 frame", "%d frames (%s)", - framecount, AssTime(fps.TimeAtFrame(framecount - 1)).GetAssFormated(true))); + framecount, agi::Time(fps.TimeAtFrame(framecount - 1)).GetAssFormatted(true))); make_field(_("Decoder:"), to_wx(provider->GetDecoderName())); auto video_sizer = new wxStaticBoxSizer(wxVERTICAL, &d, _("Video")); diff --git a/src/grid_column.cpp b/src/grid_column.cpp index db52ce1a2..0cdec482b 100644 --- a/src/grid_column.cpp +++ b/src/grid_column.cpp @@ -131,7 +131,7 @@ struct GridColumnStartTime final : GridColumnTime { wxString Value(const AssDialogue *d, const agi::Context *c) const override { if (by_frame) return std::to_wstring(c->videoController->FrameAtTime(d->Start, agi::vfr::START)); - return to_wx(d->Start.GetAssFormated()); + return to_wx(d->Start.GetAssFormatted()); } int Width(const agi::Context *c, WidthHelper &helper) const override { @@ -149,7 +149,7 @@ struct GridColumnEndTime final : GridColumnTime { wxString Value(const AssDialogue *d, const agi::Context *c) const override { if (by_frame) return std::to_wstring(c->videoController->FrameAtTime(d->End, agi::vfr::END)); - return to_wx(d->End.GetAssFormated()); + return to_wx(d->End.GetAssFormatted()); } int Width(const agi::Context *c, WidthHelper &helper) const override { diff --git a/src/mkv_wrap.cpp b/src/mkv_wrap.cpp index 2085bcddc..38bc1aac4 100644 --- a/src/mkv_wrap.cpp +++ b/src/mkv_wrap.cpp @@ -36,12 +36,12 @@ #include "ass_file.h" #include "ass_parser.h" -#include "ass_time.h" #include "compat.h" #include "dialog_progress.h" #include "MatroskaParser.h" #include "options.h" +#include #include #include #include @@ -130,8 +130,8 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO * // Get start and end times int64_t timecodeScaleLow = 1000000; - AssTime subStart = startTime / timecodeScaleLow; - AssTime subEnd = endTime / timecodeScaleLow; + agi::Time subStart = startTime / timecodeScaleLow; + agi::Time subEnd = endTime / timecodeScaleLow; using str_range = boost::iterator_range; @@ -146,15 +146,15 @@ static void read_subtitles(agi::ProgressSink *ps, MatroskaFile *file, MkvStdIO * boost::lexical_cast(str_range(readBuf, first)), agi::format("Dialogue: %d,%s,%s,%s" , boost::lexical_cast(str_range(first + 1, second)) - , subStart.GetAssFormated() - , subEnd.GetAssFormated() + , subStart.GetAssFormatted() + , subEnd.GetAssFormatted() , str_range(second + 1, readBufEnd))); } // Process SRT else { auto line = agi::format("Dialogue: 0,%s,%s,Default,,0,0,0,,%s" - , subStart.GetAssFormated() - , subEnd.GetAssFormated() + , subStart.GetAssFormatted() + , subEnd.GetAssFormatted() , str_range(readBuf, readBufEnd)); boost::replace_all(line, "\r\n", "\\N"); boost::replace_all(line, "\r", "\\N"); diff --git a/src/resolution_resampler.cpp b/src/resolution_resampler.cpp index 2f79be4a2..9e3c49b66 100644 --- a/src/resolution_resampler.cpp +++ b/src/resolution_resampler.cpp @@ -21,6 +21,7 @@ #include "ass_style.h" #include "utils.h" +#include #include #include #include diff --git a/src/search_replace_engine.cpp b/src/search_replace_engine.cpp index e0e2066b4..d345bfbb1 100644 --- a/src/search_replace_engine.cpp +++ b/src/search_replace_engine.cpp @@ -23,6 +23,7 @@ #include "selection_controller.h" #include "text_selection_controller.h" +#include #include #include diff --git a/src/subs_edit_box.h b/src/subs_edit_box.h index 71e92cdd4..81b82aed6 100644 --- a/src/subs_edit_box.h +++ b/src/subs_edit_box.h @@ -40,9 +40,9 @@ namespace agi { namespace vfr { class Framerate; } } namespace agi { struct Context; } +namespace agi { class Time; } class AssDialogue; class AssStyle; -class AssTime; class SubsTextEditCtrl; class TimeEdit; class wxButton; @@ -126,7 +126,7 @@ class SubsEditBox final : public wxPanel { /// The start and end times of the selected lines without changes made to /// avoid negative durations, so that they can be restored if future changes /// eliminate the negative durations - boost::container::map> initial_times; + boost::container::map> initial_times; // Constructor helpers wxTextCtrl *MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg); diff --git a/src/subtitle_format.cpp b/src/subtitle_format.cpp index 11fe3b19a..d2d1573da 100644 --- a/src/subtitle_format.cpp +++ b/src/subtitle_format.cpp @@ -50,6 +50,7 @@ #include #include +#include #include #include diff --git a/src/subtitle_format_encore.cpp b/src/subtitle_format_encore.cpp index 865dda902..51843180a 100644 --- a/src/subtitle_format_encore.cpp +++ b/src/subtitle_format_encore.cpp @@ -38,6 +38,7 @@ #include "ass_file.h" #include "text_file_writer.h" +#include #include EncoreSubtitleFormat::EncoreSubtitleFormat() @@ -64,7 +65,7 @@ void EncoreSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& fi // Encore wants ; for NTSC and : for PAL // The manual suggests no other frame rates are supported - SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ";" : ":"); + agi::SmpteFormatter ft(fps, fps.NeedsDropFrames() ? ";" : ":"); // Write lines int i = 0; diff --git a/src/subtitle_format_microdvd.cpp b/src/subtitle_format_microdvd.cpp index d2e981bc1..6c3991718 100644 --- a/src/subtitle_format_microdvd.cpp +++ b/src/subtitle_format_microdvd.cpp @@ -36,14 +36,15 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "options.h" #include "text_file_reader.h" #include "text_file_writer.h" +#include #include #include #include +#include #include #include diff --git a/src/subtitle_format_srt.cpp b/src/subtitle_format_srt.cpp index 56c89f547..011f6c7a1 100644 --- a/src/subtitle_format_srt.cpp +++ b/src/subtitle_format_srt.cpp @@ -277,7 +277,7 @@ public: } }; -std::string WriteSRTTime(AssTime const& ts) +std::string WriteSRTTime(agi::Time const& ts) { return agi::format("%02d:%02d:%02d,%03d", ts.GetTimeHours(), ts.GetTimeMinutes(), ts.GetTimeSeconds(), ts.GetTimeMiliseconds()); } diff --git a/src/subtitle_format_ssa.cpp b/src/subtitle_format_ssa.cpp index 3a66f2856..623fbbc67 100644 --- a/src/subtitle_format_ssa.cpp +++ b/src/subtitle_format_ssa.cpp @@ -84,7 +84,7 @@ void SsaSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filen for (auto const& line : src->Events) file.WriteLineToFile(agi::format("%s: Marked=0,%s,%s,%s,%s,%d,%d,%d,%s,%s" , (line.Comment ? "Comment" : "Dialogue") - , line.Start.GetAssFormated(), line.End.GetAssFormated() + , line.Start.GetAssFormatted(), line.End.GetAssFormatted() , replace_commas(line.Style), replace_commas(line.Actor) , line.Margin[0], line.Margin[1], line.Margin[2] , replace_commas(line.Effect) diff --git a/src/subtitle_format_transtation.cpp b/src/subtitle_format_transtation.cpp index 8ac2c7ba7..66196764a 100644 --- a/src/subtitle_format_transtation.cpp +++ b/src/subtitle_format_transtation.cpp @@ -27,19 +27,15 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file subtitle_format_transtation.cpp -/// @brief Reading/writing Transtation-compatible subtitles -/// @ingroup subtitle_io -/// - #include "subtitle_format_transtation.h" #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" -#include "ass_time.h" #include "text_file_writer.h" +#include +#include #include TranStationSubtitleFormat::TranStationSubtitleFormat() @@ -52,7 +48,7 @@ std::vector TranStationSubtitleFormat::GetWriteWildcards() const { } void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path const& filename, agi::vfr::Framerate const& vfps, std::string const& encoding) const { - agi::vfr::Framerate fps = AskForFPS(false, true, vfps); + auto fps = AskForFPS(false, true, vfps); if (!fps.IsLoaded()) return; // Convert to TranStation @@ -64,7 +60,7 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons StripTags(copy); ConvertNewlines(copy, "\r\n"); - SmpteFormatter ft(fps); + agi::SmpteFormatter ft(fps); TextFileWriter file(filename, encoding); const AssDialogue *prev = nullptr; for (auto const& cur : copy.Events) { @@ -84,7 +80,7 @@ void TranStationSubtitleFormat::WriteFile(const AssFile *src, agi::fs::path cons file.WriteLineToFile("SUB["); } -std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialogue *current, agi::vfr::Framerate const& fps, SmpteFormatter const& ft, int nextl_start) const { +std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialogue *current, agi::vfr::Framerate const& fps, agi::SmpteFormatter const& ft, int nextl_start) const { int valign = 0; const char *halign = " "; // default is centered const char *type = "N"; // no special style @@ -101,7 +97,7 @@ std::string TranStationSubtitleFormat::ConvertLine(AssFile *file, const AssDialo if (current->Text.get().find("\\i1") != std::string::npos) type = "I"; // Write header - AssTime end = current->End; + agi::Time end = current->End; // Subtract one frame if the end time of the current line is equal to the // start of next one, since the end timestamp is inclusive and the lines diff --git a/src/subtitle_format_transtation.h b/src/subtitle_format_transtation.h index 02e51ceb5..da4390b29 100644 --- a/src/subtitle_format_transtation.h +++ b/src/subtitle_format_transtation.h @@ -27,18 +27,13 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file subtitle_format_transtation.h -/// @see subtitle_format_transtation.cpp -/// @ingroup subtitle_io -/// - #include "subtitle_format.h" class AssDialogue; -class SmpteFormatter; +namespace agi { class SmpteFormatter; } 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; + std::string ConvertLine(AssFile *file, const AssDialogue *line, agi::vfr::Framerate const& fps, agi::SmpteFormatter const& ft, int nextl_start) const; public: TranStationSubtitleFormat(); diff --git a/src/subtitle_format_ttxt.cpp b/src/subtitle_format_ttxt.cpp index d2af0a6d3..609f51bba 100644 --- a/src/subtitle_format_ttxt.cpp +++ b/src/subtitle_format_ttxt.cpp @@ -36,10 +36,11 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "compat.h" #include "options.h" +#include + #include DEFINE_EXCEPTION(TTXTParseError, SubtitleFormatParseError); @@ -102,7 +103,7 @@ void TTXTSubtitleFormat::ReadFile(AssFile *target, agi::fs::path const& filename AssDialogue *TTXTSubtitleFormat::ProcessLine(wxXmlNode *node, AssDialogue *prev, int version) const { // Get time wxString sampleTime = node->GetAttribute("sampleTime", "00:00:00.000"); - AssTime time(from_wx(sampleTime)); + agi::Time time(from_wx(sampleTime)); // Set end time of last line if (prev) @@ -233,7 +234,7 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, con // If it doesn't start at the end of previous, add blank if (prev && prev->End != line->Start) { wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample"); - node->AddAttribute("sampleTime", to_wx("0" + prev->End.GetAssFormated(true))); + node->AddAttribute("sampleTime", to_wx("0" + prev->End.GetAssFormatted(true))); node->AddAttribute("xml:space", "preserve"); root->AddChild(node); node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", "")); @@ -241,7 +242,7 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, const AssDialogue *prev, con // Generate and insert node wxXmlNode *node = new wxXmlNode(wxXML_ELEMENT_NODE, "TextSample"); - node->AddAttribute("sampleTime", to_wx("0" + line->Start.GetAssFormated(true))); + node->AddAttribute("sampleTime", to_wx("0" + line->Start.GetAssFormatted(true))); node->AddAttribute("xml:space", "preserve"); root->AddChild(node); node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, "", to_wx(line->Text))); @@ -256,7 +257,7 @@ void TTXTSubtitleFormat::ConvertToTTXT(AssFile &file) const { ConvertNewlines(file, "\r\n"); // Find last line - AssTime lastTime; + agi::Time lastTime; if (!file.Events.empty()) lastTime = file.Events.back().End; diff --git a/src/subtitles_provider_libass.cpp b/src/subtitles_provider_libass.cpp index 0d2bfdd01..3dc047e71 100644 --- a/src/subtitles_provider_libass.cpp +++ b/src/subtitles_provider_libass.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include diff --git a/src/timeedit_ctrl.cpp b/src/timeedit_ctrl.cpp index 20aed53e8..4ccb454d3 100644 --- a/src/timeedit_ctrl.cpp +++ b/src/timeedit_ctrl.cpp @@ -34,15 +34,15 @@ #include "timeedit_ctrl.h" -#include - -#include "ass_time.h" #include "compat.h" #include "include/aegisub/context.h" #include "options.h" #include "project.h" #include "utils.h" +#include + +#include #include #include @@ -67,7 +67,7 @@ TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const std:: SetValidator(val); // Other stuff - if (value.empty()) SetValue(to_wx(time.GetAssFormated())); + if (value.empty()) SetValue(to_wx(time.GetAssFormatted())); Bind(wxEVT_MENU, std::bind(&TimeEdit::CopyTime, this), Time_Edit_Copy); Bind(wxEVT_MENU, std::bind(&TimeEdit::PasteTime, this), Time_Edit_Paste); @@ -78,7 +78,7 @@ TimeEdit::TimeEdit(wxWindow* parent, wxWindowID id, agi::Context *c, const std:: Bind(wxEVT_KILL_FOCUS, &TimeEdit::OnFocusLost, this); } -void TimeEdit::SetTime(AssTime new_time) { +void TimeEdit::SetTime(agi::Time new_time) { if (time != new_time) { time = new_time; UpdateText(); @@ -115,7 +115,7 @@ void TimeEdit::UpdateText() { if (byFrame) ChangeValue(std::to_wstring(c->project->Timecodes().FrameAtTime(time, isEnd ? agi::vfr::END : agi::vfr::START))); else - ChangeValue(to_wx(time.GetAssFormated())); + ChangeValue(to_wx(time.GetAssFormatted())); } void TimeEdit::OnKeyDown(wxKeyEvent &event) { @@ -190,7 +190,7 @@ void TimeEdit::OnChar(wxKeyEvent &event) { // Overwrite the digit text[start] = (char)key; time = text; - SetValue(to_wx(time.GetAssFormated())); + SetValue(to_wx(time.GetAssFormatted())); SetInsertionPoint(start + 1); } @@ -229,8 +229,8 @@ void TimeEdit::PasteTime() { std::string text(GetClipboard()); if (text.empty()) return; - AssTime tempTime(text); - if (tempTime.GetAssFormated() == text) { + agi::Time tempTime(text); + if (tempTime.GetAssFormatted() == text) { SetTime(tempTime); SetSelection(0, GetValue().size()); diff --git a/src/timeedit_ctrl.h b/src/timeedit_ctrl.h index 925270223..a056a1181 100644 --- a/src/timeedit_ctrl.h +++ b/src/timeedit_ctrl.h @@ -27,23 +27,17 @@ // // Aegisub Project http://www.aegisub.org/ -/// @file timeedit_ctrl.h -/// @see timeedit_ctrl.cpp -/// @ingroup custom_control -/// +#include +#include #include -#include "ass_time.h" - -#include - namespace agi { class OptionValue; struct Context; } -/// @brief A text edit control for editing AssTime objects +/// @brief A text edit control for editing agi::Time objects /// /// 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 @@ -51,7 +45,7 @@ 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? - AssTime time; ///< The time, which may be displayed as either a frame number or time + agi::Time time; ///< The time, which may be displayed as either a frame number or time bool insert; ///< If true, disable overwriting behavior in time mode agi::signal::Connection insert_opt; @@ -78,10 +72,10 @@ class TimeEdit final : public wxTextCtrl { #endif public: - /// Get the current time as an AssTime object - AssTime GetTime() const { return time; } + /// Get the current time as an agi::Time object + agi::Time GetTime() const { return time; } /// Set the time - void SetTime(AssTime time); + void SetTime(agi::Time time); /// Get the current time as a frame number, or 0 if timecodes are unavailable int GetFrame() const; diff --git a/src/video_box.cpp b/src/video_box.cpp index 96a1d5948..49fff80aa 100644 --- a/src/video_box.cpp +++ b/src/video_box.cpp @@ -115,7 +115,7 @@ void VideoBox::UpdateTimeBoxes() { int time = context->videoController->TimeAtFrame(frame, agi::vfr::EXACT); // Set the text box for frame number and time - VideoPosition->SetValue(fmt_wx("%s - %d", AssTime(time).GetAssFormated(true), frame)); + VideoPosition->SetValue(fmt_wx("%s - %d", agi::Time(time).GetAssFormatted(true), frame)); if (boost::binary_search(context->project->Keyframes(), frame)) { // Set the background color to indicate this is a keyframe VideoPosition->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle Grid/Background/Selection")->GetColor())); diff --git a/src/video_controller.cpp b/src/video_controller.cpp index 1ea315a50..ae78fb2b4 100644 --- a/src/video_controller.cpp +++ b/src/video_controller.cpp @@ -31,7 +31,6 @@ #include "ass_dialogue.h" #include "ass_file.h" -#include "ass_time.h" #include "audio_controller.h" #include "compat.h" #include "include/aegisub/context.h" @@ -42,6 +41,8 @@ #include "async_video_provider.h" #include "utils.h" +#include + #include VideoController::VideoController(agi::Context *c) diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp index 049286291..bec098dd5 100644 --- a/src/visual_tool.cpp +++ b/src/visual_tool.cpp @@ -23,7 +23,6 @@ #include "ass_dialogue.h" #include "ass_file.h" #include "ass_style.h" -#include "ass_time.h" #include "include/aegisub/context.h" #include "selection_controller.h" #include "video_controller.h" @@ -32,6 +31,7 @@ #include "visual_tool_drag.h" #include "visual_tool_vector_clip.h" +#include #include #include diff --git a/tests/tests/time.cpp b/tests/tests/time.cpp new file mode 100644 index 000000000..74ea7558f --- /dev/null +++ b/tests/tests/time.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2014, Thomas Goyne +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +#include + +#include +#include + +using agi::Time; + +TEST(lagi_time, out_of_range_times) { + EXPECT_EQ(0, (int)Time(-1)); + EXPECT_EQ(10 * 60 * 60 * 1000 - 10, (int)Time(10 * 60 * 60 * 1000)); +} + +TEST(lagi_time, rounds_to_cs) { + EXPECT_EQ(10, (int)Time(14)); +} + +TEST(lagi_time, cs_formatting) { + EXPECT_STREQ("1:23:45.67", Time((((1 * 60) + 23) * 60 + 45) * 1000 + 670).GetAssFormatted().c_str()); +} + +TEST(lagi_time, ms_formatting) { + EXPECT_STREQ("1:23:45.678", Time((((1 * 60) + 23) * 60 + 45) * 1000 + 678).GetAssFormatted(true).c_str()); +} + +TEST(lagi_time, well_formed_ass_time_parse) { + EXPECT_STREQ("1:23:45.67", Time("1:23:45.67").GetAssFormatted().c_str()); +} + +TEST(lagi_time, missing_components) { + EXPECT_STREQ("0:23:45.67", Time("23:45.67").GetAssFormatted().c_str()); + EXPECT_STREQ("0:00:45.67", Time("45.67").GetAssFormatted().c_str()); + EXPECT_STREQ("0:00:45.60", Time("45.6").GetAssFormatted().c_str()); + EXPECT_STREQ("0:00:45.00", Time("45").GetAssFormatted().c_str()); +} + +TEST(lagi_time, out_of_range_compontents) { + EXPECT_STREQ("1:23:45.67", Time("0:83:45.67").GetAssFormatted().c_str()); + EXPECT_STREQ("0:01:40.00", Time("100").GetAssFormatted().c_str()); +} + +TEST(lagi_time, comma_decimal) { + EXPECT_STREQ("1:23:45.67", Time("1:23:45,67").GetAssFormatted().c_str()); +} + +TEST(lagi_time, component_getters) { + Time t("1:23:45.67"); + EXPECT_EQ(1, t.GetTimeHours()); + EXPECT_EQ(23, t.GetTimeMinutes()); + EXPECT_EQ(45, t.GetTimeSeconds()); + EXPECT_EQ(67, t.GetTimeCentiseconds()); + EXPECT_EQ(670, t.GetTimeMiliseconds()); +} + +TEST(lagi_time, srt_time) { + EXPECT_STREQ("1:23:45.678", Time("1:23:45,678").GetAssFormatted(true).c_str()); +} + +TEST(lagi_time, smpte_parse_valid) { + EXPECT_STREQ("1:23:45.44", agi::SmpteFormatter(25).FromSMPTE("1:23:45:11").GetAssFormatted().c_str()); +} + +TEST(lagi_time, smpte_parse_invalid) { + EXPECT_EQ(0, (int)agi::SmpteFormatter(25).FromSMPTE("1:23:45.11")); +} + +TEST(lagi_time, to_smpte) { + EXPECT_STREQ("01:23:45:11", agi::SmpteFormatter(25).ToSMPTE(Time("1:23:45.44")).c_str()); +}