Extract RecombineLines from SubtitlesGrid

This commit is contained in:
Thomas Goyne 2013-06-18 19:24:45 -07:00
parent bda127144d
commit ad58ae14bf
3 changed files with 101 additions and 117 deletions

View file

@ -61,10 +61,9 @@
#include <libaegisub/util.h> #include <libaegisub/util.h>
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptor/filtered.hpp> #include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/sliced.hpp> #include <boost/range/adaptor/sliced.hpp>
@ -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<std::string, std::string>);
}
bool check_end(AssDialogue *d1, AssDialogue *d2) {
return check_lines(d1, d2, &boost::ends_with<std::string, std::string>);
}
}
/// Recombine subtitles when they have been split and merged. /// Recombine subtitles when they have been split and merged.
struct edit_line_recombine : public validate_sel_multiple { struct edit_line_recombine : public validate_sel_multiple {
CMD_NAME("edit/line/recombine") 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") STR_HELP("Recombine subtitles when they have been split and merged")
void operator()(agi::Context *c) { 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<AssDialogue*> 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<AssDialogue>(), 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);
} }
}; };

View file

@ -36,118 +36,7 @@
#include "subs_grid.h" #include "subs_grid.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "include/aegisub/context.h"
#include "options.h"
#include "utils.h"
#include <algorithm>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/regex.hpp>
#include <utility>
SubtitlesGrid::SubtitlesGrid(wxWindow *parent, agi::Context *context) SubtitlesGrid::SubtitlesGrid(wxWindow *parent, agi::Context *context)
: BaseGrid(parent, context, wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER) : 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<std::string, std::string>);
}
static bool check_end(AssDialogue *d1, AssDialogue *d2) {
return check_lines(d1, d2, &boost::ends_with<std::string, std::string>);
}
void SubtitlesGrid::RecombineLines() {
Selection selectedSet = GetSelectedSet();
if (selectedSet.size() < 2) return;
AssDialogue *activeLine = GetActiveLine();
std::vector<AssDialogue*> 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<AssDialogue*>());
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);
}

View file

@ -37,6 +37,4 @@
class SubtitlesGrid: public BaseGrid { class SubtitlesGrid: public BaseGrid {
public: public:
SubtitlesGrid(wxWindow *parent, agi::Context *context); SubtitlesGrid(wxWindow *parent, agi::Context *context);
void RecombineLines();
}; };