mirror of https://github.com/odrling/Aegisub
768 lines
21 KiB
C++
768 lines
21 KiB
C++
// Copyright (c) 2005, 2006, 2007, Niels Martin Hansen
|
|
// 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
|
|
//
|
|
// Website: http://aegisub.cellosoft.com
|
|
// Contact: mailto:jiifurusu@gmail.com
|
|
//
|
|
|
|
|
|
#ifdef WITH_AUTOMATION
|
|
#ifdef WITH_AUTO3
|
|
|
|
#if __VISUALC__ >= 1200
|
|
#pragma comment(lib,"aegisub-auto3.lib")
|
|
#endif
|
|
|
|
#include <wx/spinctrl.h>
|
|
#include <wx/tokenzr.h>
|
|
#include "auto4_auto3.h"
|
|
#include "../auto3/auto3.h"
|
|
#include "options.h"
|
|
#include "string_codec.h"
|
|
#include "vfr.h"
|
|
#include "ass_override.h"
|
|
|
|
namespace Automation4 {
|
|
|
|
// Auto3ProgressSink
|
|
|
|
void Auto3ProgressSink::SetStatus(void *cbdata, const char *msg)
|
|
{
|
|
Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata;
|
|
ps->SetTask(wxString(msg, wxConvUTF8));
|
|
}
|
|
|
|
void Auto3ProgressSink::OutputDebug(void *cbdata, const char *msg)
|
|
{
|
|
Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata;
|
|
ps->AddDebugOutput(wxString(msg, wxConvUTF8));
|
|
ps->AddDebugOutput(_T("\n"));
|
|
}
|
|
|
|
void Auto3ProgressSink::ReportProgress(void *cbdata, float progress)
|
|
{
|
|
Auto3ProgressSink *ps = (Auto3ProgressSink*)cbdata;
|
|
ps->SetProgress(progress);
|
|
}
|
|
|
|
Auto3ProgressSink::Auto3ProgressSink(Auto3Interpreter *_script, wxWindow *parent)
|
|
: ProgressSink(parent)
|
|
, script(_script)
|
|
{
|
|
script->cb.logdata = this;
|
|
script->cb.log_error = OutputDebug;
|
|
script->cb.log_message = OutputDebug;
|
|
script->cb.set_progress = ReportProgress;
|
|
script->cb.set_status = SetStatus;
|
|
}
|
|
|
|
Auto3ProgressSink::~Auto3ProgressSink()
|
|
{
|
|
script->cb.logdata = 0;
|
|
script->cb.log_error = 0;
|
|
script->cb.log_message = 0;
|
|
script->cb.set_progress = 0;
|
|
script->cb.set_status = 0;
|
|
}
|
|
|
|
|
|
// Auto3ConfigDialog
|
|
|
|
wxWindow* Auto3ConfigDialog::CreateWindow(wxWindow *parent)
|
|
{
|
|
if (options->name == 0)
|
|
return 0;
|
|
|
|
wxPanel *res = new wxPanel(parent, -1);
|
|
|
|
wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 5, 5);
|
|
|
|
for (Auto3ConfigOption *opt = options; opt->name; opt++) {
|
|
if (opt->kind == COK_INVALID)
|
|
continue;
|
|
|
|
Control control;
|
|
control.option = opt;
|
|
|
|
switch (opt->kind) {
|
|
case COK_LABEL:
|
|
control.control = new wxStaticText(res, -1, wxString(opt->label, wxConvUTF8));
|
|
break;
|
|
|
|
case COK_TEXT:
|
|
control.control = new wxTextCtrl(res, -1, wxString(opt->value.stringval, wxConvUTF8));
|
|
break;
|
|
|
|
case COK_INT:
|
|
control.control = new wxSpinCtrl(res, -1);
|
|
if (opt->min.valid && opt->max.valid) {
|
|
((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, opt->max.intval);
|
|
} else if (opt->min.valid) {
|
|
((wxSpinCtrl*)control.control)->SetRange(opt->min.intval, 0x7fff);
|
|
} else if (opt->max.valid) {
|
|
((wxSpinCtrl*)control.control)->SetRange(-0x7fff, opt->max.intval);
|
|
} else {
|
|
((wxSpinCtrl*)control.control)->SetRange(-0x7fff, 0x7fff);
|
|
}
|
|
((wxSpinCtrl*)control.control)->SetValue(opt->value.intval);
|
|
break;
|
|
|
|
case COK_FLOAT:
|
|
control.control = new wxTextCtrl(res, -1, wxString::Format(_T("%f"), opt->value.floatval));
|
|
break;
|
|
|
|
case COK_BOOL:
|
|
control.control = new wxCheckBox(res, -1, wxString(opt->label, wxConvUTF8));
|
|
((wxCheckBox*)control.control)->SetValue(!!opt->value.intval);
|
|
break;
|
|
|
|
case COK_COLOUR:
|
|
// *FIXME* what to do here?
|
|
// just put a stupid edit box for now
|
|
control.control = new wxTextCtrl(res, -1, wxString(opt->value.stringval, wxConvUTF8));
|
|
break;
|
|
|
|
case COK_STYLE:
|
|
control.control = new wxChoice(res, -1, wxDefaultPosition, wxDefaultSize, AssFile::top->GetStyles());
|
|
((wxChoice*)control.control)->Insert(_T(""), 0);
|
|
break;
|
|
|
|
case COK_INVALID:
|
|
break;
|
|
}
|
|
|
|
if (opt->kind != COK_LABEL && opt->kind != COK_BOOL) {
|
|
control.label = new wxStaticText(res, -1, wxString(opt->label, wxConvUTF8));
|
|
sizer->Add(control.label, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL);
|
|
} else {
|
|
control.label = 0;
|
|
sizer->AddSpacer(0);
|
|
}
|
|
control.control->SetToolTip(wxString(opt->hint, wxConvUTF8));
|
|
sizer->Add(control.control, 1, wxEXPAND);
|
|
|
|
controls.push_back(control);
|
|
}
|
|
|
|
res->SetSizerAndFit(sizer);
|
|
|
|
return res;
|
|
}
|
|
|
|
Auto3ConfigDialog::Auto3ConfigDialog(Auto3Interpreter *script)
|
|
{
|
|
options = script->config;
|
|
}
|
|
|
|
Auto3ConfigDialog::~Auto3ConfigDialog()
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
void Auto3ConfigDialog::ReadBack()
|
|
{
|
|
for (std::vector<Control>::iterator ctl = controls.begin(); ctl != controls.end(); ctl++) {
|
|
switch (ctl->option->kind) {
|
|
case COK_TEXT:
|
|
Auto3Free(ctl->option->value.stringval);
|
|
ctl->option->value.stringval = Auto3Strdup(((wxTextCtrl*)ctl->control)->GetValue().mb_str(wxConvUTF8));
|
|
break;
|
|
|
|
case COK_INT:
|
|
ctl->option->value.intval = ((wxSpinCtrl*)ctl->control)->GetValue();
|
|
break;
|
|
|
|
case COK_FLOAT: {
|
|
double v;
|
|
if (!((wxTextCtrl*)ctl->control)->GetValue().ToDouble(&v)) {
|
|
wxLogWarning(
|
|
_T("The value entered for field '%s' (%s) could not be converted to a floating-point number. Default value (%f) substituted for the entered value."),
|
|
wxString(ctl->option->label, wxConvUTF8).c_str(),
|
|
((wxTextCtrl*)ctl->control)->GetValue().c_str(),
|
|
ctl->option->default_val.floatval);
|
|
ctl->option->value.floatval = ctl->option->default_val.floatval;
|
|
}
|
|
ctl->option->value.floatval = v;
|
|
break; }
|
|
|
|
case COK_BOOL:
|
|
ctl->option->value.intval = (int)((wxCheckBox*)ctl->control)->GetValue();
|
|
break;
|
|
|
|
case COK_COLOUR:
|
|
// *FIXME* needs to be updated to use a proper color control
|
|
Auto3Free(ctl->option->value.stringval);
|
|
ctl->option->value.stringval = Auto3Strdup(((wxTextCtrl*)ctl->control)->GetValue().mb_str(wxConvUTF8));
|
|
break;
|
|
|
|
case COK_STYLE:
|
|
Auto3Free(ctl->option->value.stringval);
|
|
ctl->option->value.stringval = Auto3Strdup(((wxChoice*)ctl->control)->GetStringSelection().mb_str(wxConvUTF8));
|
|
break;
|
|
|
|
case COK_LABEL:
|
|
case COK_INVALID:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
wxString Auto3ConfigDialog::Serialise()
|
|
{
|
|
if (options->name == 0)
|
|
return _T("");
|
|
|
|
wxString result;
|
|
for (Auto3ConfigOption *opt = options; opt->name; opt++) {
|
|
wxString optname(opt->name, wxConvUTF8);
|
|
switch (opt->kind) {
|
|
case COK_TEXT:
|
|
case COK_STYLE:
|
|
case COK_COLOUR: {
|
|
wxString optstrval(opt->value.stringval, wxConvUTF8);
|
|
result << wxString::Format(_T("%s:%s|"), optname.c_str(), inline_string_encode(optstrval).c_str());
|
|
break; }
|
|
case COK_INT:
|
|
case COK_BOOL:
|
|
result << wxString::Format(_T("%s:%d|"), optname.c_str(), opt->value.intval);
|
|
break;
|
|
case COK_FLOAT:
|
|
result << wxString::Format(_T("%s:%e|"), optname.c_str(), opt->value.floatval);
|
|
break;
|
|
default:
|
|
// The rest aren't stored
|
|
break;
|
|
}
|
|
}
|
|
if (!result.IsEmpty() && result.Last() == _T('|'))
|
|
result.RemoveLast();
|
|
return result;
|
|
}
|
|
|
|
void Auto3ConfigDialog::Unserialise(const wxString &settings)
|
|
{
|
|
wxStringTokenizer toker(settings, _T("|"), wxTOKEN_STRTOK);
|
|
while (toker.HasMoreTokens()) {
|
|
// get the parts of this setting
|
|
wxString setting = toker.GetNextToken();
|
|
|
|
wxString optname = setting.BeforeFirst(_T(':'));
|
|
wxString optval = setting.AfterFirst(_T(':'));
|
|
|
|
// find the setting in the list loaded from the script
|
|
Auto3ConfigOption *opt = options;
|
|
while (opt->name && wxString(opt->name, wxConvUTF8) != optname)
|
|
opt ++;
|
|
|
|
if (opt->name) {
|
|
// ok, found the option!
|
|
switch (opt->kind) {
|
|
case COK_TEXT:
|
|
case COK_STYLE:
|
|
case COK_COLOUR:
|
|
Auto3Free(opt->value.stringval);
|
|
opt->value.stringval = Auto3Strdup(inline_string_decode(optval).mb_str(wxConvUTF8));
|
|
break;
|
|
|
|
case COK_INT:
|
|
case COK_BOOL: {
|
|
long n;
|
|
optval.ToLong(&n, 10);
|
|
opt->value.intval = n;
|
|
break; }
|
|
|
|
case COK_FLOAT: {
|
|
double v;
|
|
optval.ToDouble(&v);
|
|
opt->value.floatval = (float)v;
|
|
break; }
|
|
|
|
case COK_LABEL:
|
|
case COK_INVALID:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Auto3Filter
|
|
|
|
Auto3Filter::Auto3Filter(const wxString &_name, const wxString &_description, Auto3Interpreter *_script)
|
|
: Feature(SCRIPTFEATURE_FILTER, _name)
|
|
, FeatureFilter(_name, _description, 0)
|
|
, script(_script)
|
|
{
|
|
// Nothing more to do
|
|
}
|
|
|
|
ScriptConfigDialog* Auto3Filter::GenerateConfigDialog(wxWindow *parent)
|
|
{
|
|
config = new Auto3ConfigDialog(script);
|
|
return config;
|
|
}
|
|
|
|
void Auto3Filter::Init()
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
void Auto3Filter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
|
|
{
|
|
Auto3ProgressSink *sink = new Auto3ProgressSink(script, export_dialog);
|
|
sink->SetTitle(GetName());
|
|
Auto3ThreadedProcessor thread(script, subs, config, sink);
|
|
|
|
sink->ShowModal();
|
|
thread.Wait();
|
|
|
|
delete sink;
|
|
}
|
|
|
|
|
|
// Auto3ThreadedProcessor
|
|
|
|
Auto3ThreadedProcessor::Auto3ThreadedProcessor(Auto3Interpreter *_script, AssFile *_file, Auto3ConfigDialog *_config, Auto3ProgressSink *_sink)
|
|
: wxThread(wxTHREAD_JOINABLE)
|
|
, script(_script)
|
|
, file(_file)
|
|
, config(_config)
|
|
, sink(_sink)
|
|
{
|
|
// Pure copypasta
|
|
int prio = Options.AsInt(_T("Automation Thread Priority"));
|
|
if (prio == 0) prio = 50; // normal
|
|
else if (prio == 1) prio = 30; // below normal
|
|
else if (prio == 2) prio = 10; // lowest
|
|
else prio = 50; // fallback normal
|
|
Create();
|
|
SetPriority(prio);
|
|
Run();
|
|
}
|
|
|
|
wxThread::ExitCode Auto3ThreadedProcessor::Entry()
|
|
{
|
|
script->cb.rwdata = this;
|
|
script->cb.reset_style_pointer = ResetStylePointer;
|
|
script->cb.reset_subs_pointer = ResetSubsPointer;
|
|
script->cb.get_meta_info = GetMetaInfo;
|
|
script->cb.get_next_style = GetNextStyle;
|
|
script->cb.get_next_sub = GetNextSub;
|
|
script->cb.start_subs_write = StartSubsWrite;
|
|
script->cb.write_sub = WriteSub;
|
|
|
|
int res = RunAuto3Script(script);
|
|
|
|
script->cb.rwdata = 0;
|
|
script->cb.reset_style_pointer = 0;
|
|
script->cb.reset_subs_pointer = 0;
|
|
script->cb.get_meta_info = 0;
|
|
script->cb.get_next_style = 0;
|
|
script->cb.get_next_sub = 0;
|
|
script->cb.start_subs_write = 0;
|
|
script->cb.write_sub = 0;
|
|
|
|
sink->script_finished = true;
|
|
wxWakeUpIdle();
|
|
|
|
if (res) return (wxThread::ExitCode) 1;
|
|
else return 0;
|
|
}
|
|
|
|
|
|
void Auto3ThreadedProcessor::ResetStylePointer(void *cbdata)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
self->style_pointer = self->file->Line.begin();
|
|
}
|
|
|
|
|
|
void Auto3ThreadedProcessor::ResetSubsPointer(void *cbdata)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
self->subs_pointer = self->file->Line.begin();
|
|
}
|
|
|
|
|
|
void Auto3ThreadedProcessor::GetMetaInfo(void *cbdata, int *res_x, int *res_y)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
self->file->GetResolution(*res_x, *res_y);
|
|
}
|
|
|
|
|
|
int Auto3ThreadedProcessor::GetNextStyle(
|
|
void *cbdata, char **name, char **fontname, int *fontsize, char **color1, char **color2, char **color3, char **color4,
|
|
int *bold, int *italic, int *underline, int *strikeout, float *scale_x, float *scale_y, float *spacing, float *angle,
|
|
int *borderstyle, float *outline, float *shadow, int *align, int *margin_l, int *margin_r, int *margin_v, int *encoding)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
|
|
while (self->style_pointer != self->file->Line.end()) {
|
|
AssStyle *style = AssEntry::GetAsStyle(*self->style_pointer);
|
|
// Increase iterator before we test for and return style data, since it needs to be done either way
|
|
// The iterator should always point to the next line to be examined
|
|
self->style_pointer++;
|
|
if (style) {
|
|
// Put strings into buffers
|
|
self->stylename = style->name.mb_str(wxConvUTF8);
|
|
self->stylefont = style->font.mb_str(wxConvUTF8);
|
|
self->stylecolor[0] = style->primary.GetASSFormatted(true, false, true).mb_str(wxConvUTF8);
|
|
self->stylecolor[1] = style->secondary.GetASSFormatted(true, false, true).mb_str(wxConvUTF8);
|
|
self->stylecolor[2] = style->outline.GetASSFormatted(true, false, true).mb_str(wxConvUTF8);
|
|
self->stylecolor[3] = style->shadow.GetASSFormatted(true, false, true).mb_str(wxConvUTF8);
|
|
|
|
// Store data to lib
|
|
*name = self->stylename.data();
|
|
*fontname = self->stylefont.data();
|
|
*fontsize = (int)style->fontsize;
|
|
*color1 = self->stylecolor[0].data();
|
|
*color2 = self->stylecolor[1].data();
|
|
*color3 = self->stylecolor[2].data();
|
|
*color4 = self->stylecolor[3].data();
|
|
*bold = (int)style->bold;
|
|
*italic = (int)style->italic;
|
|
*underline = (int)style->italic;
|
|
*strikeout = (int)style->strikeout;
|
|
*scale_x = style->scalex;
|
|
*scale_y = style->scaley;
|
|
*spacing = style->spacing;
|
|
*angle = style->angle;
|
|
*borderstyle = style->borderstyle;
|
|
*outline = style->outline_w;
|
|
*shadow = style->shadow_w;
|
|
*align = style->alignment;
|
|
*margin_l = style->Margin[0];
|
|
*margin_r = style->Margin[1];
|
|
*margin_v = style->Margin[2];
|
|
*encoding = style->encoding;
|
|
|
|
// and return success
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Auto3ThreadedProcessor::GetNextSub(void *cbdata, int *layer, int *start_time, int *end_time, char **style, char **actor,
|
|
int *margin_l, int *margin_r, int *margin_v, char **effect, char **text, int *comment)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
|
|
while (self->subs_pointer != self->file->Line.end()) {
|
|
AssDialogue *dia = AssEntry::GetAsDialogue(*self->subs_pointer);
|
|
self->subs_pointer++;
|
|
if (dia) {
|
|
// Put strings into buffers
|
|
self->diagstyle = dia->Style.mb_str(wxConvUTF8);
|
|
self->diagactor = dia->Actor.mb_str(wxConvUTF8);
|
|
self->diageffect = dia->Effect.mb_str(wxConvUTF8);
|
|
self->diagtext = dia->Text.mb_str(wxConvUTF8);
|
|
|
|
// Store data to lib
|
|
*comment = (int)dia->Comment;
|
|
*layer = dia->Layer;
|
|
*start_time = dia->Start.GetMS()/10;
|
|
*end_time = dia->End.GetMS()/10;
|
|
*style = self->diagstyle.data();
|
|
*actor = self->diagactor.data();
|
|
*margin_l = dia->Margin[0];
|
|
*margin_r = dia->Margin[1];
|
|
*margin_v = dia->Margin[2];
|
|
*effect = self->diageffect.data();
|
|
*text = self->diagtext.data();
|
|
|
|
// return success
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Auto3ThreadedProcessor::StartSubsWrite(void *cbdata)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
|
|
// clear all dialogue lines
|
|
std::list<AssEntry*>::iterator line = self->file->Line.begin();
|
|
while (line != self->file->Line.end()) {
|
|
std::list<AssEntry*>::iterator cur = line;
|
|
line++;
|
|
if (AssEntry::GetAsDialogue(*cur)) {
|
|
delete *cur;
|
|
self->file->Line.erase(cur);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Auto3ThreadedProcessor::WriteSub(void *cbdata, int layer, int start_time, int end_time, const char *style, const char *actor,
|
|
int margin_l, int margin_r, int margin_v, const char *effect, const char *text, int comment)
|
|
{
|
|
Auto3ThreadedProcessor *self = (Auto3ThreadedProcessor*)cbdata;
|
|
|
|
// Construct dialogue object
|
|
AssDialogue *dia = new AssDialogue();
|
|
dia->Comment = !!comment;
|
|
dia->Layer = layer;
|
|
dia->Start.SetMS(start_time*10);
|
|
dia->End.SetMS(end_time*10);
|
|
dia->Style = wxString(style, wxConvUTF8);
|
|
dia->Actor = wxString(actor, wxConvUTF8);
|
|
dia->Margin[0] = margin_l;
|
|
dia->Margin[1] = margin_r;
|
|
dia->Margin[2] = dia->Margin[3] = margin_v;
|
|
dia->Effect = wxString(effect, wxConvUTF8);
|
|
dia->Text = wxString(text, wxConvUTF8);
|
|
|
|
// Append to file
|
|
self->file->Line.push_back(dia);
|
|
}
|
|
|
|
|
|
// Auto3Script
|
|
|
|
Auto3Script::Auto3Script(const wxString &filename)
|
|
: Script(filename)
|
|
, filter(0)
|
|
, script(0)
|
|
{
|
|
try {
|
|
Create();
|
|
}
|
|
catch (wxChar *e) {
|
|
description = e;
|
|
loaded = false;
|
|
}
|
|
}
|
|
|
|
Auto3Script::~Auto3Script()
|
|
{
|
|
if (script) Destroy();
|
|
}
|
|
|
|
void Auto3Script::TextExtents(void *cbdata, const char *text, const char *fontname, int fontsize, int bold, int italic, int spacing,
|
|
float scale_x, float scale_y, int encoding, float *out_width, float *out_height, float *out_descent, float *out_extlead)
|
|
{
|
|
double resx, resy, resd, resl;
|
|
|
|
wxString intext(text, wxConvUTF8);
|
|
|
|
AssStyle st;
|
|
|
|
st.font = wxString(fontname, wxConvUTF8);
|
|
st.fontsize = fontsize;
|
|
st.bold = !!bold;
|
|
st.italic = !!italic;
|
|
st.underline = false;
|
|
st.strikeout = false;
|
|
st.scalex = scale_x;
|
|
st.scaley = scale_y;
|
|
st.spacing = spacing;
|
|
st.encoding = encoding;
|
|
|
|
CalculateTextExtents(&st, intext, resx, resy, resd, resl);
|
|
// So no error checking here... FIXME?
|
|
|
|
*out_width = resx;
|
|
*out_height = resy;
|
|
*out_descent = resd;
|
|
*out_extlead = resl;
|
|
}
|
|
|
|
filename_t Auto3Script::ResolveInclude(void *cbdata, const char *incname)
|
|
{
|
|
Auto3Script *s = (Auto3Script*)cbdata;
|
|
|
|
wxString fnames(incname, wxConvUTF8);
|
|
|
|
wxFileName fname(fnames);
|
|
if (fname.GetDirCount() == 0) {
|
|
// filename only
|
|
fname = s->include_path.FindAbsoluteValidPath(fnames);
|
|
} else if (fname.IsRelative()) {
|
|
// relative path
|
|
wxFileName sfname(s->GetFilename());
|
|
fname.MakeAbsolute(sfname.GetPath(true));
|
|
} else {
|
|
// absolute path, do nothing
|
|
}
|
|
if (!fname.IsOk() || !fname.FileExists()) {
|
|
return 0;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
// Get number of widechars in filename string
|
|
size_t wfnlen = wcslen(fname.GetFullPath().wc_str());
|
|
// Alloc memory to hold string
|
|
filename_t wfn = (filename_t)Auto3Malloc((wfnlen+1) * sizeof(wchar_t));
|
|
// And copy string into memory
|
|
wcsncpy(wfn, fname.GetFullPath().wc_str(), wfnlen);
|
|
wfn[wfnlen] = 0;
|
|
return wfn;
|
|
#else
|
|
return Auto3Strdup(fname.GetFullPath().fn_str());
|
|
#endif
|
|
}
|
|
|
|
int Auto3Script::FrameFromMs(void *cbdata, int ms)
|
|
{
|
|
if (VFR_Output.IsLoaded()) {
|
|
return VFR_Output.GetFrameAtTime(ms, true);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int Auto3Script::MsFromFrame(void *cbdata, int frame)
|
|
{
|
|
if (VFR_Output.IsLoaded()) {
|
|
return VFR_Output.GetTimeAtFrame(frame, true);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void Auto3Script::Create()
|
|
{
|
|
Destroy();
|
|
|
|
// Fill callbacks struct
|
|
Auto3Callbacks cb;
|
|
// Logging, implemented only during script execution
|
|
cb.logdata = 0;
|
|
cb.log_error = 0;
|
|
cb.log_message = 0;
|
|
cb.set_progress = 0;
|
|
cb.set_status = 0;
|
|
// Read/write, also only during execution
|
|
cb.rwdata = 0;
|
|
cb.get_meta_info = 0;
|
|
cb.get_next_style = 0;
|
|
cb.get_next_sub = 0;
|
|
cb.reset_style_pointer = 0;
|
|
cb.reset_subs_pointer = 0;
|
|
cb.start_subs_write = 0;
|
|
cb.write_sub = 0;
|
|
// Misc, implemented all the time
|
|
cb.rundata = this;
|
|
cb.resolve_include = ResolveInclude;
|
|
cb.text_extents = TextExtents;
|
|
cb.frame_from_ms = FrameFromMs;
|
|
cb.ms_from_frame = MsFromFrame;
|
|
|
|
char *errormsg = 0;
|
|
// Why oh why... GCC wants fn_str() to be dereffed with .data() but MSVC hates that...
|
|
// If anyone can FIXME to something more sensible, please do so
|
|
#ifdef WIN32
|
|
script = CreateAuto3Script((const filename_t)GetFilename().fn_str(), GetPrettyFilename().mb_str(wxConvUTF8), &cb, &errormsg);
|
|
#else
|
|
script = CreateAuto3Script((const filename_t)GetFilename().fn_str().data(), GetPrettyFilename().mb_str(wxConvUTF8).data(), &cb, &errormsg);
|
|
#endif
|
|
|
|
if (script) {
|
|
assert(errormsg == 0);
|
|
loaded = true;
|
|
|
|
name = wxString(script->name, wxConvUTF8);
|
|
description = wxString(script->description, wxConvUTF8);
|
|
author = _T("");
|
|
version = _T("");
|
|
|
|
filter = new Auto3Filter(name, description, script);
|
|
|
|
} else {
|
|
loaded = false;
|
|
name = GetPrettyFilename();
|
|
if (errormsg) {
|
|
description = wxString(errormsg, wxConvUTF8);
|
|
Auto3Free(errormsg);
|
|
} else {
|
|
description = _T("Unknown error (auto3 library returned NULL error message)");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Auto3Script::Destroy()
|
|
{
|
|
if (!script) return;
|
|
|
|
if (filter) {
|
|
delete filter;
|
|
filter = 0;
|
|
}
|
|
|
|
DestroyAuto3Script(script);
|
|
script = 0;
|
|
|
|
loaded = false;
|
|
}
|
|
|
|
void Auto3Script::Reload()
|
|
{
|
|
Destroy();
|
|
Create();
|
|
}
|
|
|
|
|
|
// Auto3ScriptFactory
|
|
|
|
class Auto3ScriptFactory : public ScriptFactory {
|
|
public:
|
|
Auto3ScriptFactory()
|
|
{
|
|
engine_name = _T("Legacy Automation 3");
|
|
filename_pattern = _T("*.auto3");
|
|
Register(this);
|
|
}
|
|
|
|
~Auto3ScriptFactory() { }
|
|
|
|
virtual Script* Produce(const wxString &filename) const
|
|
{
|
|
if (filename.Right(6).Lower() == _T(".auto3")) {
|
|
return new Auto3Script(filename);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
Auto3ScriptFactory _auto3_script_factory;
|
|
|
|
};
|
|
|
|
#endif // WITH_AUTO3
|
|
#endif // WITH_AUTOMATION
|