Ruby: configuration dialogs

Originally committed to SVN as r922.
This commit is contained in:
pomyk 2007-02-03 20:31:20 +00:00
parent 2f52b36910
commit 505e09257e
3 changed files with 244 additions and 256 deletions

View file

@ -55,7 +55,7 @@ namespace Automation4 {
RubyObjects *RubyObjects::inst = NULL;
RubyScript * RubyScript::inst = NULL; // current Ruby Script
RubyProgressSink* RubyProgressSink::inst = NULL;
VALUE RubyScript::RubyAegisub;
VALUE RubyScript::RubyAegisub = Qfalse;
// RubyAssFile *RubyAssFile::raf = NULL;
// RubyScriptReader
@ -115,6 +115,7 @@ namespace Automation4 {
ruby_init();
ruby_init_loadpath();
RubyScript::inst = this;
if(!RubyAegisub) {
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);
@ -124,6 +125,8 @@ namespace Automation4 {
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);
rb_define_module_function(RubyScript::RubyAegisub, "display_dialog",reinterpret_cast<RB_HOOK>(&RubyProgressSink::RubyDisplayDialog), 2);
}
VALUE paths = rb_gv_get("$:");
for(int i = 0; i < include_path.GetCount(); i++)
{
@ -138,7 +141,9 @@ namespace Automation4 {
if(status > 0) // something bad happened (probably parsing error)
{
VALUE err = rb_errinfo();
if(TYPE(err) == T_STRING)
throw StringValueCStr(err);
else throw "Error loading script";
}
VALUE global_var = rb_gv_get("$script_name");
@ -166,11 +171,8 @@ namespace Automation4 {
void RubyScript::Destroy()
{
if(loaded) {
ruby_finalize();
ruby_cleanup(0);
// ruby_finalize(); // broken in 1.9 ?_?
}
// TODO: would be nice to implement this
// RubyObjects::Get()->UnregisterAll();
// remove features
for (int i = 0; i < (int)features.size(); i++) {
@ -324,6 +326,7 @@ namespace Automation4 {
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
delete RubyProgressSink::inst;
RubyProgressSink::inst = NULL;
if(code)
{
@ -331,7 +334,7 @@ namespace Automation4 {
throw StringValueCStr(result);
else throw "Unknown Error";
}
else if(result != Qnil && result != Qfalse)
else if(TYPE(result) == T_ARRAY)
{
subsobj->RubyUpdateAssFile(result);
}
@ -404,27 +407,27 @@ namespace Automation4 {
void RubyFeatureFilter::ProcessSubs(AssFile *subs, wxWindow *export_dialog)
{
// TODO: configuration dialog
try {
VALUE cfg;
if (has_config && config_dialog) {
assert(config_dialog->RubyReadBack() == 1);
cfg = config_dialog->RubyReadBack();
// TODO, write back stored options here
}
delete RubyProgressSink::inst;
RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
RubyProgressSink::inst->SetTitle(GetName());
// delete RubyProgressSink::inst;
// RubyProgressSink::inst = new RubyProgressSink(export_dialog, false);
// RubyProgressSink::inst->SetTitle(GetName());
RubyAssFile *subsobj = new RubyAssFile(subs, true/*modify*/, false/*undo*/);
VALUE *argv = ALLOCA_N(VALUE, 2);
argv[0] = subsobj->rbAssFile;
argv[1] = Qnil; // config
argv[1] = cfg; // config
RubyCallArguments arg(rb_mKernel, rb_to_id(filter_fun), 2, argv);
VALUE result;
RubyThreadedCall call(&arg, &result);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
delete RubyProgressSink::inst;
RubyProgressSink::inst = NULL;
RubyThreadedCall call(&arg, &result);
// RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
if(code)
{
if(TYPE(result) == T_STRING)
@ -447,25 +450,26 @@ namespace Automation4 {
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();
// 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*/);
*/ return config_dialog = new RubyConfigDialog(false);
VALUE *argv = ALLOCA_N(VALUE, 2);
argv[0] = subsobj->rbAssFile;
argv[1] = Qnil; // TODO: stored options
RubyCallArguments arg(rb_mKernel, rb_to_id(dialog_fun), 2, argv);
VALUE dialog_data;
RubyThreadedCall call(&arg, &dialog_data);
RubyProgressSink::inst->ShowModal();
wxThread::ExitCode code = call.Wait();
delete RubyProgressSink::inst;
RubyProgressSink::inst = NULL;
return config_dialog = new RubyConfigDialog(dialog_data, Qnil, false);
}
@ -523,9 +527,19 @@ namespace Automation4 {
return Qtrue;
}
int RubyProgressSink::RubyDisplayDialog()
VALUE RubyProgressSink::RubyDisplayDialog(VALUE self, VALUE dialog_data, VALUE buttons)
{
return 0;
// Send the "show dialog" event
ShowConfigDialogEvent evt;
RubyConfigDialog dlg(dialog_data, buttons, true); // magically creates the config dialog structure etc
evt.config_dialog = &dlg;
wxSemaphore sema(0, 1);
evt.sync_sema = &sema;
RubyProgressSink::inst->AddPendingEvent(evt);
sema.Wait();
return dlg.RubyReadBack();
}

View file

@ -92,7 +92,6 @@ namespace Automation4 {
// 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);
@ -103,6 +102,7 @@ namespace Automation4 {
static VALUE RubySetTitle(VALUE self, VALUE title);
static VALUE RubyGetCancelled(VALUE self);
static VALUE RubyDebugOut(int argc, VALUE *args, VALUE self);
static VALUE RubyDisplayDialog(VALUE self, VALUE cfg, VALUE buttons);
};
@ -115,9 +115,10 @@ namespace Automation4 {
virtual wxControl *Create(wxWindow *parent) = 0;
virtual void ControlReadBack() = 0;
virtual void RubyReadBack() = 0;
virtual VALUE RubyReadBack() = 0;
RubyConfigDialogControl();
RubyConfigDialogControl(VALUE opts);
virtual ~RubyConfigDialogControl() { }
};
@ -140,9 +141,9 @@ namespace Automation4 {
wxWindow* CreateWindow(wxWindow *parent);
public:
RubyConfigDialog(bool include_buttons);
RubyConfigDialog(VALUE cfg, VALUE buttons, bool show_buttons);
virtual ~RubyConfigDialog();
int RubyReadBack(); // read back internal structure to lua structures
VALUE RubyReadBack(); // read back internal structure to Ruby hash
void ReadBack(); // from auto4 base
};

View file

@ -47,62 +47,46 @@ namespace Automation4 {
// RubyConfigDialogControl
RubyConfigDialogControl::RubyConfigDialogControl()
RubyConfigDialogControl::RubyConfigDialogControl(VALUE opts)
{
// Assume top of stack is a control table (don't do checking)
VALUE val = rb_hash_aref(opts, rb_str_new2("name"));
if(TYPE(val) == T_STRING)
name = wxString(StringValueCStr(val), wxConvUTF8);
else name = _T("");
/* 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);
val = rb_hash_aref(opts, rb_str_new2("x"));
if(TYPE(val) == T_FIXNUM) {
x = FIX2INT(val);
if (x < 0) x = 0;
} else {
x = 0;
}
lua_pop(L, 1);
else x = 0;
lua_getfield(L, -1, "y");
if (lua_isnumber(L, -1)) {
y = lua_tointeger(L, -1);
val = rb_hash_aref(opts, rb_str_new2("y"));
if(TYPE(val) == T_FIXNUM) {
y = FIX2INT(val);
if (y < 0) y = 0;
} else {
y = 0;
}
lua_pop(L, 1);
else y = 0;
lua_getfield(L, -1, "width");
if (lua_isnumber(L, -1)) {
width = lua_tointeger(L, -1);
val = rb_hash_aref(opts, rb_str_new2("width"));
if(TYPE(val) == T_FIXNUM) {
width = FIX2INT(val);
if (width < 1) width = 1;
} else {
width = 1;
}
lua_pop(L, 1);
else width = 1;
lua_getfield(L, -1, "height");
if (lua_isnumber(L, -1)) {
height = lua_tointeger(L, -1);
if (height < 1) height = 1;
} else {
height = 1;
val = rb_hash_aref(opts, rb_str_new2("height"));
if(TYPE(val) == T_FIXNUM) {
height = FIX2INT(val);
if (height < 1) width = 1;
}
lua_pop(L, 1);
else height = 1;
val = rb_hash_aref(opts, rb_str_new2("hint"));
if(TYPE(val) == T_STRING)
hint = wxString(StringValueCStr(val), wxConvUTF8);
else hint = _T("");
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());
}
@ -114,13 +98,15 @@ namespace Automation4 {
public:
wxString label;
Label()
: RubyConfigDialogControl()
Label(){};
Label(VALUE opts)
: RubyConfigDialogControl(opts)
{
/* lua_getfield(L, -1, "label");
label = wxString(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
*/ }
VALUE val = rb_hash_aref(opts, rb_str_new2("label"));
if(TYPE(val) == T_STRING)
label = wxString(StringValueCStr(val), wxConvUTF8);
else label = _T("");
}
virtual ~Label() { }
@ -134,10 +120,9 @@ namespace Automation4 {
// Nothing here
}
void RubyReadBack()
VALUE RubyReadBack()
{
// Label doesn't produce output, so let it be nil
// lua_pushnil(L);
return Qnil;
}
};
@ -148,13 +133,15 @@ namespace Automation4 {
public:
wxString text;
Edit()
: RubyConfigDialogControl()
Edit(){};
Edit(VALUE opts)
: RubyConfigDialogControl(opts)
{
/* lua_getfield(L, -1, "text");
text = wxString(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
*/ }
VALUE val = rb_hash_aref(opts, rb_str_new2("text"));
if(TYPE(val) == T_STRING)
text = wxString(StringValueCStr(val), wxConvUTF8);
else text = _T("");
}
virtual ~Edit() { }
@ -168,9 +155,9 @@ namespace Automation4 {
text = ((wxTextCtrl*)cw)->GetValue();
}
void RubyReadBack()
VALUE RubyReadBack()
{
// lua_pushstring(L, text.mb_str(wxConvUTF8));
return rb_str_new2(text.mb_str(wxConvUTF8));
}
};
@ -181,8 +168,9 @@ namespace Automation4 {
class Textbox : public Edit {
public:
Textbox()
: Edit()
Textbox(){};
Textbox(VALUE opts)
: Edit(opts)
{
// Nothing more
}
@ -207,33 +195,28 @@ namespace Automation4 {
bool hasspin;
int min, max;
IntEdit()
: Edit()
IntEdit(){};
IntEdit(VALUE opts)
: Edit(opts)
{
/* lua_getfield(L, -1, "value");
value = lua_tointeger(L, -1);
lua_pop(L, 1);
VALUE val = rb_hash_aref(opts, rb_str_new2("value"));
if(TYPE(val) == T_FIXNUM) {
value = FIX2INT(val);
}
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);
val = rb_hash_aref(opts, rb_str_new2("min"));
if(TYPE(val) == T_FIXNUM) {
min = FIX2INT(val);
}
else return;
val = rb_hash_aref(opts, rb_str_new2("min"));
if(TYPE(val) == T_FIXNUM) {
min = FIX2INT(val);
hasspin = true;
}
}
*/ }
virtual ~IntEdit() { }
@ -260,9 +243,9 @@ nospin:
}
}
void RubyReadBack()
VALUE RubyReadBack()
{
// lua_pushinteger(L, value);
return INT2FIX(value);
}
};
@ -275,13 +258,16 @@ nospin:
float value;
// FIXME: Can't support spin button atm
FloatEdit()
: Edit()
FloatEdit(){};
FloatEdit(VALUE opts)
: Edit(opts)
{
/* lua_getfield(L, -1, "value");
value = lua_tointeger(L, -1);
lua_pop(L, 1);
*/
VALUE val = rb_hash_aref(opts, rb_str_new2("value"));
if(TYPE(val) == T_FLOAT) {
value = NUM2DBL(val);
} else if (TYPE(val) == T_FIXNUM) {
value = FIX2INT(val);
}
// TODO: spin button support
}
@ -302,9 +288,9 @@ nospin:
}
}
void RubyReadBack()
VALUE RubyReadBack()
{
// lua_pushnumber(L, value);
return rb_float_new(value);
}
};
@ -317,23 +303,26 @@ nospin:
wxArrayString items;
wxString value;
Dropdown()
: RubyConfigDialogControl()
Dropdown(){};
Dropdown(VALUE opts)
: RubyConfigDialogControl(opts)
{
/* lua_getfield(L, -1, "value");
value = wxString(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
VALUE val = rb_hash_aref(opts, rb_str_new2("value"));
if(TYPE(val) == T_STRING)
value = wxString(StringValueCStr(val), wxConvUTF8);
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));
val = rb_hash_aref(opts, rb_str_new2("items"));
if(TYPE(val) == T_ARRAY)
{
long len = RARRAY(val)->len;
VALUE *ptr = RARRAY(val)->ptr;
for(int i = 0; i < len; i++)
{
if(TYPE(ptr[i]) == T_STRING)
items.Add(wxString(StringValueCStr(ptr[i]), wxConvUTF8));
}
}
lua_pop(L, 1);
}
lua_pop(L, 1);
*/ }
virtual ~Dropdown() { }
@ -347,11 +336,10 @@ nospin:
value = ((wxComboBox*)cw)->GetValue();
}
void RubyReadBack()
VALUE RubyReadBack()
{
// lua_pushstring(L, value.mb_str(wxConvUTF8));
return rb_str_new2(value.mb_str(wxConvUTF8));
}
};
@ -362,17 +350,18 @@ nospin:
wxString label;
bool value;
Checkbox()
: RubyConfigDialogControl()
Checkbox(){};
Checkbox(VALUE opts)
: RubyConfigDialogControl(opts)
{
/* lua_getfield(L, -1, "label");
label = wxString(lua_tostring(L, -1), wxConvUTF8);
lua_pop(L, 1);
VALUE val = rb_hash_aref(opts, rb_str_new2("label"));
if(TYPE(val) == T_STRING)
label = wxString(StringValueCStr(val), wxConvUTF8);
lua_getfield(L, -1, "value");
value = lua_toboolean(L, -1) != 0;
lua_pop(L, 1);
*/ }
val = rb_hash_aref(opts, rb_str_new2("value"));
if(val == Qtrue) value = true;
else value = false;
}
virtual ~Checkbox() { }
@ -386,9 +375,10 @@ nospin:
value = ((wxCheckBox*)cw)->GetValue();
}
void RubyReadBack()
VALUE RubyReadBack()
{
// lua_pushboolean(L, value);
if(value) return Qtrue;
return Qfalse;
}
};
@ -398,91 +388,77 @@ nospin:
// RubyConfigDialog
RubyConfigDialog::RubyConfigDialog(bool include_buttons)
RubyConfigDialog::RubyConfigDialog(VALUE config, VALUE btn_data, 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);
if(include_buttons && TYPE(btn_data) == T_ARRAY)
{
long len = RARRAY(config)->len;
VALUE *ptr = RARRAY(config)->ptr;
for(int i = 0; i < len; i++)
{
if(TYPE(ptr[i]) == T_STRING)
{
wxString s(StringValueCStr(ptr[i]), 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);
if(TYPE(config) != T_ARRAY) {
if(rb_respond_to(config, rb_intern("to_ary")))
config = rb_funcall(config, rb_intern("to_ary"), 0);
else throw "Cannot create config dialog from something non-table";
}
// 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);
long len = RARRAY(config)->len;
VALUE *ptr = RARRAY(config)->ptr;
for(int i = 0; i < len; i++)
{
if(TYPE(ptr[i]) != T_HASH)
continue; // skip invalid entry
VALUE ctrlclass = rb_hash_aref(ptr[i], rb_str_new2("class"));
if (TYPE(ctrlclass) != T_STRING)
continue; // skip
wxString controlclass(StringValueCStr(ctrlclass), 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);
ctl = new RubyControl::Label(ptr[i]);
} else if (controlclass == _T("edit")) {
ctl = new RubyControl::Edit(L);
ctl = new RubyControl::Edit(ptr[i]);
} else if (controlclass == _T("intedit")) {
ctl = new RubyControl::IntEdit(L);
ctl = new RubyControl::IntEdit(ptr[i]);
} else if (controlclass == _T("floatedit")) {
ctl = new RubyControl::FloatEdit(L);
ctl = new RubyControl::FloatEdit(ptr[i]);
} else if (controlclass == _T("textbox")) {
ctl = new RubyControl::Textbox(L);
ctl = new RubyControl::Textbox(ptr[i]);
} else if (controlclass == _T("dropdown")) {
ctl = new RubyControl::Dropdown(L);
ctl = new RubyControl::Dropdown(ptr[i]);
} else if (controlclass == _T("checkbox")) {
ctl = new RubyControl::Checkbox(L);
ctl = new RubyControl::Checkbox(ptr[i]);
} else if (controlclass == _T("color")) {
// FIXME
ctl = new RubyControl::Edit(L);
ctl = new RubyControl::Edit(ptr[i]);
} else if (controlclass == _T("coloralpha")) {
// FIXME
ctl = new RubyControl::Edit(L);
ctl = new RubyControl::Edit(ptr[i]);
} else if (controlclass == _T("alpha")) {
// FIXME
ctl = new RubyControl::Edit(L);
} else {
goto badcontrol;
}
ctl = new RubyControl::Edit(ptr[i]);
} else continue; // skip
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()
{
@ -537,42 +513,39 @@ badcontrol:
return w;
}
int RubyConfigDialog::RubyReadBack()
VALUE RubyConfigDialog::RubyReadBack()
{
// First read back which button was pressed, if any
VALUE cfg = rb_hash_new();
for (size_t i = 0; i < controls.size(); ++i) {
rb_hash_aset(cfg, rb_str_new2(controls[i]->name.mb_str(wxConvUTF8)), controls[i]->RubyReadBack());
}
if (use_buttons) {
VALUE res = rb_ary_new();
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);
rb_ary_push(res, Qfalse);
} 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));
rb_ary_push(res, rb_str_new2(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);
rb_ary_push(res, Qtrue);
}
}
rb_ary_push(res, cfg); // return array [button, hash with config]
return res;
}
// 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;
}
return cfg; // if no buttons return only hash with config
}
void RubyConfigDialog::ReadBack()