Auto4 Ruby - not working yet
Originally committed to SVN as r906.
This commit is contained in:
parent
370512917c
commit
614758ac6c
4 changed files with 2025 additions and 0 deletions
596
aegisub/auto4_ruby.cpp
Normal file
596
aegisub/auto4_ruby.cpp
Normal file
|
@ -0,0 +1,596 @@
|
||||||
|
// Copyright (c) 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "auto4_ruby.h"
|
||||||
|
#include "auto4_auto3.h"
|
||||||
|
#include "ass_dialogue.h"
|
||||||
|
#include "ass_style.h"
|
||||||
|
#include "ass_file.h"
|
||||||
|
#include "ass_override.h"
|
||||||
|
#include "text_file_reader.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "../ruby/include/ruby.h"
|
||||||
|
#include <wx/msgdlg.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
|
#include <wx/filefn.h>
|
||||||
|
#include <wx/window.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace Automation4 {
|
||||||
|
|
||||||
|
// I'm not sure if this will work ?_?
|
||||||
|
RubyObjects *RubyObjects::inst = NULL;
|
||||||
|
RubyScript * RubyScript::inst = NULL; // current Ruby Script
|
||||||
|
RubyProgressSink* RubyProgressSink::inst = NULL;
|
||||||
|
VALUE RubyScript::RubyAegisub;
|
||||||
|
RubyAssFile *RubyAssFile::raf = NULL;
|
||||||
|
|
||||||
|
// RubyScriptReader
|
||||||
|
RubyScriptReader::RubyScriptReader(const wxString &filename)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
f = _tfopen(filename.c_str(), _T("rb"));
|
||||||
|
#else
|
||||||
|
f = fopen(filename.fn_str(), "rb");
|
||||||
|
#endif
|
||||||
|
first = true;
|
||||||
|
databuf = new char[bufsize];
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyScriptReader::~RubyScriptReader()
|
||||||
|
{
|
||||||
|
if (databuf)
|
||||||
|
delete databuf;
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* const char* RubyScriptReader::reader_func( void *data, size_t *size)
|
||||||
|
{
|
||||||
|
return self->databuf;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// RubyScript
|
||||||
|
|
||||||
|
RubyScript::RubyScript(const wxString &filename)
|
||||||
|
: Script(filename)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Create();
|
||||||
|
}
|
||||||
|
catch (wxChar *e) {
|
||||||
|
description = e;
|
||||||
|
loaded = false;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyScript::~RubyScript()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyScript::Create()
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
|
||||||
|
try {
|
||||||
|
#if defined(NT)
|
||||||
|
int argc = 0;
|
||||||
|
char **argv = 0;
|
||||||
|
NtInitialize(&argc, &argv);
|
||||||
|
#endif
|
||||||
|
ruby_init();
|
||||||
|
ruby_init_loadpath();
|
||||||
|
RubyScript::inst = this;
|
||||||
|
RubyAegisub = rb_define_module("Aegisub");
|
||||||
|
rb_define_module_function(RubyAegisub, "register_macro",reinterpret_cast<RB_HOOK>(&RubyFeatureMacro::RubyRegister), 4);
|
||||||
|
rb_define_module_function(RubyAegisub, "register_filter",reinterpret_cast<RB_HOOK>(&RubyFeatureFilter::RubyRegister), 5);
|
||||||
|
rb_define_module_function(RubyAegisub, "text_extents",reinterpret_cast<RB_HOOK>(&RubyTextExtents), 2);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "progress_set",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetProgress), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "progress_task",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetTask), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "progress_title",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubySetTitle), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "debug_out",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyDebugOut), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "get_cancelled",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyGetCancelled), 0);
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
wxCharBuffer buf = GetFilename().mb_str(wxConvISO8859_1);
|
||||||
|
const char *t = buf.data();
|
||||||
|
|
||||||
|
rb_protect(rbLoadWrapper, rb_str_new2(t), &status);
|
||||||
|
if(status > 0) // something bad happened (probably parsing error)
|
||||||
|
{
|
||||||
|
throw StringValueCStr(ruby_errinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE global_var = rb_gv_get("$script_name");
|
||||||
|
name = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||||
|
global_var = rb_gv_get("$script_description");
|
||||||
|
description = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||||
|
global_var = rb_gv_get("$script_author");
|
||||||
|
author = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||||
|
global_var = rb_gv_get("$script_version");
|
||||||
|
version = wxString(StringValueCStr(global_var), wxConvUTF8);
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
catch (const char* e) {
|
||||||
|
Destroy();
|
||||||
|
loaded = false;
|
||||||
|
wxString *err = new wxString(e, wxConvUTF8);
|
||||||
|
throw err->c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyScript::Destroy()
|
||||||
|
{
|
||||||
|
if(loaded) {
|
||||||
|
ruby_finalize();
|
||||||
|
ruby_cleanup(0);
|
||||||
|
}
|
||||||
|
// TODO: would be nice to implement this
|
||||||
|
// RubyObjects::Get()->UnregisterAll();
|
||||||
|
|
||||||
|
// remove features
|
||||||
|
for (int i = 0; i < (int)features.size(); i++) {
|
||||||
|
Feature *f = features[i];
|
||||||
|
delete f;
|
||||||
|
}
|
||||||
|
features.clear();
|
||||||
|
loaded = false;
|
||||||
|
RubyScript::inst = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyScript::Reload()
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyScript* RubyScript::GetScriptObject()
|
||||||
|
{
|
||||||
|
return RubyScript::inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VALUE RubyScript::RubyTextExtents(VALUE self, VALUE _style, VALUE _text)
|
||||||
|
{
|
||||||
|
if(TYPE(_style) != T_HASH)
|
||||||
|
rb_raise(rb_eRuntimeError, "text_extents: Style parameter must be a hash");
|
||||||
|
|
||||||
|
AssEntry *et = RubyAssFile::RubyToAssEntry(_style);
|
||||||
|
AssStyle *st = dynamic_cast<AssStyle*>(et);
|
||||||
|
if (!st) {
|
||||||
|
delete et; // Make sure to delete the "live" pointer
|
||||||
|
rb_raise(rb_eRuntimeError, "Not a style entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString text(StringValueCStr(_text), wxConvUTF8);
|
||||||
|
|
||||||
|
double width, height, descent, extlead;
|
||||||
|
if (!CalculateTextExtents(st, text, width, height, descent, extlead)) {
|
||||||
|
delete st;
|
||||||
|
rb_raise(rb_eRuntimeError, "Some internal error occurred calculating text_extents");
|
||||||
|
}
|
||||||
|
delete st;
|
||||||
|
|
||||||
|
VALUE result = rb_ary_new3(4, rb_float_new(width), rb_float_new(height), rb_float_new(descent), rb_float_new(extlead));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// RubyFeature
|
||||||
|
|
||||||
|
RubyFeature::RubyFeature(ScriptFeatureClass _featureclass, const wxString &_name)
|
||||||
|
: Feature(_featureclass, _name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyFeature::RegisterFeature()
|
||||||
|
{
|
||||||
|
RubyScript::GetScriptObject()->features.push_back(this);
|
||||||
|
|
||||||
|
// get the index+1 it was pushed into
|
||||||
|
myid = (int)RubyScript::GetScriptObject()->features.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyFeature::CreateIntegerArray(const std::vector<int> &ints)
|
||||||
|
{
|
||||||
|
VALUE res = rb_ary_new2(ints.size());
|
||||||
|
// create an array-style table with an integer vector in it
|
||||||
|
// leave the new table on top of the stack
|
||||||
|
for (int i = 0; i != ints.size(); ++i) {
|
||||||
|
int k = ints[i];
|
||||||
|
rb_ary_push(res, rb_int2inum(k));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyFeature::ThrowError()
|
||||||
|
{
|
||||||
|
/* wxString err(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
wxLogError(err);
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
|
||||||
|
// RubyFeatureMacro
|
||||||
|
|
||||||
|
VALUE RubyFeatureMacro::RubyRegister(VALUE self, VALUE name, VALUE description, VALUE macro_function, VALUE validate_function)
|
||||||
|
{
|
||||||
|
wxString _name(StringValueCStr(name), wxConvUTF8);
|
||||||
|
wxString _description(StringValueCStr(description), wxConvUTF8);
|
||||||
|
RubyFeatureMacro *macro = new RubyFeatureMacro(_name, _description, macro_function, validate_function);
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyFeatureMacro::RubyFeatureMacro(const wxString &_name, const wxString &_description, VALUE macro_function, VALUE validate_function)
|
||||||
|
: Feature(SCRIPTFEATURE_MACRO, _name)
|
||||||
|
, FeatureMacro(_name, _description)
|
||||||
|
, RubyFeature(SCRIPTFEATURE_MACRO, _name)
|
||||||
|
, macro_fun(macro_function)
|
||||||
|
, validation_fun(validate_function)
|
||||||
|
{
|
||||||
|
RegisterFeature();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RubyFeatureMacro::Validate(AssFile *subs, const std::vector<int> &selected, int active)
|
||||||
|
{
|
||||||
|
if (no_validate)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
|
||||||
|
VALUE sel = CreateIntegerArray(selected); // selected items
|
||||||
|
// RubyObjects::Get()->Register(sel);
|
||||||
|
VALUE result = rbFunCall(rb_mKernel, rb_to_id(validation_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active));
|
||||||
|
if(result != Qnil && result != Qfalse)
|
||||||
|
return true;
|
||||||
|
// RubyObjects::Get()->Unregister(sel);
|
||||||
|
}catch (const char* e) {
|
||||||
|
wxString *err = new wxString(e, wxConvUTF8);
|
||||||
|
throw err->c_str();
|
||||||
|
}
|
||||||
|
// wxMessageBox(error, _T("Error running validation function"), wxICON_ERROR | wxOK);
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyFeatureMacro::Process(AssFile *subs, const std::vector<int> &selected, int active, wxWindow * const progress_parent)
|
||||||
|
{
|
||||||
|
// prepare function call
|
||||||
|
try {
|
||||||
|
|
||||||
|
RubyProgressSink::inst = new RubyProgressSink(progress_parent, false);
|
||||||
|
RubyProgressSink::inst->SetTitle(GetName());
|
||||||
|
RubyProgressSink::inst->Show(true);
|
||||||
|
|
||||||
|
// do call
|
||||||
|
RubyAssFile *subsobj = new RubyAssFile(subs, true, true);
|
||||||
|
VALUE sel = CreateIntegerArray(selected); // selected items
|
||||||
|
RubyObjects::Get()->Register(sel);
|
||||||
|
VALUE result = rbFunCall(rb_mKernel, rb_to_id(macro_fun), 3, subsobj->rbAssFile, sel, rb_int2inum(active));
|
||||||
|
if(result != Qnil && result != Qfalse)
|
||||||
|
{
|
||||||
|
subsobj->RubyUpdateAssFile(result);
|
||||||
|
RubyProgressSink::inst->script_finished = true;
|
||||||
|
}
|
||||||
|
else RubyProgressSink::inst->Show(false);
|
||||||
|
// RubyObjects::Get()->Unregister(sel);
|
||||||
|
} catch (const char* e) {
|
||||||
|
wxString *err = new wxString(e, wxConvUTF8);
|
||||||
|
wxMessageBox(*err, _T("Error running macro"),wxICON_ERROR | wxOK);
|
||||||
|
}
|
||||||
|
delete RubyProgressSink::inst;
|
||||||
|
RubyProgressSink::inst = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// RubyFeatureFilter
|
||||||
|
RubyFeatureFilter::RubyFeatureFilter(const wxString &_name, const wxString &_description,
|
||||||
|
int merit, VALUE _filter_fun, VALUE _dialog_fun)
|
||||||
|
: Feature(SCRIPTFEATURE_FILTER, _name)
|
||||||
|
, FeatureFilter(_name, _description, merit)
|
||||||
|
, RubyFeature(SCRIPTFEATURE_FILTER, _name)
|
||||||
|
, filter_fun(_filter_fun)
|
||||||
|
, dialog_fun(_dialog_fun)
|
||||||
|
{
|
||||||
|
has_config = _dialog_fun != Qnil;
|
||||||
|
// Works the same as in RubyFeatureMacro
|
||||||
|
RegisterFeature();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyFeatureFilter::Init()
|
||||||
|
{
|
||||||
|
// Don't think there's anything to do here... (empty in auto3)
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyFeatureFilter::RubyRegister(VALUE self, VALUE name, VALUE description, VALUE merit, VALUE function, VALUE dialog)
|
||||||
|
{
|
||||||
|
wxString _name(StringValueCStr(name), wxConvUTF8);
|
||||||
|
wxString _description(StringValueCStr(description), wxConvUTF8);
|
||||||
|
int _merit = rb_num2long(merit);
|
||||||
|
RubyFeatureFilter *filter = new RubyFeatureFilter(_name, _description, _merit, function, dialog);
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyFeatureFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO: configuration dialog
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (has_config && config_dialog) {
|
||||||
|
assert(config_dialog->RubyReadBack() == 1);
|
||||||
|
// TODO, write back stored options here
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
|
||||||
|
RubyProgressSink::inst->SetTitle(GetName());
|
||||||
|
RubyProgressSink::inst->Show(true);
|
||||||
|
|
||||||
|
RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/);
|
||||||
|
VALUE result = rbFunCall(rb_mKernel, rb_to_id(filter_fun), 2, subsobj->rbAssFile, Qnil /* config */);
|
||||||
|
if(result != Qnil && result != Qfalse)
|
||||||
|
{
|
||||||
|
subsobj->RubyUpdateAssFile(result);
|
||||||
|
}
|
||||||
|
// else RubyProgressSink::inst->Show(false);
|
||||||
|
} catch (const char* e) {
|
||||||
|
wxString *err = new wxString(e, wxConvUTF8);
|
||||||
|
wxMessageBox(*err, _T("Error running filter"),wxICON_ERROR | wxOK);
|
||||||
|
}
|
||||||
|
delete RubyProgressSink::inst;
|
||||||
|
RubyProgressSink::inst = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptConfigDialog* RubyFeatureFilter::GenerateConfigDialog(wxWindow *parent)
|
||||||
|
{
|
||||||
|
if (!has_config)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
//GetFeatureFunction(2); // 2 = config dialog function
|
||||||
|
|
||||||
|
// prepare function call
|
||||||
|
// subtitles (don't allow any modifications during dialog creation, ideally the subs aren't even accessed)
|
||||||
|
// RubyAssFile *subsobj = new RubyAssFile(AssFile::top, false/*allow modifications*/, false/*disallow undo*/);
|
||||||
|
// stored options
|
||||||
|
|
||||||
|
/* if(RubyProgressSink::inst)
|
||||||
|
{
|
||||||
|
delete RubyProgressSink::inst;
|
||||||
|
RubyProgressSink::inst = NULL;
|
||||||
|
}
|
||||||
|
RubyProgressSink::inst = new RubyProgressSink(parent, false);
|
||||||
|
RubyProgressSink::inst->SetTitle(GetName());
|
||||||
|
|
||||||
|
// do call TODO
|
||||||
|
RubyProgressSink::inst->ShowModal();
|
||||||
|
|
||||||
|
*/ return config_dialog = new RubyConfigDialog(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// RubyProgressSink
|
||||||
|
|
||||||
|
RubyProgressSink::RubyProgressSink(wxWindow *parent, bool allow_config_dialog)
|
||||||
|
: ProgressSink(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyProgressSink::~RubyProgressSink()
|
||||||
|
{
|
||||||
|
rb_undef_method(RubyScript::RubyAegisub, "progress_set");
|
||||||
|
rb_undef_method(RubyScript::RubyAegisub, "progress_task");
|
||||||
|
rb_undef_method(RubyScript::RubyAegisub, "progress_title");
|
||||||
|
rb_undef_method(RubyScript::RubyAegisub, "debug_out");
|
||||||
|
// remove progress reporting stuff
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyProgressSink::RubySetProgress(VALUE self, VALUE progress)
|
||||||
|
{
|
||||||
|
float _progr = rb_num2dbl(progress);
|
||||||
|
RubyProgressSink::inst->SetProgress(_progr);
|
||||||
|
RubyProgressSink::inst->DoUpdateDisplay();
|
||||||
|
wxSafeYield(RubyProgressSink::inst);
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyProgressSink::RubySetTask(VALUE self, VALUE task)
|
||||||
|
{
|
||||||
|
wxString _t(StringValueCStr(task), wxConvUTF8);
|
||||||
|
RubyProgressSink::inst->SetTask(_t);
|
||||||
|
RubyProgressSink::inst->DoUpdateDisplay();
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyProgressSink::RubySetTitle(VALUE self, VALUE title)
|
||||||
|
{
|
||||||
|
wxString _t(StringValueCStr(title), wxConvUTF8);
|
||||||
|
RubyProgressSink::inst->SetTitle(_t);
|
||||||
|
//wxSafeYield(RubyProgressSink::inst);
|
||||||
|
RubyProgressSink::inst->DoUpdateDisplay();
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyProgressSink::RubyGetCancelled(VALUE self)
|
||||||
|
{
|
||||||
|
if(RubyProgressSink::inst->cancelled)
|
||||||
|
return Qtrue;
|
||||||
|
return Qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE RubyProgressSink::RubyDebugOut(VALUE self, VALUE msg)
|
||||||
|
{
|
||||||
|
wxString _m(StringValueCStr(msg), wxConvUTF8);
|
||||||
|
RubyProgressSink::inst->AddDebugOutput(_m);
|
||||||
|
RubyProgressSink::inst->Refresh();
|
||||||
|
RubyProgressSink::inst->Update();
|
||||||
|
return Qtrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyProgressSink::RubyDisplayDialog()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Factory class for Ruby scripts
|
||||||
|
// Not declared in header, since it doesn't need to be accessed from outside
|
||||||
|
// except through polymorphism
|
||||||
|
class RubyScriptFactory : public ScriptFactory {
|
||||||
|
public:
|
||||||
|
RubyScriptFactory()
|
||||||
|
{
|
||||||
|
engine_name = _T("Ruby");
|
||||||
|
filename_pattern = _T("*.rb");
|
||||||
|
Register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RubyScriptFactory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Script* Produce(const wxString &filename) const
|
||||||
|
{
|
||||||
|
// Just check if file extension is .rb
|
||||||
|
// Reject anything else
|
||||||
|
if (filename.Right(3).Lower() == _T(".rb")) {
|
||||||
|
return new RubyScript(filename);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RubyScriptFactory _script_factory;
|
||||||
|
|
||||||
|
RubyObjects::RubyObjects()
|
||||||
|
{
|
||||||
|
objects = rb_ary_new();
|
||||||
|
rb_gc_register_address(&objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyObjects::~RubyObjects()
|
||||||
|
{
|
||||||
|
rb_gc_unregister_address(&objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyObjects *RubyObjects::Get()
|
||||||
|
{
|
||||||
|
if(inst)
|
||||||
|
return inst;
|
||||||
|
else
|
||||||
|
inst = new RubyObjects;
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyObjects::Register(VALUE obj) {
|
||||||
|
rb_ary_push(objects, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyObjects::Unregister(VALUE obj) {
|
||||||
|
rb_ary_delete(objects, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyCallArguments::RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv)
|
||||||
|
:id(_id), n(_n), argv(_argv)
|
||||||
|
{
|
||||||
|
recv = _recv;
|
||||||
|
};
|
||||||
|
typedef struct run_safely_arg {
|
||||||
|
VALUE (*func)(void);
|
||||||
|
void *arg;
|
||||||
|
} run_safely_arg_t;
|
||||||
|
|
||||||
|
static VALUE run_safely_0(void *arg)
|
||||||
|
{
|
||||||
|
run_safely_arg_t *rsarg = (run_safely_arg_t *) arg;
|
||||||
|
VALUE result;
|
||||||
|
result = (*rsarg->func)();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_safely(VALUE (*func)(void), void *arg, VALUE *retval)
|
||||||
|
{
|
||||||
|
VALUE thread, ret;
|
||||||
|
run_safely_arg_t rsarg;
|
||||||
|
int state;
|
||||||
|
rsarg.func = func;
|
||||||
|
rsarg.arg = arg;
|
||||||
|
#if defined(HAVE_SETITIMER)
|
||||||
|
rb_thread_start_timer();
|
||||||
|
#endif
|
||||||
|
thread = rb_thread_create(reinterpret_cast<RB_HOOK>(&run_safely_0), &rsarg);
|
||||||
|
rb_thread_kill(thread);
|
||||||
|
#if defined(HAVE_SETITIMER)
|
||||||
|
rb_thread_stop_timer();
|
||||||
|
#endif
|
||||||
|
if (retval)
|
||||||
|
*retval = ret;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
VALUE rbCallWrapper(VALUE arg)
|
||||||
|
{
|
||||||
|
RubyCallArguments &a = *reinterpret_cast<RubyCallArguments*>(arg);
|
||||||
|
return rb_funcall2(a.recv, a.id, a.n, a.argv);
|
||||||
|
}
|
||||||
|
VALUE rbExecWrapper(VALUE arg){return ruby_exec();}
|
||||||
|
VALUE rbLoadWrapper(VALUE arg){rb_load(/*reinterpret_cast<const char*>*/(arg), 0); return Qtrue;}
|
||||||
|
|
||||||
|
VALUE rbFunCall(VALUE recv, ID id, int n, ...)
|
||||||
|
{
|
||||||
|
VALUE *argv = 0;
|
||||||
|
if (n > 0) {
|
||||||
|
argv = ALLOCA_N(VALUE, n);
|
||||||
|
va_list ar;
|
||||||
|
va_start(ar, n);
|
||||||
|
int i;
|
||||||
|
for(i=0;i<n;i++) {
|
||||||
|
argv[i] = va_arg(ar, VALUE);
|
||||||
|
}
|
||||||
|
va_end(ar);
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyCallArguments arg(recv, id, n, argv);
|
||||||
|
int error = 0;
|
||||||
|
VALUE result;
|
||||||
|
result = rb_protect(rbCallWrapper, reinterpret_cast<VALUE>(&arg), &error);
|
||||||
|
if(error) {
|
||||||
|
throw StringValueCStr(ruby_errinfo);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
259
aegisub/auto4_ruby.h
Normal file
259
aegisub/auto4_ruby.h
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
// Copyright (c) 2006, 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef _AUTO4_RUBY_H
|
||||||
|
#define _AUTO4_RUBY_H
|
||||||
|
|
||||||
|
#include "auto4_base.h"
|
||||||
|
#include <wx/thread.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
#include "../ruby/include/ruby.h"
|
||||||
|
|
||||||
|
class wxWindow;
|
||||||
|
|
||||||
|
namespace Automation4 {
|
||||||
|
|
||||||
|
|
||||||
|
// Manage reading in a Ruby script file
|
||||||
|
struct RubyScriptReader {
|
||||||
|
FILE *f;
|
||||||
|
bool first;
|
||||||
|
char *databuf;
|
||||||
|
static const size_t bufsize = 512;
|
||||||
|
RubyScriptReader(const wxString &filename);
|
||||||
|
~RubyScriptReader();
|
||||||
|
// static const char* reader_func( void *data, size_t *size);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Provides access to an AssFile object (and all lines contained) for a Ruby script
|
||||||
|
class RubyAssFile {
|
||||||
|
private:
|
||||||
|
AssFile *ass;
|
||||||
|
|
||||||
|
bool can_modify;
|
||||||
|
bool can_set_undo;
|
||||||
|
//void CheckAllowModify(); // throws an error if modification is disallowed
|
||||||
|
|
||||||
|
// keep a cursor of last accessed item to avoid walking over the entire file on every access
|
||||||
|
std::list<AssEntry*>::iterator last_entry_ptr;
|
||||||
|
int last_entry_id;
|
||||||
|
void GetAssEntry(int n); // set last_entry_ptr to point to item n
|
||||||
|
|
||||||
|
static int RubyParseTagData();
|
||||||
|
static int RubyUnparseTagData();
|
||||||
|
static int RubyParseKaraokeData();
|
||||||
|
static int RubySetUndoPoint();
|
||||||
|
|
||||||
|
~RubyAssFile();
|
||||||
|
public:
|
||||||
|
void RubyUpdateAssFile(VALUE subtitles);
|
||||||
|
static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry
|
||||||
|
static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation
|
||||||
|
RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo);
|
||||||
|
|
||||||
|
static RubyAssFile *raf;
|
||||||
|
VALUE rbAssFile;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Provides progress UI and control functions for a Ruby script
|
||||||
|
class RubyProgressSink : public ProgressSink {
|
||||||
|
private:
|
||||||
|
static int RubyDisplayDialog();
|
||||||
|
|
||||||
|
public:
|
||||||
|
RubyProgressSink(wxWindow *parent, bool allow_config_dialog = true);
|
||||||
|
virtual ~RubyProgressSink();
|
||||||
|
static RubyProgressSink* inst;
|
||||||
|
static VALUE RubySetProgress(VALUE self, VALUE progress);
|
||||||
|
static VALUE RubySetTask(VALUE self, VALUE task);
|
||||||
|
static VALUE RubySetTitle(VALUE self, VALUE title);
|
||||||
|
static VALUE RubyGetCancelled(VALUE self);
|
||||||
|
static VALUE RubyDebugOut(VALUE self, VALUE msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Provides Config UI functions for a Ruby script
|
||||||
|
class RubyConfigDialogControl {
|
||||||
|
public:
|
||||||
|
wxControl *cw; // control window
|
||||||
|
wxString name, hint;
|
||||||
|
int x, y, width, height;
|
||||||
|
|
||||||
|
virtual wxControl *Create(wxWindow *parent) = 0;
|
||||||
|
virtual void ControlReadBack() = 0;
|
||||||
|
virtual void RubyReadBack() = 0;
|
||||||
|
|
||||||
|
RubyConfigDialogControl();
|
||||||
|
virtual ~RubyConfigDialogControl() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
class RubyConfigDialog : public ScriptConfigDialog {
|
||||||
|
private:
|
||||||
|
std::vector<RubyConfigDialogControl*> controls;
|
||||||
|
std::vector<wxString> buttons;
|
||||||
|
bool use_buttons;
|
||||||
|
|
||||||
|
class ButtonEventHandler : public wxEvtHandler {
|
||||||
|
public:
|
||||||
|
int *button_pushed;
|
||||||
|
void OnButtonPush(wxCommandEvent &evt);
|
||||||
|
};
|
||||||
|
|
||||||
|
ButtonEventHandler *button_event;
|
||||||
|
int button_pushed;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
wxWindow* CreateWindow(wxWindow *parent);
|
||||||
|
|
||||||
|
public:
|
||||||
|
RubyConfigDialog(bool include_buttons);
|
||||||
|
virtual ~RubyConfigDialog();
|
||||||
|
int RubyReadBack(); // read back internal structure to lua structures
|
||||||
|
|
||||||
|
void ReadBack(); // from auto4 base
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Second base-class for Ruby implemented Features
|
||||||
|
class RubyFeature : public virtual Feature {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
int myid;
|
||||||
|
|
||||||
|
RubyFeature(ScriptFeatureClass _featureclass, const wxString &_name);
|
||||||
|
|
||||||
|
void RegisterFeature();
|
||||||
|
VALUE CreateIntegerArray(const std::vector<int> &ints);
|
||||||
|
void ThrowError();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Class of Ruby scripts
|
||||||
|
class RubyScript : public Script {
|
||||||
|
friend class RubyFeature;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Create(); // load script and create internal structures etc.
|
||||||
|
void Destroy(); // destroy internal structures, unreg features and delete environment
|
||||||
|
|
||||||
|
static RubyScript* GetScriptObject();
|
||||||
|
static RubyScript* inst;
|
||||||
|
static VALUE RubyTextExtents(VALUE self, VALUE style, VALUE text);
|
||||||
|
static int RubyInclude();
|
||||||
|
|
||||||
|
public:
|
||||||
|
RubyScript(const wxString &filename);
|
||||||
|
virtual ~RubyScript();
|
||||||
|
virtual void Reload();
|
||||||
|
static VALUE RubyAegisub;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation of the Macro Feature for Ruby scripts
|
||||||
|
class RubyFeatureMacro : public FeatureMacro, RubyFeature {
|
||||||
|
private:
|
||||||
|
bool no_validate;
|
||||||
|
VALUE macro_fun;
|
||||||
|
VALUE validation_fun;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RubyFeatureMacro(const wxString &_name, const wxString &_description, VALUE macro_function, VALUE validate_function);
|
||||||
|
public:
|
||||||
|
static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE macro_function, VALUE validate_function);
|
||||||
|
virtual ~RubyFeatureMacro() { }
|
||||||
|
|
||||||
|
virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active);
|
||||||
|
virtual void Process(AssFile *subs, const std::vector<int> &selected, int active, wxWindow * const progress_parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Implementation of the Export Filter Feature for Ruby scripts
|
||||||
|
class RubyFeatureFilter : public FeatureFilter, RubyFeature {
|
||||||
|
private:
|
||||||
|
bool has_config;
|
||||||
|
RubyConfigDialog *config_dialog;
|
||||||
|
VALUE filter_fun;
|
||||||
|
VALUE dialog_fun;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RubyFeatureFilter(const wxString &_name, const wxString &_description, int merit, VALUE function, VALUE dialog);
|
||||||
|
|
||||||
|
ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent);
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
public:
|
||||||
|
static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE merit, VALUE macro_function, VALUE validate_function);
|
||||||
|
|
||||||
|
void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
|
||||||
|
};
|
||||||
|
|
||||||
|
// class for registering ruby objects created in c++
|
||||||
|
// so garbage collector doesn't destroy them
|
||||||
|
class RubyObjects {
|
||||||
|
private:
|
||||||
|
VALUE objects;
|
||||||
|
static RubyObjects *inst;
|
||||||
|
RubyObjects();
|
||||||
|
public:
|
||||||
|
~RubyObjects();
|
||||||
|
static RubyObjects *Get();
|
||||||
|
void Register(VALUE object);
|
||||||
|
void Unregister(VALUE object);
|
||||||
|
};
|
||||||
|
|
||||||
|
// stuff for safe calling of ruby functions
|
||||||
|
struct RubyCallArguments {
|
||||||
|
VALUE recv;
|
||||||
|
ID id;
|
||||||
|
int n;
|
||||||
|
VALUE *argv;
|
||||||
|
RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv);
|
||||||
|
};
|
||||||
|
|
||||||
|
VALUE rbCallWrapper(VALUE arg);
|
||||||
|
VALUE rbExecWrapper(VALUE arg);
|
||||||
|
VALUE rbLoadWrapper(VALUE arg);
|
||||||
|
VALUE rbFunCall(VALUE recv, ID id, int n, ...);
|
||||||
|
typedef VALUE (*RB_HOOK)(...);
|
||||||
|
typedef VALUE (*RB_HOOK2)(void*);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
565
aegisub/auto4_ruby_assfile.cpp
Normal file
565
aegisub/auto4_ruby_assfile.cpp
Normal file
|
@ -0,0 +1,565 @@
|
||||||
|
// Copyright (c) 2006, 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "auto4_ruby.h"
|
||||||
|
#include "ass_dialogue.h"
|
||||||
|
#include "ass_style.h"
|
||||||
|
#include "ass_file.h"
|
||||||
|
#include "ass_override.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "../ruby/include/ruby.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Automation4 {
|
||||||
|
|
||||||
|
// LuaAssFile
|
||||||
|
|
||||||
|
/* void RubyAssFile::CheckAllowModify()
|
||||||
|
{
|
||||||
|
if (can_modify)
|
||||||
|
return;
|
||||||
|
rb_raise(rb_eRuntimeError, "Attempt to modify subtitles in read-only feature context.");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
VALUE RubyAssFile::AssEntryToRuby(AssEntry *e)
|
||||||
|
{
|
||||||
|
VALUE ass_entry;
|
||||||
|
ass_entry = rb_hash_new();
|
||||||
|
|
||||||
|
wxString section(e->group);
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("section"), rb_str_new2(e->group.mb_str(wxConvUTF8)));
|
||||||
|
wxString raw(e->GetEntryData());
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("raw"), rb_str_new2(e->GetEntryData().mb_str(wxConvUTF8)));
|
||||||
|
VALUE entry_class;
|
||||||
|
|
||||||
|
if (raw.Trim().IsEmpty()) {
|
||||||
|
entry_class = rb_str_new2("clear");
|
||||||
|
|
||||||
|
} else if (raw[0] == _T(';')) {
|
||||||
|
// "text" field, same as "raw" but with semicolon stripped
|
||||||
|
wxString text(raw, 1, raw.size()-1);
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("text"), rb_str_new2(text.mb_str(wxConvUTF8)));
|
||||||
|
entry_class = rb_str_new2("comment");
|
||||||
|
} else if (raw[0] == _T('[')) {
|
||||||
|
entry_class = rb_str_new2("head");
|
||||||
|
|
||||||
|
} else if (section.Lower() == _T("[script info]")) {
|
||||||
|
// assumed "info" class
|
||||||
|
// first "key"
|
||||||
|
wxString key = raw.BeforeFirst(_T(':'));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("key"), rb_str_new2(key.mb_str(wxConvUTF8)));
|
||||||
|
|
||||||
|
// then "value"
|
||||||
|
wxString value = raw.AfterFirst(_T(':'));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("value"), rb_str_new2(value.mb_str(wxConvUTF8)));
|
||||||
|
entry_class = rb_str_new2("info");
|
||||||
|
|
||||||
|
} else if (raw.Left(7).Lower() == _T("format:")) {
|
||||||
|
// TODO: parse the format line; just use a tokenizer
|
||||||
|
entry_class = rb_str_new2("format");
|
||||||
|
|
||||||
|
} else if (e->GetType() == ENTRY_DIALOGUE) {
|
||||||
|
AssDialogue *dia = e->GetAsDialogue(e);
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("comment"), rb_int2inum((int)dia->Comment));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("layer"), rb_int2inum(dia->Layer));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("start_time"), rb_int2inum(dia->Start.GetMS()));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("end_time"), rb_int2inum(dia->End.GetMS()));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("style"), rb_str_new2(dia->Style.mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("actor"), rb_str_new2(dia->Actor.mb_str(wxConvUTF8)));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_l"), rb_int2inum(dia->Margin[0]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_r"), rb_int2inum(dia->Margin[1]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_t"), rb_int2inum(dia->Margin[2]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_b"), rb_int2inum(dia->Margin[3]));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("effect"), rb_str_new2(dia->Effect.mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("userdata"), rb_str_new2(""));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("text"), rb_str_new2(dia->Text.mb_str(wxConvUTF8)));
|
||||||
|
|
||||||
|
entry_class = rb_str_new2("dialogue");
|
||||||
|
|
||||||
|
} else if (e->GetType() == ENTRY_STYLE) {
|
||||||
|
AssStyle *sty = e->GetAsStyle(e);
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("name"), rb_str_new2(sty->name.mb_str(wxConvUTF8)));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("fontname"), rb_str_new2(sty->font.mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("fontsize"), rb_int2inum(sty->fontsize));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("color1"), rb_str_new2(sty->primary.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("color2"), rb_str_new2(sty->secondary.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("color3"), rb_str_new2(sty->outline.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("color4"), rb_str_new2(sty->shadow.GetASSFormatted(true).mb_str(wxConvUTF8)));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("bold"), rb_int2inum(sty->bold));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("italic"), rb_int2inum(sty->italic));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("underline"), rb_int2inum(sty->underline));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("strikeout"), rb_int2inum(sty->strikeout));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("scale_x"), rb_int2inum(sty->scalex));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("scale_y"), rb_int2inum(sty->scaley));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("spacing"), rb_int2inum(sty->spacing));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("angle"), rb_int2inum(sty->angle));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("borderstyle"), rb_int2inum(sty->borderstyle));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("outline"), rb_int2inum(sty->outline_w));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("shadow"), rb_int2inum(sty->shadow_w));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("align"), rb_int2inum(sty->alignment));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_l"), rb_int2inum(sty->Margin[0]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_r"), rb_int2inum(sty->Margin[1]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_t"), rb_int2inum(sty->Margin[2]));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("margin_b"), rb_int2inum(sty->Margin[3]));
|
||||||
|
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("encoding"), rb_int2inum(sty->encoding));
|
||||||
|
|
||||||
|
// From STS.h: "0: window, 1: video, 2: undefined (~window)"
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("relative_to"), rb_int2inum(2));
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("vertical"), Qfalse);
|
||||||
|
|
||||||
|
entry_class = rb_str_new2("style");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
entry_class = rb_str_new2("unknown");
|
||||||
|
}
|
||||||
|
// store class of item; last thing done for each class specific code must be pushing the class name
|
||||||
|
rb_hash_aset(ass_entry, rb_str_new2("class"), entry_class);
|
||||||
|
|
||||||
|
return ass_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssEntry *RubyAssFile::RubyToAssEntry(VALUE ass_entry)
|
||||||
|
{
|
||||||
|
VALUE entry_class = rb_hash_aref(ass_entry, rb_str_new2("class"));
|
||||||
|
|
||||||
|
wxString lclass(StringValueCStr(entry_class), wxConvUTF8);
|
||||||
|
lclass.MakeLower();
|
||||||
|
|
||||||
|
VALUE _section = rb_hash_aref(ass_entry, rb_str_new2("section"));
|
||||||
|
wxString section(StringValueCStr(_section), wxConvUTF8);
|
||||||
|
|
||||||
|
AssEntry *result;
|
||||||
|
if (lclass == _T("clear")) {
|
||||||
|
result = new AssEntry(_T(""));
|
||||||
|
result->group = section;
|
||||||
|
|
||||||
|
} else if (lclass == _T("comment")) {
|
||||||
|
// GETSTRING(raw, "text", "comment")
|
||||||
|
VALUE _text = rb_hash_aref(ass_entry, rb_str_new2("text"));
|
||||||
|
wxString raw(StringValueCStr(_text), wxConvUTF8);
|
||||||
|
raw.Prepend(_T(";"));
|
||||||
|
result = new AssEntry(raw);
|
||||||
|
result->group = section;
|
||||||
|
|
||||||
|
} else if (lclass == _T("head")) {
|
||||||
|
result = new AssEntry(section);
|
||||||
|
result->group = section;
|
||||||
|
|
||||||
|
} else if (lclass == _T("info")) {
|
||||||
|
VALUE _key = rb_hash_aref(ass_entry, rb_str_new2("key"));
|
||||||
|
wxString key(StringValueCStr(_key), wxConvUTF8);
|
||||||
|
VALUE _value = rb_hash_aref(ass_entry, rb_str_new2("value"));
|
||||||
|
wxString value(StringValueCStr(_value), wxConvUTF8);
|
||||||
|
result = new AssEntry(wxString::Format(_T("%s: %s"), key.c_str(), value.c_str()));
|
||||||
|
result->group = _T("[Script Info]"); // just so it can be read correctly back
|
||||||
|
|
||||||
|
} else if (lclass == _T("format")) {
|
||||||
|
// ohshi- ...
|
||||||
|
// *FIXME* maybe ignore the actual data and just put some default stuff based on section?
|
||||||
|
result = new AssEntry(_T("Format: Auto4,Is,Broken"));
|
||||||
|
result->group = section;
|
||||||
|
|
||||||
|
} else if (lclass == _T("style")) {
|
||||||
|
VALUE _name = rb_hash_aref(ass_entry, rb_str_new2("name"));
|
||||||
|
wxString name(StringValueCStr(_name), wxConvUTF8);
|
||||||
|
VALUE _fontname = rb_hash_aref(ass_entry, rb_str_new2("fontname"));
|
||||||
|
wxString fontname(StringValueCStr(_fontname), wxConvUTF8);
|
||||||
|
VALUE _fontsize = rb_hash_aref(ass_entry, rb_str_new2("fontsize"));
|
||||||
|
float fontsize = rb_num2dbl(_fontsize);
|
||||||
|
VALUE _color1 = rb_hash_aref(ass_entry, rb_str_new2("color1"));
|
||||||
|
wxString color1(StringValueCStr(_color1), wxConvUTF8);
|
||||||
|
VALUE _color2 = rb_hash_aref(ass_entry, rb_str_new2("color2"));
|
||||||
|
wxString color2(StringValueCStr(_color2), wxConvUTF8);
|
||||||
|
VALUE _color3 = rb_hash_aref(ass_entry, rb_str_new2("color3"));
|
||||||
|
wxString color3(StringValueCStr(_color3), wxConvUTF8);
|
||||||
|
VALUE _color4 = rb_hash_aref(ass_entry, rb_str_new2("color4"));
|
||||||
|
wxString color4(StringValueCStr(_color4), wxConvUTF8);
|
||||||
|
VALUE _bold = rb_hash_aref(ass_entry, rb_str_new2("bold"));
|
||||||
|
bool bold = (bool)rb_num2long(_bold);
|
||||||
|
VALUE _italic = rb_hash_aref(ass_entry, rb_str_new2("italic"));
|
||||||
|
bool italic = (bool)rb_num2long(_italic);
|
||||||
|
VALUE _underline = rb_hash_aref(ass_entry, rb_str_new2("underline"));
|
||||||
|
bool underline = (bool)rb_num2long(_underline);
|
||||||
|
VALUE _strikeout = rb_hash_aref(ass_entry, rb_str_new2("strikeout"));
|
||||||
|
bool strikeout = (bool)rb_num2long(_strikeout);
|
||||||
|
VALUE _scale_x = rb_hash_aref(ass_entry, rb_str_new2("scale_x"));
|
||||||
|
float scale_x = rb_num2dbl(_scale_x);
|
||||||
|
VALUE _scale_y = rb_hash_aref(ass_entry, rb_str_new2("scale_y"));
|
||||||
|
float scale_y = rb_num2dbl(_scale_y);
|
||||||
|
VALUE _spacing = rb_hash_aref(ass_entry, rb_str_new2("spacing"));
|
||||||
|
int spacing = rb_num2long(_spacing);
|
||||||
|
VALUE _angle = rb_hash_aref(ass_entry, rb_str_new2("angle"));
|
||||||
|
float angle = rb_num2dbl(_angle);
|
||||||
|
VALUE _borderstyle = rb_hash_aref(ass_entry, rb_str_new2("borderstyle"));
|
||||||
|
int borderstyle = rb_num2long(_borderstyle);
|
||||||
|
VALUE _outline = rb_hash_aref(ass_entry, rb_str_new2("outline"));
|
||||||
|
float outline = rb_num2dbl(_outline);
|
||||||
|
VALUE _shadow = rb_hash_aref(ass_entry, rb_str_new2("shadow"));
|
||||||
|
float shadow = rb_num2dbl(_shadow);
|
||||||
|
VALUE _align = rb_hash_aref(ass_entry, rb_str_new2("align"));
|
||||||
|
int align = rb_num2long(_align);
|
||||||
|
VALUE _margin_l = rb_hash_aref(ass_entry, rb_str_new2("margin_l"));
|
||||||
|
int margin_l = rb_num2long(_margin_l);
|
||||||
|
VALUE _margin_r = rb_hash_aref(ass_entry, rb_str_new2("margin_r"));
|
||||||
|
int margin_r = rb_num2long(_margin_r);
|
||||||
|
VALUE _margin_t = rb_hash_aref(ass_entry, rb_str_new2("margin_t"));
|
||||||
|
int margin_t = rb_num2long(_margin_t);
|
||||||
|
VALUE _margin_b = rb_hash_aref(ass_entry, rb_str_new2("margin_b"));
|
||||||
|
int margin_b = rb_num2long(_margin_b);
|
||||||
|
VALUE _encoding = rb_hash_aref(ass_entry, rb_str_new2("encoding"));
|
||||||
|
int encoding = rb_num2long(_encoding);
|
||||||
|
// leaving out relative_to and vertical
|
||||||
|
|
||||||
|
AssStyle *sty = new AssStyle();
|
||||||
|
sty->name = name;
|
||||||
|
sty->font = fontname;
|
||||||
|
sty->fontsize = fontsize;
|
||||||
|
sty->primary.Parse(color1);
|
||||||
|
sty->secondary.Parse(color2);
|
||||||
|
sty->outline.Parse(color3);
|
||||||
|
sty->shadow.Parse(color4);
|
||||||
|
sty->bold = bold;
|
||||||
|
sty->italic = italic;
|
||||||
|
sty->underline = underline;
|
||||||
|
sty->strikeout = strikeout;
|
||||||
|
sty->scalex = scale_x;
|
||||||
|
sty->scaley = scale_y;
|
||||||
|
sty->spacing = spacing;
|
||||||
|
sty->angle = angle;
|
||||||
|
sty->borderstyle = borderstyle;
|
||||||
|
sty->outline_w = outline;
|
||||||
|
sty->shadow_w = shadow;
|
||||||
|
sty->alignment = align;
|
||||||
|
sty->Margin[0] = margin_l;
|
||||||
|
sty->Margin[1] = margin_r;
|
||||||
|
sty->Margin[2] = margin_t;
|
||||||
|
sty->Margin[3] = margin_b;
|
||||||
|
sty->encoding = encoding;
|
||||||
|
sty->UpdateData();
|
||||||
|
|
||||||
|
result = sty;
|
||||||
|
} else if (lclass == _T("styleex")) {
|
||||||
|
rb_raise(rb_eRuntimeError, "Found line with class 'styleex' which is not supported. Wait until AS5 is a reality.");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} else if (lclass == _T("dialogue")) {
|
||||||
|
VALUE _comment = rb_hash_aref(ass_entry, rb_str_new2("comment"));
|
||||||
|
bool comment = (bool)rb_num2long(_comment);
|
||||||
|
VALUE _layer = rb_hash_aref(ass_entry, rb_str_new2("layer"));
|
||||||
|
int layer = rb_num2long(_layer);
|
||||||
|
VALUE _start_time = rb_hash_aref(ass_entry, rb_str_new2("start_time"));
|
||||||
|
int start_time = rb_num2long(_start_time);
|
||||||
|
VALUE _end_time = rb_hash_aref(ass_entry, rb_str_new2("end_time"));
|
||||||
|
int end_time = rb_num2long(_end_time);
|
||||||
|
VALUE _style = rb_hash_aref(ass_entry, rb_str_new2("style"));
|
||||||
|
wxString style(StringValueCStr(_style), wxConvUTF8);
|
||||||
|
VALUE _actor = rb_hash_aref(ass_entry, rb_str_new2("actor"));
|
||||||
|
wxString actor(StringValueCStr(_actor), wxConvUTF8);
|
||||||
|
VALUE _margin_l = rb_hash_aref(ass_entry, rb_str_new2("margin_l"));
|
||||||
|
int margin_l = rb_num2long(_margin_l);
|
||||||
|
VALUE _margin_r = rb_hash_aref(ass_entry, rb_str_new2("margin_r"));
|
||||||
|
int margin_r = rb_num2long(_margin_r);
|
||||||
|
VALUE _margin_t = rb_hash_aref(ass_entry, rb_str_new2("margin_t"));
|
||||||
|
int margin_t = rb_num2long(_margin_t);
|
||||||
|
VALUE _margin_b = rb_hash_aref(ass_entry, rb_str_new2("margin_b"));
|
||||||
|
int margin_b = rb_num2long(_margin_b);
|
||||||
|
VALUE _effect = rb_hash_aref(ass_entry, rb_str_new2("effect"));
|
||||||
|
wxString effect(StringValueCStr(_effect), wxConvUTF8);
|
||||||
|
VALUE _text = rb_hash_aref(ass_entry, rb_str_new2("text"));
|
||||||
|
wxString text(StringValueCStr(_text), wxConvUTF8);
|
||||||
|
//GETSTRING(userdata, "userdata", "dialogue")
|
||||||
|
|
||||||
|
AssDialogue *dia = new AssDialogue();
|
||||||
|
dia->Comment = comment;
|
||||||
|
dia->Layer = layer;
|
||||||
|
dia->Start.SetMS(start_time);
|
||||||
|
dia->End.SetMS(end_time);
|
||||||
|
dia->Style = style;
|
||||||
|
dia->Actor = actor;
|
||||||
|
dia->Margin[0] = margin_l;
|
||||||
|
dia->Margin[1] = margin_r;
|
||||||
|
dia->Margin[2] = margin_t;
|
||||||
|
dia->Margin[3] = margin_b;
|
||||||
|
dia->Effect = effect;
|
||||||
|
dia->Text = text;
|
||||||
|
dia->UpdateData();
|
||||||
|
|
||||||
|
result = dia;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rb_raise(rb_eRuntimeError, "Found line with unknown class: %s", lclass.mb_str(wxConvUTF8).data());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the AssFile with data returned by macro/filter
|
||||||
|
// If the first line is dialogue we leave header from the original (styles, info, etc)
|
||||||
|
void RubyAssFile::RubyUpdateAssFile(VALUE subtitles)
|
||||||
|
{
|
||||||
|
RubyObjects::Get()->Register(subtitles);
|
||||||
|
int size = rb_num2long(rb_funcall(subtitles, rb_to_id(rb_str_new2("size")), 0));
|
||||||
|
|
||||||
|
if(size <= 0) return; // empty - leave the original
|
||||||
|
|
||||||
|
VALUE rbEntry = rb_ary_shift(subtitles);
|
||||||
|
AssEntry *new_entry = RubyToAssEntry(rbEntry);
|
||||||
|
|
||||||
|
entryIter e = ass->Line.begin();
|
||||||
|
if(new_entry->GetType() == ENTRY_DIALOGUE) // check if the first line is a dialogue
|
||||||
|
{
|
||||||
|
while(e != ass->Line.end() && (*e)->GetType() != ENTRY_DIALOGUE) ++e;
|
||||||
|
}
|
||||||
|
while(e != ass->Line.end()) // delete the old lines backwards
|
||||||
|
{
|
||||||
|
delete (*e);
|
||||||
|
e = ass->Line.erase(e);
|
||||||
|
}
|
||||||
|
ass->Line.push_back(new_entry);
|
||||||
|
for(int i = 1; i < size; i++) // insert new lines
|
||||||
|
{
|
||||||
|
rbEntry = rb_ary_shift(subtitles);
|
||||||
|
new_entry = RubyToAssEntry(rbEntry);
|
||||||
|
ass->Line.push_back(new_entry);
|
||||||
|
}
|
||||||
|
RubyObjects::Get()->Unregister(subtitles);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyAssFile::RubyParseTagData()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyAssFile::RubyUnparseTagData()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyAssFile::RubyParseKaraokeData()
|
||||||
|
{
|
||||||
|
/* AssEntry *e = RubyToAssEntry(L);
|
||||||
|
if (e->GetType() != ENTRY_DIALOGUE) {
|
||||||
|
delete e;
|
||||||
|
lua_pushstring(L, "Attempt to create karaoke table from non-dialogue subtitle line");
|
||||||
|
lua_error(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssDialogue *dia = e->GetAsDialogue(e);
|
||||||
|
dia->ParseASSTags();
|
||||||
|
|
||||||
|
int kcount = 0;
|
||||||
|
int kdur = 0;
|
||||||
|
int ktime = 0;
|
||||||
|
wxString ktag = _T("");
|
||||||
|
wxString ktext = _T("");
|
||||||
|
wxString ktext_stripped = _T("");
|
||||||
|
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)dia->Blocks.size(); i++) {
|
||||||
|
AssDialogueBlock *block = dia->Blocks[i];
|
||||||
|
|
||||||
|
switch (block->type) {
|
||||||
|
|
||||||
|
case BLOCK_BASE:
|
||||||
|
assert(block->type != BLOCK_BASE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLOCK_PLAIN:
|
||||||
|
ktext += block->text;
|
||||||
|
ktext_stripped += block->text;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLOCK_DRAWING:
|
||||||
|
// a drawing is regarded as a kind of control code here, so it's just stripped away
|
||||||
|
ktext += block->text;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BLOCK_OVERRIDE: {
|
||||||
|
bool brackets_open = false;
|
||||||
|
AssDialogueBlockOverride *ovr = block->GetAsOverride(block);
|
||||||
|
|
||||||
|
for (int j = 0; j < (int)ovr->Tags.size(); j++) {
|
||||||
|
AssOverrideTag *tag = ovr->Tags[j];
|
||||||
|
|
||||||
|
if (tag->IsValid() && tag->Name.Mid(0,2).CmpNoCase(_T("\\k")) == 0) {
|
||||||
|
// karaoke tag
|
||||||
|
if (brackets_open) {
|
||||||
|
ktext += _T("}");
|
||||||
|
brackets_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store to lua
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_pushnumber(L, kdur);
|
||||||
|
lua_setfield(L, -2, "duration");
|
||||||
|
lua_pushnumber(L, ktime);
|
||||||
|
lua_setfield(L, -2, "start_time");
|
||||||
|
lua_pushnumber(L, ktime+kdur);
|
||||||
|
lua_setfield(L, -2, "end_time");
|
||||||
|
lua_pushstring(L, ktag.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "tag");
|
||||||
|
lua_pushstring(L, ktext.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "text");
|
||||||
|
lua_pushstring(L, ktext_stripped.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "text_stripped");
|
||||||
|
lua_rawseti(L, -2, kcount);
|
||||||
|
|
||||||
|
// prepare new syllable
|
||||||
|
kcount++;
|
||||||
|
ktag = tag->Name.Mid(1);
|
||||||
|
// check if it's a "set time" tag, special handling for that (depends on previous syllable duration)
|
||||||
|
if (ktag == _T("kt")) {
|
||||||
|
ktime = tag->Params[0]->AsInt() * 10;
|
||||||
|
kdur = 0;
|
||||||
|
} else {
|
||||||
|
ktime += kdur; // duration of previous syllable
|
||||||
|
kdur = tag->Params[0]->AsInt() * 10;
|
||||||
|
}
|
||||||
|
ktext.clear();
|
||||||
|
ktext_stripped.clear();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// not karaoke tag
|
||||||
|
if (!brackets_open) {
|
||||||
|
ktext += _T("{");
|
||||||
|
brackets_open = true;
|
||||||
|
}
|
||||||
|
ktext += tag->ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brackets_open) {
|
||||||
|
ktext += _T("}");
|
||||||
|
brackets_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dia->ClearBlocks();
|
||||||
|
|
||||||
|
// store final syllable/block to lua
|
||||||
|
lua_newtable(L);
|
||||||
|
lua_pushnumber(L, kdur);
|
||||||
|
lua_setfield(L, -2, "duration");
|
||||||
|
lua_pushnumber(L, ktime);
|
||||||
|
lua_setfield(L, -2, "start_time");
|
||||||
|
lua_pushnumber(L, ktime+kdur);
|
||||||
|
lua_setfield(L, -2, "end_time");
|
||||||
|
lua_pushstring(L, ktag.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "tag");
|
||||||
|
lua_pushstring(L, ktext.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "text");
|
||||||
|
lua_pushstring(L, ktext_stripped.mb_str(wxConvUTF8));
|
||||||
|
lua_setfield(L, -2, "text_stripped");
|
||||||
|
lua_rawseti(L, -2, kcount);
|
||||||
|
|
||||||
|
delete dia;*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyAssFile::RubySetUndoPoint()
|
||||||
|
{
|
||||||
|
/* RubyAssFile *laf = GetObjPointer(L, lua_upvalueindex(1));
|
||||||
|
if (!laf->can_set_undo) {
|
||||||
|
lua_pushstring(L, "Attempt to set an undo point in a context without undo-support.");
|
||||||
|
lua_error(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString description;
|
||||||
|
if (lua_isstring(L, 1)) {
|
||||||
|
description = wxString(lua_tostring(L, 1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
AssFile::top->FlagAsModified(); // TODO: make undo system support description of action undone
|
||||||
|
|
||||||
|
laf->ass = AssFile::top; // make sure we're still working on the most recent undo point
|
||||||
|
*/ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyAssFile::~RubyAssFile()
|
||||||
|
{
|
||||||
|
RubyObjects::Get()->Unregister(rbAssFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
RubyAssFile::RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo)
|
||||||
|
: ass(_ass)
|
||||||
|
, can_modify(_can_modify)
|
||||||
|
, can_set_undo(_can_set_undo)
|
||||||
|
{
|
||||||
|
if(RubyAssFile::raf)
|
||||||
|
delete RubyAssFile::raf; // delete previous if there is one
|
||||||
|
RubyAssFile::raf = this; // set pointer to this obj
|
||||||
|
|
||||||
|
rbAssFile = rb_ary_new2(ass->Line.size());
|
||||||
|
RubyObjects::Get()->Register(rbAssFile);
|
||||||
|
|
||||||
|
std::list<AssEntry*>::iterator entry;
|
||||||
|
for(entry = ass->Line.begin(); entry != ass->Line.end(); ++entry)
|
||||||
|
{
|
||||||
|
rb_ary_push(rbAssFile, AssEntryToRuby(*entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "parse_tag_data",reinterpret_cast<RB_HOOK>(&RubyParseTagData), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "unparse_tag_data",reinterpret_cast<RB_HOOK>(&RubyUnparseTagData), 1);
|
||||||
|
rb_define_module_function(RubyScript::RubyAegisub, "parse_karaoke_data",reinterpret_cast<RB_HOOK>(&RubyParseKaraokeData), 1);
|
||||||
|
//rb_define_module_function(RubyScript::RubyAegisub, "set_undo_point",reinterpret_cast<RB_HOOK>(&RubySetUndoPoint), 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
605
aegisub/auto4_ruby_dialog.cpp
Normal file
605
aegisub/auto4_ruby_dialog.cpp
Normal file
|
@ -0,0 +1,605 @@
|
||||||
|
// Copyright (c) 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "auto4_ruby.h"
|
||||||
|
#include "../ruby/include/ruby.h"
|
||||||
|
#include <wx/window.h>
|
||||||
|
#include <wx/spinctrl.h>
|
||||||
|
#include <wx/gbsizer.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/validate.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace Automation4 {
|
||||||
|
|
||||||
|
|
||||||
|
// RubyConfigDialogControl
|
||||||
|
|
||||||
|
RubyConfigDialogControl::RubyConfigDialogControl()
|
||||||
|
{
|
||||||
|
// Assume top of stack is a control table (don't do checking)
|
||||||
|
|
||||||
|
/* lua_getfield(L, -1, "name");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
name = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
} else {
|
||||||
|
name = _T("");
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "x");
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
x = lua_tointeger(L, -1);
|
||||||
|
if (x < 0) x = 0;
|
||||||
|
} else {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "y");
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
y = lua_tointeger(L, -1);
|
||||||
|
if (y < 0) y = 0;
|
||||||
|
} else {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "width");
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
width = lua_tointeger(L, -1);
|
||||||
|
if (width < 1) width = 1;
|
||||||
|
} else {
|
||||||
|
width = 1;
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "height");
|
||||||
|
if (lua_isnumber(L, -1)) {
|
||||||
|
height = lua_tointeger(L, -1);
|
||||||
|
if (height < 1) height = 1;
|
||||||
|
} else {
|
||||||
|
height = 1;
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "hint");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
hint = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
} else {
|
||||||
|
hint = _T("");
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/
|
||||||
|
wxLogDebug(_T("created control: '%s', (%d,%d)(%d,%d), '%s'"), name.c_str(), x, y, width, height, hint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace RubyControl {
|
||||||
|
|
||||||
|
// Label
|
||||||
|
|
||||||
|
class Label : public RubyConfigDialogControl {
|
||||||
|
public:
|
||||||
|
wxString label;
|
||||||
|
|
||||||
|
Label()
|
||||||
|
: RubyConfigDialogControl()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "label");
|
||||||
|
label = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
virtual ~Label() { }
|
||||||
|
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
return cw = new wxStaticText(parent, -1, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// Label doesn't produce output, so let it be nil
|
||||||
|
// lua_pushnil(L);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Basic edit
|
||||||
|
|
||||||
|
class Edit : public RubyConfigDialogControl {
|
||||||
|
public:
|
||||||
|
wxString text;
|
||||||
|
|
||||||
|
Edit()
|
||||||
|
: RubyConfigDialogControl()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "text");
|
||||||
|
text = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
virtual ~Edit() { }
|
||||||
|
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
text = ((wxTextCtrl*)cw)->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// lua_pushstring(L, text.mb_str(wxConvUTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Multiline edit
|
||||||
|
|
||||||
|
class Textbox : public Edit {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Textbox()
|
||||||
|
: Edit()
|
||||||
|
{
|
||||||
|
// Nothing more
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Textbox() { }
|
||||||
|
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
|
||||||
|
cw->SetMinSize(wxSize(0, 30));
|
||||||
|
return cw;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Integer only edit
|
||||||
|
|
||||||
|
class IntEdit : public Edit {
|
||||||
|
public:
|
||||||
|
int value;
|
||||||
|
bool hasspin;
|
||||||
|
int min, max;
|
||||||
|
|
||||||
|
IntEdit()
|
||||||
|
: Edit()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "value");
|
||||||
|
value = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
hasspin = false;
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "min");
|
||||||
|
if (!lua_isnumber(L, -1))
|
||||||
|
goto nospin;
|
||||||
|
min = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "max");
|
||||||
|
if (!lua_isnumber(L, -1))
|
||||||
|
goto nospin;
|
||||||
|
max = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
hasspin = true;
|
||||||
|
nospin:
|
||||||
|
if (!hasspin) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
virtual ~IntEdit() { }
|
||||||
|
|
||||||
|
typedef wxValidator IntTextValidator; // TODO
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
if (hasspin) {
|
||||||
|
return cw = new wxSpinCtrl(parent, -1, wxString::Format(_T("%d"), value), wxDefaultPosition, wxDefaultSize, min, max, value);
|
||||||
|
} else {
|
||||||
|
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0); //, IntTextValidator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
if (hasspin) {
|
||||||
|
value = ((wxSpinCtrl*)cw)->GetValue();
|
||||||
|
} else {
|
||||||
|
long newval;
|
||||||
|
text = ((wxTextCtrl*)cw)->GetValue();
|
||||||
|
if (text.ToLong(&newval)) {
|
||||||
|
value = newval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// lua_pushinteger(L, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Float only edit
|
||||||
|
|
||||||
|
class FloatEdit : public Edit {
|
||||||
|
public:
|
||||||
|
float value;
|
||||||
|
// FIXME: Can't support spin button atm
|
||||||
|
|
||||||
|
FloatEdit()
|
||||||
|
: Edit()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "value");
|
||||||
|
value = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/
|
||||||
|
// TODO: spin button support
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~FloatEdit() { }
|
||||||
|
|
||||||
|
typedef wxValidator FloatTextValidator;
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
return cw = new wxTextCtrl(parent, -1, text, wxDefaultPosition, wxDefaultSize, 0); //, FloatTextValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
double newval;
|
||||||
|
text = ((wxTextCtrl*)cw)->GetValue();
|
||||||
|
if (text.ToDouble(&newval)) {
|
||||||
|
value = newval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// lua_pushnumber(L, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Dropdown
|
||||||
|
|
||||||
|
class Dropdown : public RubyConfigDialogControl {
|
||||||
|
public:
|
||||||
|
wxArrayString items;
|
||||||
|
wxString value;
|
||||||
|
|
||||||
|
Dropdown()
|
||||||
|
: RubyConfigDialogControl()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "value");
|
||||||
|
value = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "items");
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, -2)) {
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
items.Add(wxString(lua_tostring(L, -1), wxConvUTF8));
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
virtual ~Dropdown() { }
|
||||||
|
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
return cw = new wxComboBox(parent, -1, value, wxDefaultPosition, wxDefaultSize, items, wxCB_READONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
value = ((wxComboBox*)cw)->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// lua_pushstring(L, value.mb_str(wxConvUTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Checkbox
|
||||||
|
|
||||||
|
class Checkbox : public RubyConfigDialogControl {
|
||||||
|
public:
|
||||||
|
wxString label;
|
||||||
|
bool value;
|
||||||
|
|
||||||
|
Checkbox()
|
||||||
|
: RubyConfigDialogControl()
|
||||||
|
{
|
||||||
|
/* lua_getfield(L, -1, "label");
|
||||||
|
label = wxString(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "value");
|
||||||
|
value = lua_toboolean(L, -1) != 0;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
virtual ~Checkbox() { }
|
||||||
|
|
||||||
|
wxControl *Create(wxWindow *parent)
|
||||||
|
{
|
||||||
|
return cw = new wxCheckBox(parent, -1, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlReadBack()
|
||||||
|
{
|
||||||
|
value = ((wxCheckBox*)cw)->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyReadBack()
|
||||||
|
{
|
||||||
|
// lua_pushboolean(L, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// RubyConfigDialog
|
||||||
|
|
||||||
|
RubyConfigDialog::RubyConfigDialog(bool include_buttons)
|
||||||
|
: use_buttons(include_buttons)
|
||||||
|
{
|
||||||
|
wxLogDebug(_T("creating RubyConfigDialog, this addr is %p"), this);
|
||||||
|
button_pushed = 0;
|
||||||
|
/* if (include_buttons) {
|
||||||
|
|
||||||
|
if (!lua_istable(L, -1))
|
||||||
|
// Just to avoid deeper indentation...
|
||||||
|
goto skipbuttons;
|
||||||
|
// Iterate over items in table
|
||||||
|
lua_pushnil(L); // initial key
|
||||||
|
while (lua_next(L, -2)) {
|
||||||
|
// Simply skip invalid items... FIXME, warn here?
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
wxString s(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
buttons.push_back(s);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
skipbuttons:
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume top of stack now contains a dialog table
|
||||||
|
if (!lua_istable(L, -1)) {
|
||||||
|
lua_pushstring(L, "Cannot create config dialog from something non-table");
|
||||||
|
lua_error(L);
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, so there is a table with controls
|
||||||
|
lua_pushnil(L); // initial key
|
||||||
|
while (lua_next(L, -2)) {
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
// Get control class
|
||||||
|
lua_getfield(L, -1, "class");
|
||||||
|
if (!lua_isstring(L, -1))
|
||||||
|
goto badcontrol;
|
||||||
|
wxString controlclass(lua_tostring(L, -1), wxConvUTF8);
|
||||||
|
controlclass.LowerCase();
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
RubyConfigDialogControl *ctl;
|
||||||
|
|
||||||
|
// Check control class and create relevant control
|
||||||
|
if (controlclass == _T("label")) {
|
||||||
|
ctl = new RubyControl::Label(L);
|
||||||
|
} else if (controlclass == _T("edit")) {
|
||||||
|
ctl = new RubyControl::Edit(L);
|
||||||
|
} else if (controlclass == _T("intedit")) {
|
||||||
|
ctl = new RubyControl::IntEdit(L);
|
||||||
|
} else if (controlclass == _T("floatedit")) {
|
||||||
|
ctl = new RubyControl::FloatEdit(L);
|
||||||
|
} else if (controlclass == _T("textbox")) {
|
||||||
|
ctl = new RubyControl::Textbox(L);
|
||||||
|
} else if (controlclass == _T("dropdown")) {
|
||||||
|
ctl = new RubyControl::Dropdown(L);
|
||||||
|
} else if (controlclass == _T("checkbox")) {
|
||||||
|
ctl = new RubyControl::Checkbox(L);
|
||||||
|
} else if (controlclass == _T("color")) {
|
||||||
|
// FIXME
|
||||||
|
ctl = new RubyControl::Edit(L);
|
||||||
|
} else if (controlclass == _T("coloralpha")) {
|
||||||
|
// FIXME
|
||||||
|
ctl = new RubyControl::Edit(L);
|
||||||
|
} else if (controlclass == _T("alpha")) {
|
||||||
|
// FIXME
|
||||||
|
ctl = new RubyControl::Edit(L);
|
||||||
|
} else {
|
||||||
|
goto badcontrol;
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.push_back(ctl);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
badcontrol:
|
||||||
|
// not a control...
|
||||||
|
// FIXME, better error reporting?
|
||||||
|
lua_pushstring(L, "bad control table entry");
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
*/ }
|
||||||
|
|
||||||
|
RubyConfigDialog::~RubyConfigDialog()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < controls.size(); ++i)
|
||||||
|
delete controls[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWindow* RubyConfigDialog::CreateWindow(wxWindow *parent)
|
||||||
|
{
|
||||||
|
wxWindow *w = new wxPanel(parent);
|
||||||
|
wxGridBagSizer *s = new wxGridBagSizer(4, 4);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < controls.size(); ++i) {
|
||||||
|
RubyConfigDialogControl *c = controls[i];
|
||||||
|
c->Create(w);
|
||||||
|
if (dynamic_cast<RubyControl::Label*>(c)) {
|
||||||
|
s->Add(c->cw, wxGBPosition(c->y, c->x), wxGBSpan(c->height, c->width), wxALIGN_CENTRE_VERTICAL|wxALIGN_LEFT);
|
||||||
|
} else {
|
||||||
|
s->Add(c->cw, wxGBPosition(c->y, c->x), wxGBSpan(c->height, c->width), wxEXPAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_buttons) {
|
||||||
|
wxStdDialogButtonSizer *bs = new wxStdDialogButtonSizer();
|
||||||
|
if (buttons.size() > 0) {
|
||||||
|
wxLogDebug(_T("creating user buttons"));
|
||||||
|
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||||
|
wxLogDebug(_T("button '%s' gets id %d"), buttons[i].c_str(), 1001+(wxWindowID)i);
|
||||||
|
bs->Add(new wxButton(w, 1001+(wxWindowID)i, buttons[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wxLogDebug(_T("creating default buttons"));
|
||||||
|
bs->Add(new wxButton(w, wxID_OK));
|
||||||
|
bs->Add(new wxButton(w, wxID_CANCEL));
|
||||||
|
}
|
||||||
|
bs->Realize();
|
||||||
|
|
||||||
|
button_event = new ButtonEventHandler();
|
||||||
|
button_event->button_pushed = &button_pushed;
|
||||||
|
// passing button_event as userdata because wx will then delete it
|
||||||
|
w->Connect(wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(RubyConfigDialog::ButtonEventHandler::OnButtonPush), button_event, button_event);
|
||||||
|
wxLogDebug(_T("set event handler, this addr is %p"), this);
|
||||||
|
|
||||||
|
wxBoxSizer *ms = new wxBoxSizer(wxVERTICAL);
|
||||||
|
ms->Add(s, 0, wxBOTTOM, 5);
|
||||||
|
ms->Add(bs);
|
||||||
|
w->SetSizerAndFit(ms);
|
||||||
|
} else {
|
||||||
|
w->SetSizerAndFit(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RubyConfigDialog::RubyReadBack()
|
||||||
|
{
|
||||||
|
// First read back which button was pressed, if any
|
||||||
|
if (use_buttons) {
|
||||||
|
wxLogDebug(_T("reading back button_pushed"));
|
||||||
|
int btn = button_pushed;
|
||||||
|
if (btn == 0) {
|
||||||
|
wxLogDebug(_T("was zero, cancelled"));
|
||||||
|
// Always cancel/closed
|
||||||
|
// lua_pushboolean(L, 0);
|
||||||
|
} else {
|
||||||
|
wxLogDebug(_T("nonzero, something else: %d"), btn);
|
||||||
|
if (buttons.size() > 0) {
|
||||||
|
wxLogDebug(_T("user button: %s"), buttons[btn-1].c_str());
|
||||||
|
// button_pushed is index+1 to reserve 0 for Cancel
|
||||||
|
// lua_pushstring(L, buttons[btn-1].mb_str(wxConvUTF8));
|
||||||
|
} else {
|
||||||
|
wxLogDebug(_T("default button, must be Ok"));
|
||||||
|
// Cancel case already covered, must be Ok then
|
||||||
|
// lua_pushboolean(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then read controls back
|
||||||
|
// lua_newtable(L);
|
||||||
|
for (size_t i = 0; i < controls.size(); ++i) {
|
||||||
|
controls[i]->RubyReadBack(); // TODO
|
||||||
|
// lua_setfield(L, -2, controls[i]->name.mb_str(wxConvUTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_buttons) {
|
||||||
|
return 2;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyConfigDialog::ReadBack()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < controls.size(); ++i) {
|
||||||
|
controls[i]->ControlReadBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RubyConfigDialog::ButtonEventHandler::OnButtonPush(wxCommandEvent &evt)
|
||||||
|
{
|
||||||
|
// Let button_pushed == 0 mean "cancelled", such that pushing Cancel or closing the dialog
|
||||||
|
// will both result in button_pushed == 0
|
||||||
|
if (evt.GetId() == wxID_OK) {
|
||||||
|
wxLogDebug(_T("was wxID_OK"));
|
||||||
|
*button_pushed = 1;
|
||||||
|
} else if (evt.GetId() == wxID_CANCEL) {
|
||||||
|
wxLogDebug(_T("was wxID_CANCEL"));
|
||||||
|
*button_pushed = 0;
|
||||||
|
} else {
|
||||||
|
wxLogDebug(_T("was user button"));
|
||||||
|
// Therefore, when buttons are numbered from 1001 to 1000+n, make sure to set it to i+1
|
||||||
|
*button_pushed = evt.GetId() - 1000;
|
||||||
|
evt.SetId(wxID_OK); // hack to make sure the dialog will be closed
|
||||||
|
}
|
||||||
|
wxLogDebug(_T("button_pushed set to %d"), *button_pushed);
|
||||||
|
evt.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
Loading…
Reference in a new issue