diff --git a/aegisub/src/command/edit.cpp b/aegisub/src/command/edit.cpp index b1225d81b..66b437fa2 100644 --- a/aegisub/src/command/edit.cpp +++ b/aegisub/src/command/edit.cpp @@ -61,10 +61,9 @@ #include #include -#include -#include -#include +#include #include +#include #include #include #include @@ -826,6 +825,40 @@ struct edit_line_paste_over : public Command { } }; +namespace { +std::string trim_text(std::string text) { + boost::regex start("^( |\t|\\\\[nNh])+"); + boost::regex end("( |\t|\\\\[nNh])+$"); + + regex_replace(text, start, "", boost::format_first_only); + regex_replace(text, end, "", boost::format_first_only); + return text; +} + +void expand_times(AssDialogue *src, AssDialogue *dst) { + dst->Start = std::min(dst->Start, src->Start); + dst->End = std::max(dst->End, src->End); +} + +bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (*pred)(std::string const&, std::string const&)) { + if (pred(d1->Text.get(), d2->Text.get())) { + d1->Text = trim_text(d1->Text.get().substr(d2->Text.get().size())); + expand_times(d1, d2); + return true; + } + return false; +} + +bool check_start(AssDialogue *d1, AssDialogue *d2) { + return check_lines(d1, d2, &boost::starts_with); +} + +bool check_end(AssDialogue *d1, AssDialogue *d2) { + return check_lines(d1, d2, &boost::ends_with); +} + +} + /// Recombine subtitles when they have been split and merged. struct edit_line_recombine : public validate_sel_multiple { CMD_NAME("edit/line/recombine") @@ -834,7 +867,71 @@ struct edit_line_recombine : public validate_sel_multiple { STR_HELP("Recombine subtitles when they have been split and merged") void operator()(agi::Context *c) { - c->subsGrid->RecombineLines(); + auto sel_set = c->selectionController->GetSelectedSet(); + if (sel_set.size() < 2) return; + + auto active_line = c->selectionController->GetActiveLine(); + + std::vector sel(sel_set.begin(), sel_set.end()); + boost::sort(sel, &AssFile::CompStart); + for (auto &diag : sel) + diag->Text = trim_text(diag->Text); + + auto end = sel.end() - 1; + for (auto cur = sel.begin(); cur != end; ++cur) { + auto d1 = *cur; + auto d2 = cur + 1; + + // 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate + if (d1->Text == (*d2)->Text) { + expand_times(d1, *d2); + delete d1; + continue; + } + + // 1, 1+2, 1 turns into 1, 2, [empty] + if (d1->Text.get().empty()) { + delete d1; + continue; + } + + // If d2 is the last line in the selection it'll never hit the above test + if (d2 == end && (*d2)->Text.get().empty()) { + delete *d2; + continue; + } + + // 1, 1+2 + while (d2 <= end && check_start(*d2, d1)) + ++d2; + + // 1, 2+1 + while (d2 <= end && check_end(*d2, d1)) + ++d2; + + // 1+2, 2 + while (d2 <= end && check_end(d1, *d2)) + ++d2; + + // 2+1, 2 + while (d2 <= end && check_start(d1, *d2)) + ++d2; + } + + // Remove now non-existent lines from the selection + SubtitleSelection lines, new_sel; + boost::copy(c->ass->Line | agi::of_type(), inserter(lines, lines.begin())); + boost::set_intersection(lines, sel_set, inserter(new_sel, new_sel.begin())); + + if (new_sel.empty()) + new_sel.insert(*lines.begin()); + + // Restore selection + if (!new_sel.count(active_line)) + active_line = *new_sel.begin(); + c->selectionController->SetSelectionAndActive(new_sel, active_line); + + c->ass->Commit(_("combining"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL); } }; diff --git a/aegisub/src/subs_grid.cpp b/aegisub/src/subs_grid.cpp index a79bcb2b3..5b6533bac 100644 --- a/aegisub/src/subs_grid.cpp +++ b/aegisub/src/subs_grid.cpp @@ -36,118 +36,7 @@ #include "subs_grid.h" -#include "ass_dialogue.h" -#include "ass_file.h" -#include "include/aegisub/context.h" -#include "options.h" -#include "utils.h" - -#include -#include -#include -#include - SubtitlesGrid::SubtitlesGrid(wxWindow *parent, agi::Context *context) : BaseGrid(parent, context, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER) { } - -static std::string trim_text(std::string text) { - boost::regex start("^( |\t|\\\\[nNh])+"); - boost::regex end("( |\t|\\\\[nNh])+$"); - - regex_replace(text, start, "", boost::format_first_only); - regex_replace(text, end, "", boost::format_first_only); - return text; -} - -static void expand_times(AssDialogue *src, AssDialogue *dst) { - dst->Start = std::min(dst->Start, src->Start); - dst->End = std::max(dst->End, src->End); -} - -static bool check_lines(AssDialogue *d1, AssDialogue *d2, bool (*pred)(std::string const&, std::string const&)) { - if (pred(d1->Text.get(), d2->Text.get())) { - d1->Text = trim_text(d1->Text.get().substr(d2->Text.get().size())); - expand_times(d1, d2); - return true; - } - return false; -} - -static bool check_start(AssDialogue *d1, AssDialogue *d2) { - return check_lines(d1, d2, &boost::starts_with); -} - -static bool check_end(AssDialogue *d1, AssDialogue *d2) { - return check_lines(d1, d2, &boost::ends_with); -} - -void SubtitlesGrid::RecombineLines() { - Selection selectedSet = GetSelectedSet(); - if (selectedSet.size() < 2) return; - - AssDialogue *activeLine = GetActiveLine(); - - std::vector sel(selectedSet.begin(), selectedSet.end()); - sort(sel.begin(), sel.end(), &AssFile::CompStart); - for (auto &diag : sel) - diag->Text = trim_text(diag->Text); - - auto end = sel.end() - 1; - for (auto cur = sel.begin(); cur != end; ++cur) { - AssDialogue *d1 = *cur; - auto d2 = cur + 1; - - // 1, 1+2 (or 2+1), 2 gets turned into 1, 2, 2 so kill the duplicate - if (d1->Text == (*d2)->Text) { - expand_times(d1, *d2); - delete d1; - continue; - } - - // 1, 1+2, 1 turns into 1, 2, [empty] - if (d1->Text.get().empty()) { - delete d1; - continue; - } - - // If d2 is the last line in the selection it'll never hit the above test - if (d2 == end && (*d2)->Text.get().empty()) { - delete *d2; - continue; - } - - // 1, 1+2 - while (d2 <= end && check_start(*d2, d1)) - ++d2; - - // 1, 2+1 - while (d2 <= end && check_end(*d2, d1)) - ++d2; - - // 1+2, 2 - while (d2 <= end && check_end(d1, *d2)) - ++d2; - - // 2+1, 2 - while (d2 <= end && check_start(d1, *d2)) - ++d2; - } - - // Remove now non-existent lines from the selection - Selection lines; - transform(context->ass->Line.begin(), context->ass->Line.end(), inserter(lines, lines.begin()), cast()); - Selection newSel; - set_intersection(lines.begin(), lines.end(), selectedSet.begin(), selectedSet.end(), inserter(newSel, newSel.begin())); - - if (newSel.empty()) - newSel.insert(*lines.begin()); - - // Restore selection - if (!newSel.count(activeLine)) - activeLine = *newSel.begin(); - SetSelectionAndActive(newSel, activeLine); - - context->ass->Commit(_("combining"), AssFile::COMMIT_DIAG_ADDREM | AssFile::COMMIT_DIAG_FULL); -} diff --git a/aegisub/src/subs_grid.h b/aegisub/src/subs_grid.h index 33632b94e..e6b7fa443 100644 --- a/aegisub/src/subs_grid.h +++ b/aegisub/src/subs_grid.h @@ -37,6 +37,4 @@ class SubtitlesGrid: public BaseGrid { public: SubtitlesGrid(wxWindow *parent, agi::Context *context); - - void RecombineLines(); };