Aegisub/aegisub/src/subtitle_format_txt.cpp
Thomas Goyne 4ec507f814 Clean up SubtitleFormat
Document all of the SubtitleFormat methods.

Add default implementations of CanReadFile and CanWriteFile that check
against the appropriate wildcard list.

Clean up and simplify a lot of very odd code.

Throw typed exceptions in all subtitle readers rather than strings.

Originally committed to SVN as r5617.
2011-09-28 19:44:53 +00:00

204 lines
5.9 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/
//
// $Id$
/// @file subtitle_format_txt.cpp
/// @brief Importing/exporting subtitles to untimed plain text
/// @ingroup subtitle_io
///
#include "config.h"
#include "subtitle_format_txt.h"
#include "ass_dialogue.h"
#include "compat.h"
#include "dialog_text_import.h"
#include "main.h"
#include "text_file_reader.h"
#include "text_file_writer.h"
#include "version.h"
TXTSubtitleFormat::TXTSubtitleFormat()
: SubtitleFormat("Plain-Text")
{
}
wxArrayString TXTSubtitleFormat::GetReadWildcards() const {
wxArrayString formats;
formats.Add("txt");
return formats;
}
wxArrayString TXTSubtitleFormat::GetWriteWildcards() const {
return GetReadWildcards();
}
bool TXTSubtitleFormat::CanWriteFile(wxString const& filename) const {
return (filename.Right(4).Lower() == ".txt" && filename.Right(11).Lower() != ".encore.txt" && filename.Right(16).Lower() != ".transtation.txt");
}
void TXTSubtitleFormat::ReadFile(wxString const& filename, wxString const& encoding) {
using namespace std;
DialogTextImport dlg;
if (dlg.ShowModal() == wxID_CANCEL) return;
TextFileReader file(filename, encoding, false);
LoadDefault(false);
wxString actor;
wxString separator = lagi_wxString(OPT_GET("Tool/Import/Text/Actor Separator")->GetString());
wxString comment = lagi_wxString(OPT_GET("Tool/Import/Text/Comment Starter")->GetString());
int lines = 0;
// Parse file
while (file.HasMoreLines()) {
wxString value = file.ReadLineFromFile();
if(value.empty()) continue;
// Check if this isn't a timecodes file
if (value.StartsWith("# timecode"))
throw SubtitleFormatParseError("File is a timecode file, cannot load as subtitles.", 0);
// Read comment data
bool isComment = false;
if (!comment.empty() && value.StartsWith(comment)) {
isComment = true;
value = value.Mid(comment.size());
}
// Read actor data
if (!isComment && !separator.empty()) {
if (value[0] != ' ' && value[0] != '\t') {
int pos = value.Find(separator);
if (pos != wxNOT_FOUND) {
actor = value.Left(pos);
actor.Trim(false);
actor.Trim(true);
value = value.Mid(pos+1);
}
}
}
// Trim spaces at start
value.Trim(false);
if (value.empty())
isComment = true;
// Sets line up
AssDialogue *line = new AssDialogue;
line->group = "[Events]";
line->Style = "Default";
line->Actor = isComment ? "" : line->Actor;
line->Comment = isComment;
line->Text = value;
line->Start.SetMS(0);
line->End.SetMS(0);
// Adds line
Line->push_back(line);
lines++;
}
// No lines?
if (lines == 0) {
AssDialogue *line = new AssDialogue;
line->group = "[Events]";
line->Style = "Default";
line->Start.SetMS(0);
line->End.SetMS(OPT_GET("Timing/Default Duration")->GetInt());
Line->push_back(line);
}
}
void TXTSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) {
size_t num_actor_names = 0, num_dialogue_lines = 0;
// Detect number of lines with Actor field filled out
for (std::list<AssEntry*>::iterator l = Line->begin(); l != Line->end(); ++l) {
AssDialogue *dia = dynamic_cast<AssDialogue*>(*l);
if (dia && !dia->Comment) {
num_dialogue_lines++;
if (!dia->Actor.empty())
num_actor_names++;
}
}
// If too few lines have Actor filled out, don't write it
bool write_actors = num_actor_names > num_dialogue_lines/2;
bool strip_formatting = true;
TextFileWriter file(filename, encoding);
file.WriteLineToFile(wxString("# Exported by Aegisub ") + GetAegisubShortVersionString());
// Write the file
for (std::list<AssEntry*>::iterator l = Line->begin(); l != Line->end(); ++l) {
AssDialogue *dia = dynamic_cast<AssDialogue*>(*l);
if (dia) {
wxString out_line;
if (dia->Comment) {
out_line = "# ";
}
if (write_actors) {
out_line += dia->Actor + ": ";
}
wxString out_text;
if (strip_formatting) {
dia->ParseASSTags();
for (std::vector<AssDialogueBlock*>::iterator block = dia->Blocks.begin(); block != dia->Blocks.end(); ++block) {
if ((*block)->GetType() == BLOCK_PLAIN) {
out_text += (*block)->GetText();
}
}
dia->ClearBlocks();
}
else {
out_text = dia->Text;
}
out_line += out_text;
if (!out_text.empty()) {
file.WriteLineToFile(out_line);
}
}
else {
// Not a dialogue line
// TODO: should any non-dia lines cause blank lines in output?
//file.WriteLineToFile("");
}
}
}