forked from mia/Aegisub
1e0f08c0ed
Use boost::filesystem::path for all paths, and std::string for all other strings, converting to/from wxString as close to the actual uses of wx as possible. Where possible, replace the uses of non-UI wxWidgets functionality with the additions to the standard library in C++11, or the equivalents in boost. Move the path token management logic to libaegisub (and rewrite it in the process). Add a basic thread pool based on asio and std::thread to libaegisub. This touches nearly every file in the project and a nontrivial amount of code had to be rewritten entirely, so there's probably a lot of broken stuff.
189 lines
5.4 KiB
C++
189 lines
5.4 KiB
C++
// Copyright (c) 2006, Rodrigo Braz Monteiro
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of the Aegisub Group nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
/// @file subs_grid.cpp
|
|
/// @brief Subtitles grid control in main window
|
|
/// @ingroup main_ui
|
|
///
|
|
|
|
#include "config.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)
|
|
: 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);
|
|
}
|
|
|
|
void SubtitlesGrid::AdjoinLines(int n1,int n2,bool setStart) {
|
|
if (n1 == n2) {
|
|
if (setStart) {
|
|
--n1;
|
|
}
|
|
else {
|
|
++n2;
|
|
}
|
|
}
|
|
// Set start
|
|
if (setStart) {
|
|
AssDialogue *prev = GetDialogue(n1);
|
|
AssDialogue *cur;
|
|
for (int i=n1+1;i<=n2;i++) {
|
|
cur = GetDialogue(i);
|
|
if (!cur) return;
|
|
cur->Start = prev->End;
|
|
prev = cur;
|
|
}
|
|
}
|
|
|
|
// Set end
|
|
else {
|
|
AssDialogue *next;
|
|
AssDialogue *cur = GetDialogue(n1);
|
|
for (int i=n1;i<n2;i++) {
|
|
next = GetDialogue(i+1);
|
|
if (!next) return;
|
|
cur->End = next->Start;
|
|
cur = next;
|
|
}
|
|
}
|
|
|
|
context->ass->Commit(_("adjoin"), AssFile::COMMIT_DIAG_TIME);
|
|
}
|