diff --git a/gui/dialogs/nasal-console.xml b/gui/dialogs/nasal-console.xml
index 8d89c3cad..cc6273eed 100644
--- a/gui/dialogs/nasal-console.xml
+++ b/gui/dialogs/nasal-console.xml
@@ -6,81 +6,249 @@
nasal-console
vbox
-
-
-
+
+ hbox
+ 1
+
+
+
+
+
+ 1
+
+
+
+ editfield
fill
- 120
450
+ 200
20
true
- /sim/gui/dialogs/nasal-console/code
+ /sim/gui/dialogs/nasal-console/edit
+
+ dialog-apply
+ editfield
+
hbox
- 8
+ fill
+ 4
-
-
+
+ 1
fill
-
- 0.5
- 0.5
- 0.5
-
-
-
-
- hbox
- 0
-
-
-
-
-
-
-
+ 20
+ 20
+ 0
+ 1
+
+ ## /sim/gui/dialogs/nasal-console/tab-down[1] ##
+
+ dialog-apply
+ editfield
+
+
+ nasal
+
+
+
+ dialog-update
+ editfield
+
+
+
+
+
+
+ hbox
+ 4
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+ var dlg = props.globals.getNode("/sim/gui/dialogs/nasal-console");
+ var kbdctrl = props.globals.getNode("/devices/status/keyboard/ctrl", 1);
+ var printf = func { print(call(sprintf, arg)) }
+ var edit = dlg.getNode("edit", 1);
+ if (!contains(globals, "__nasal_console_locals")) {
+ globals["__nasal_console_locals"] = {};
+ }
+ var locals = globals["__nasal_console_locals"];
+ var numtabs = size(dlg.getChildren("code"));
+ if (!numtabs) {
+ numtabs = 10;
+ }
+
+ var dump = func {
+ rule = "--------------------------------------------------------------------------------";
+ print(rule ~ "\n");
+ print(edit.getValue());
+ print(rule);
+ }
+
+ var clear = func {
+ edit.setValue("");
+ }
+
+ var execute = func(what = nil) {
+ var tag = "<nasal-console>";
+ var err = [];
+ if (what == nil) {
+ what = edit;
+ }
+ var f = call(func { compile(what.getValue(), tag) }, nil, nil, nil, err);
+ if (size(err)) {
+ print(tag ~ ": " ~ err[0]);
+ return;
+ }
+ f = bind(f, globals);
+ call(f, nil, nil, locals, err);
+ if (size(err)) {
+ printf("%s at %s line %d\n", err[0], err[1], err[2]);
+ for (var i = 3; i < size(err); i += 2) {
+ printf(" called from %s line %d\n", err[i], err[i + 1]);
+ }
+ }
+ }
+
+ var tabs = cmdarg().getNode("group[1]");
+ var select = func(which, init = 0) {
+ if (active) { # false in help mode
+ dlg.getNode("active").setIntValue(active);
+ if (!init) {
+ dlg.getChild("code", active).setValue(screen.trim(edit.getValue()));
+ }
+ }
+ if (kbdctrl.getValue()) {
+ execute(dlg.getChild("code", which));
+ return;
+ }
+ active = which;
+ foreach (var c; dlg.getChildren("tab-down")) {
+ c.setBoolValue(c.getIndex() == active);
+ }
+ # animate tab buttons
+ dlg.getNode("active").setIntValue(active = which);
+ edit.setValue(dlg.getChild("code", active).getValue());
+ }
+
+ var help = func {
+ active = 0;
+ foreach (var c; dlg.getChildren("tab-down")) {
+ c.setBoolValue(0);
+ }
+ edit.setValue("Keys:\n"
+ ~ " tab ... leave edit mode (visible text cursor)\n"
+ ~ " return ... execute active code\n"
+ ~ " ctrl-c ... clear input field\n"
+ ~ " ctrl-p ... print input field contents to terminal\n"
+ ~ " escape ... close dialog\n\n"
+ ~ "Ctrl-click on tab buttons executes code without switching to the tab.\n"
+ ~ "Add more <code> properties in ~/.fgfs/autosave.xml for more tab buttons.");
+ }
+
+ # setup tab buttons and properties from the template
+ tabs.removeChildren("button");
+ var template = tabs.getNode("button-template");
+ var d = dlg.getPath();
+ for (var i = 1; i <= numtabs; i += 1) {
+ var button = tabs.getChild("button", i, 1);
+ var state = dlg.getChild("tab-down", i, 1);
+ state.setBoolValue(0);
+
+ props.copy(template, button);
+ button.getNode("hide").setValue(0);
+ button.getNode("legend").setIntValue(i);
+ button.getNode("binding[1]/script").setValue("select(" ~ i ~ ")");
+ button.getNode("property").setValue(state.getPath());
+ var c = dlg.getChild("code", i);
+ if (c == nil or c.getType() == "NONE") {
+ c = dlg.getChild("code", i, 1);
+ c.setValue("");
+ c.setAttribute("userarchive", 1);
+ }
+ }
+
+ edit.setValue("");
+ var active = dlg.getNode("active", 1).getValue();
+ if (active == nil) {
+ active = 1;
+ }
+ select(active, 1);
+
+