Files
2018-07-17 14:15:37 +02:00

323 lines
7.2 KiB
ActionScript

import elements.GuiMarkupText;
import elements.GuiListbox;
import editor.fields;
import dialogs.Dialog;
import void fillTargetsFromTab(Field@ sourceField, TargetChooser@ fld) from "editor.editor";
class HookEditor : Dialog {
HookField@ field;
string typeName;
Hook@ type;
GuiButton@ ok;
string suffix;
GuiMarkupText@ text;
array<Field@> args;
HookEditor(HookField@ field) {
@this.field = field;
@type = field.type;
super(null);
width = 1000;
addTitle(getClass(field.type).name);
@text = GuiMarkupText(bg, recti_area(12,32,width-24,30));
@ok = GuiButton(bg, recti());
ok.text = locale::OK;
if(isWindows) {
//Legacy OS support
@ok.alignment = Alignment(Left+0.4f+DIALOG_PADDING, Bottom-DIALOG_BUTTON_ROW, Right-0.4f-DIALOG_PADDING, Bottom-DIALOG_PADDING);
}
else {
@ok.alignment = Alignment(Right-0.2f-DIALOG_PADDING, Bottom-DIALOG_BUTTON_ROW, Right-0.0f-DIALOG_PADDING, Bottom-DIALOG_PADDING);
}
update();
}
void focus() {
if(args.length != 0)
args[0].focus();
}
void update() {
int y = 32;
string doc = type.formatDeclaration();
if(type.documentation !is null)
doc += "\n[offset=20][i]"+type.documentation.text+"[/i][/offset]";
text.text = doc;
text.updateAbsolutePosition();
y += text.size.height+12;
array<string> argValues;
array<string> argSpecs;
argValues.length = type.arguments.length;
string hookText = field.value;
if(funcSplit(hookText, typeName, argSpecs)) {
for(uint i = 0, cnt = argSpecs.length; i < cnt; ++i) {
if(i >= argValues.length) {
if(argValues.length != 0)
argValues[argValues.length - 1] += ","+argSpecs[i].trimmed();
continue;
}
//Check for named arguments
int eqPos = argSpecs[i].findFirst("=");
int brkPos = argSpecs[i].findFirst("(");
if(eqPos != -1 && (brkPos == -1 || eqPos < brkPos)) {
//Named argument
uint index = uint(-1);
string name = argSpecs[i].substr(0, eqPos).trimmed();
string value = argSpecs[i].substr(eqPos+1).trimmed();
for(uint n = 0, ncnt = argValues.length; n < ncnt; ++n) {
if(type.arguments[n].argName.equals_nocase(name)) {
index = n;
break;
}
}
if(index != uint(-1))
argValues[index] = value;
}
else {
argValues[i] = argSpecs[i].trimmed();
}
}
int brktPos = hookText.findLast(")");
if(brktPos != -1)
suffix = hookText.substr(brktPos+1);
}
for(uint i = 0, cnt = type.arguments.length; i < cnt; ++i) {
auto@ arg = type.arguments[i];
auto atype = arg.type;
if(atype == AT_VarArgs)
atype = ArgumentType(arg.integer);
auto@ fld = makeField(atype, bg, recti());
if(atype == AT_Target) {
fld.setValue(arg.integer2);
fillTargetsFromTab(field, cast<TargetChooser>(fld));
}
fld.setVerbose(true);
fld.set(arg.argName, arg.doc, arg.filled ? arg.str : "");
if(argValues[i].length != 0)
fld.value = argValues[i];
else
fld.value = fld.defaultValue;
fld.tabIndex = i;
fld.updateAbsolutePosition();
int h = fld.needHeight;
fld.size = vec2i(width-24, h);
fld.position = vec2i(12, y);
y += h;
args.insertLast(fld);
}
if(args.length != 0)
args[0].focus();
y += 40;
height = y;
updatePosition();
bg.updateAbsolutePosition();
}
void confirm() {
string spec = typeName+"(";
bool skipped = false, filled = false;
for(uint i = 0, cnt = type.arguments.length; i < cnt; ++i) {
auto@ arg = type.arguments[i];
string value = args[i].value;
if(arg.filled && value == arg.str) {
skipped = true;
continue;
}
if(filled)
spec += ", ";
if(skipped || (arg.filled && arg.type != AT_Hook))
spec += arg.argName+" = ";
spec += value;
filled = true;
}
spec += ")";
spec += suffix;
field.value = spec;
field.emitConfirmed();
}
bool onGuiEvent(const GuiEvent& event) {
if(event.type == GUI_Clicked && event.caller is ok && !Closed) {
confirm();
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
class HookSorter {
string name;
string text;
AnyClass@ cls;
Hook@ hook;
int opCmp(const HookSorter@ other) const {
return name.opCmp(other.name);
}
};
class HookList : Dialog {
HookField@ field;
Hook@ type;
GuiButton@ accept;
GuiButton@ cancel;
GuiListbox@ hookList;
array<HookSorter@> hooks;
HookList(HookField@ field) {
@this.field = field;
@type = field.type;
super(null);
width = 800;
addTitle("Add Hook");
@hookList = GuiListbox(bg, Alignment(Left+12, Top+32, Right-12, Bottom-40));
hookList.itemHeight = 70;
@accept = GuiButton(bg, recti());
accept.text = locale::ACCEPT;
accept.tabIndex = 100;
@accept.callback = this;
accept.color = colors::Green;
@cancel = GuiButton(bg, recti());
cancel.text = locale::CANCEL;
cancel.tabIndex = 101;
@cancel.callback = this;
cancel.color = colors::Red;
alignAcceptButtons(accept, cancel);
height = 600;
update();
setGuiFocus(hookList);
}
void update() {
AnyClass@ intf = getClass(field.hookType);
array<AnyClass@> classes;
getClassesImplementing(classes, intf);
set_int types;
for(uint i = 0, cnt = classes.length; i < cnt; ++i) {
auto@ cls = classes[i];
if(types.contains(cls.id))
continue;
types.insert(cls.id);
Hook@ hook = cast<Hook>(cls.create());
if(hook is null)
continue;
hook.initClass();
if(hook.documentation !is null && hook.documentation.hidden)
continue;
string txt = "[b]"+hook.formatDeclaration()+"[/b]";
if(hook.documentation !is null) {
txt += "\n[offset=24][i][color=#aaa]"+hook.documentation.text+"[/color][/i][/offset]";
}
HookSorter s;
s.name = getClass(hook).name;
s.text = txt;
@s.cls = cls;
@s.hook = hook;
hooks.insertLast(s);
}
hooks.sortAsc();
for(uint i = 0, cnt = hooks.length; i < cnt; ++i) {
GuiMarkupListText elem(hooks[i].text);
elem.basicText = hooks[i].name;
hookList.addItem(elem);
}
}
bool confirmed = false;
void close() {
if(!confirmed && field.value == "")
field.resetButton.emitClicked();
Dialog::close();
}
void confirm() {
int selected = hookList.selected;
if(selected == -1)
return;
confirmed = true;
auto@ s = hooks[selected];
string hname = "";
if(getClass(field.hookModule+"::"+s.name) !is s.cls)
hname += s.cls.module.name+"::";
hname += s.name;
hname += "()";
field.value = hname;
if(s.hook.arguments.length != 0)
openHookEditor(field);
else
field.emitConfirmed();
}
bool onGuiEvent(const GuiEvent& event) {
if(event.type == GUI_Clicked && event.caller is accept && !Closed) {
confirm();
close();
return true;
}
if(event.type == GUI_Clicked && event.caller is cancel && !Closed) {
close();
return true;
}
if(event.type == GUI_Confirmed && event.caller is hookList && hookList.selected >= 0) {
confirm();
close();
return true;
}
return Dialog::onGuiEvent(event);
}
};
void openHookList(HookField@ field) {
HookList hl(field);
addDialog(hl);
setGuiFocus(hl.hookList);
}
void openHookList(HookField@ field, bool isStatic) {
HookList hl(field);
if(isStatic)
hl.confirmed = true;
addDialog(hl);
setGuiFocus(hl.hookList);
}
void openHookEditor(HookField@ field) {
if(field.type is null)
return;
HookEditor ed(field);
addDialog(ed);
ed.focus();
}