Aegisub/aegisub/src/ass_dialogue.cpp

409 lines
10 KiB
C++
Raw Normal View History

2006-01-16 22:02:54 +01:00
// Copyright (c) 2005, 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 ass_dialogue.cpp
/// @brief Class for dialogue lines in subtitles
/// @ingroup subs_storage
2006-01-16 22:02:54 +01:00
#include "config.h"
#ifndef AGI_PRE
#include <fstream>
#include <list>
#include <vector>
#include <wx/regex.h>
#include <wx/tokenzr.h>
#endif
#include "ass_dialogue.h"
#include "ass_override.h"
#include "compat.h"
#include "subtitle_format.h"
#include "utils.h"
AssDialogue::AssDialogue()
: AssEntry(wxString(), "[Events]")
, Comment(false)
, Layer(0)
, Start(0)
, End(5000)
, Style("Default")
{
2012-10-05 04:10:20 +02:00
memset(Margin, 0, sizeof Margin);
2006-01-16 22:02:54 +01:00
}
AssDialogue::AssDialogue(AssDialogue const& that)
: AssEntry(wxString(), that.group)
, Comment(that.Comment)
, Layer(that.Layer)
, Start(that.Start)
, End(that.End)
, Style(that.Style)
, Actor(that.Actor)
, Effect(that.Effect)
, Text(that.Text)
{
memmove(Margin, that.Margin, sizeof Margin);
}
AssDialogue::AssDialogue(wxString const& data)
: AssEntry(wxString(), "[Events]")
, Comment(false)
, Layer(0)
, Start(0)
, End(5000)
, Style("Default")
{
if (!Parse(data))
throw SubtitleFormatParseError(STD_STR("Failed parsing line: " + data), 0);
2006-01-16 22:02:54 +01:00
}
AssDialogue::~AssDialogue () {
delete_clear(Blocks);
2006-01-16 22:02:54 +01:00
}
void AssDialogue::ClearBlocks() {
delete_clear(Blocks);
}
bool AssDialogue::Parse(wxString const& rawData) {
2006-01-16 22:02:54 +01:00
size_t pos = 0;
wxString temp;
// Get type
if (rawData.StartsWith("Dialogue:")) {
2006-01-16 22:02:54 +01:00
Comment = false;
pos = 10;
}
else if (rawData.StartsWith("Comment:")) {
2006-01-16 22:02:54 +01:00
Comment = true;
pos = 9;
}
else return false;
wxStringTokenizer tkn(rawData.Mid(pos),",",wxTOKEN_RET_EMPTY_ALL);
if (!tkn.HasMoreTokens()) return false;
2006-01-16 22:02:54 +01:00
// Get first token and see if it has "Marked=" in it
2006-01-16 22:02:54 +01:00
temp = tkn.GetNextToken().Trim(false).Trim(true);
bool ssa = temp.Lower().StartsWith("marked=");
// Get layer number
if (ssa)
Layer = 0;
2006-01-16 22:02:54 +01:00
else {
long templ;
temp.ToLong(&templ);
Layer = templ;
}
// Get start time
if (!tkn.HasMoreTokens()) return false;
Start = tkn.GetNextToken();
2006-01-16 22:02:54 +01:00
// Get end time
if (!tkn.HasMoreTokens()) return false;
End = tkn.GetNextToken();
2006-01-16 22:02:54 +01:00
// Get style
if (!tkn.HasMoreTokens()) return false;
Style = tkn.GetNextToken();
Style.Trim(true);
Style.Trim(false);
// Get actor
if (!tkn.HasMoreTokens()) return false;
Actor = tkn.GetNextToken();
Actor.Trim(true);
Actor.Trim(false);
// Get margins
for (int i = 0; i < 3; ++i) {
if (!tkn.HasMoreTokens()) return false;
SetMarginString(tkn.GetNextToken().Trim(false).Trim(true), i);
}
2006-01-16 22:02:54 +01:00
if (!tkn.HasMoreTokens()) return false;
Effect = tkn.GetNextToken();
2006-01-16 22:02:54 +01:00
Effect.Trim(true);
Effect.Trim(false);
// Get text
Text = rawData.Mid(pos + tkn.GetPosition());
2006-01-16 22:02:54 +01:00
return true;
}
wxString AssDialogue::GetData(bool ssa) const {
wxString s = Style;
wxString a = Actor;
wxString e = Effect;
s.Replace(",",";");
a.Replace(",",";");
e.Replace(",",";");
wxString str = wxString::Format(
"%s: %s,%s,%s,%s,%s,%d,%d,%d,%s,%s",
Comment ? "Comment" : "Dialogue",
ssa ? "Marked=0" : wxString::Format("%01d", Layer),
Start.GetAssFormated(),
End.GetAssFormated(),
s, a,
Margin[0], Margin[1], Margin[2],
e,
Text);
// Make sure that final has no line breaks
str.Replace("\n", "");
str.Replace("\r", "");
return str;
}
const wxString AssDialogue::GetEntryData() const {
return GetData(false);
2006-01-16 22:02:54 +01:00
}
wxString AssDialogue::GetSSAText () const {
return GetData(true);
2006-01-16 22:02:54 +01:00
}
std::vector<AssDialogueBlock*> AssDialogue::ParseTags() const {
std::vector<AssDialogueBlock*> Blocks;
2006-01-16 22:02:54 +01:00
// Empty line, make an empty block
if (Text.empty()) {
Blocks.push_back(new AssDialogueBlockPlain);
return Blocks;
}
2006-01-16 22:02:54 +01:00
int drawingLevel = 0;
for (size_t len = Text.size(), cur = 0; cur < len; ) {
2006-01-16 22:02:54 +01:00
// Overrides block
if (Text[cur] == '{') {
++cur;
2006-01-16 22:02:54 +01:00
// Get contents of block
wxString work;
size_t end = Text.find("}", cur);
2006-01-16 22:02:54 +01:00
if (end == wxString::npos) {
work = Text.substr(cur);
cur = len;
}
else {
work = Text.substr(cur, end - cur);
cur = end + 1;
2006-01-16 22:02:54 +01:00
}
if (work.size() && work.Find("\\") == wxNOT_FOUND) {
//We've found an override block with no backslashes
//We're going to assume it's a comment and not consider it an override block
//Currently we'll treat this as a plain text block, but feel free to create a new class
Blocks.push_back(new AssDialogueBlockPlain("{" + work + "}"));
}
else {
// Create block
AssDialogueBlockOverride *block = new AssDialogueBlockOverride(work);
block->ParseTags();
Blocks.push_back(block);
// Look for \p in block
std::vector<AssOverrideTag*>::iterator curTag;
2012-10-05 04:10:20 +02:00
for (curTag = block->Tags.begin(); curTag != block->Tags.end(); ++curTag) {
if ((*curTag)->Name == "\\p") {
drawingLevel = (*curTag)->Params[0]->Get<int>(0);
}
2006-01-16 22:02:54 +01:00
}
}
}
// Plain-text/drawing block
else {
wxString work;
size_t end = Text.find("{",cur);
2006-01-16 22:02:54 +01:00
if (end == wxString::npos) {
work = Text.substr(cur);
cur = len;
}
else {
work = Text.substr(cur, end - cur);
cur = end;
2006-01-16 22:02:54 +01:00
}
// Plain-text
if (drawingLevel == 0) {
Blocks.push_back(new AssDialogueBlockPlain(work));
2006-01-16 22:02:54 +01:00
}
// Drawing
else {
Blocks.push_back(new AssDialogueBlockDrawing(work, drawingLevel));
2006-01-16 22:02:54 +01:00
}
}
}
return Blocks;
}
void AssDialogue::ParseAssTags() {
ClearBlocks();
Blocks = ParseTags();
2006-01-16 22:02:54 +01:00
}
void AssDialogue::StripTags () {
Text = GetStrippedText();
2006-01-16 22:02:54 +01:00
}
void AssDialogue::StripTag (wxString tagName) {
ParseAssTags();
wxString final;
// Look for blocks
2012-10-05 04:10:20 +02:00
for (std::vector<AssDialogueBlock*>::iterator cur = Blocks.begin(); cur != Blocks.end(); ++cur) {
if ((*cur)->GetType() != BLOCK_OVERRIDE) {
final += (*cur)->GetText();
continue;
}
AssDialogueBlockOverride *over = static_cast<AssDialogueBlockOverride*>(*cur);
2012-10-05 04:10:20 +02:00
wxString temp;
for (size_t i = 0; i < over->Tags.size(); ++i) {
if (over->Tags[i]->Name != tagName)
temp += *over->Tags[i];
}
2012-10-05 04:10:20 +02:00
if (!temp.empty())
final += "{" + temp + "}";
}
ClearBlocks();
Text = final;
}
2006-01-16 22:02:54 +01:00
void AssDialogue::UpdateText () {
if (Blocks.empty()) return;
Text.clear();
2012-10-05 04:10:20 +02:00
for (std::vector<AssDialogueBlock*>::iterator cur = Blocks.begin(); cur != Blocks.end(); ++cur) {
if ((*cur)->GetType() == BLOCK_OVERRIDE) {
Text += "{";
2006-01-16 22:02:54 +01:00
Text += (*cur)->GetText();
Text += "}";
2006-01-16 22:02:54 +01:00
}
else Text += (*cur)->GetText();
}
}
2012-10-05 04:10:20 +02:00
void AssDialogue::SetMarginString(wxString const& origvalue, int which) {
if (which < 0 || which > 2) throw InvalidMarginIdError();
2006-01-16 22:02:54 +01:00
// Make it numeric
wxString strvalue = origvalue;
if (!strvalue.IsNumber()) {
strvalue.clear();
2012-10-05 04:10:20 +02:00
for (size_t i = 0; i < origvalue.Length(); ++i) {
if (origvalue.Mid(i, 1).IsNumber()) {
strvalue += origvalue.Mid(i, 1);
2006-01-16 22:02:54 +01:00
}
}
}
// Get value
long value = 0;
2006-01-16 22:02:54 +01:00
strvalue.ToLong(&value);
Margin[which] = mid<int>(0, value, 9999);
2006-01-16 22:02:54 +01:00
}
wxString AssDialogue::GetMarginString(int which, bool pad) const {
if (which < 0 || which > 2) throw InvalidMarginIdError();
2012-10-05 04:10:20 +02:00
return wxString::Format(pad ? "%04d" : "%d", Margin[which]);
2006-01-16 22:02:54 +01:00
}
void AssDialogue::ProcessParameters(AssDialogueBlockOverride::ProcessParametersCallback callback,void *userData) {
2006-01-16 22:02:54 +01:00
// Apply for all override blocks
2012-10-05 04:10:20 +02:00
for (std::vector<AssDialogueBlock*>::iterator cur = Blocks.begin(); cur != Blocks.end(); ++cur) {
if ((*cur)->GetType() == BLOCK_OVERRIDE) {
2012-10-05 04:10:20 +02:00
static_cast<AssDialogueBlockOverride*>(*cur)->ProcessParameters(callback, userData);
2006-01-16 22:02:54 +01:00
}
}
}
bool AssDialogue::CollidesWith(const AssDialogue *target) const {
2006-01-16 22:02:54 +01:00
if (!target) return false;
return ((Start < target->Start) ? (target->Start < End) : (Start < target->End));
2006-01-16 22:02:54 +01:00
}
wxString AssDialogue::GetStrippedText() const {
2012-10-05 04:10:20 +02:00
static const wxRegEx reg("\\{[^\\{]*\\}", wxRE_ADVANCED);
wxString txt(Text);
2012-10-05 04:10:20 +02:00
reg.Replace(&txt, "");
return txt;
}
AssEntry *AssDialogue::Clone() const {
return new AssDialogue(*this);
}
void AssDialogueBlockDrawing::TransformCoords(int mx,int my,double x,double y) {
2006-01-16 22:02:54 +01:00
// HACK: Implement a proper parser ffs!!
// Could use Spline but it'd be slower and this seems to work fine
wxStringTokenizer tkn(GetText()," ",wxTOKEN_DEFAULT);
2006-01-16 22:02:54 +01:00
wxString cur;
wxString final;
bool isX = true;
long temp;
// Process tokens
while (tkn.HasMoreTokens()) {
cur = tkn.GetNextToken().Lower();
// Number, process it
if (cur.IsNumber()) {
// Transform it
2006-01-16 22:02:54 +01:00
cur.ToLong(&temp);
if (isX) temp = (long int)((temp+mx)*x + 0.5);
else temp = (long int)((temp+my)*y + 0.5);
2006-01-16 22:02:54 +01:00
// Write back to list
final += wxString::Format("%i ",temp);
2006-01-16 22:02:54 +01:00
// Toggle X/Y
isX = !isX;
}
// Text
else {
if (cur == "m" || cur == "n" || cur == "l" || cur == "b" || cur == "s" || cur == "p" || cur == "c") isX = true;
final += cur + " ";
2006-01-16 22:02:54 +01:00
}
}
// Write back final
final = final.Left(final.Length()-1);
text = final;
}