2008-10-07 10:44:45 +00:00
|
|
|
var translate = { 356: '<', 357: '^', 358: '>', 359: '_' };
|
2008-09-28 20:28:28 +00:00
|
|
|
var listener = nil;
|
2008-10-02 00:59:52 +00:00
|
|
|
var dialog = nil;
|
2008-10-07 10:44:45 +00:00
|
|
|
var data = nil;
|
|
|
|
var cmd = nil;
|
2008-10-02 00:59:52 +00:00
|
|
|
var menu = 0;
|
2008-09-28 20:28:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
var start = func {
|
2008-10-02 00:59:52 +00:00
|
|
|
cmd = "";
|
|
|
|
dialog = Dialog.new();
|
|
|
|
handle_key(8);
|
2008-09-28 20:28:28 +00:00
|
|
|
listener = setlistener("/devices/status/keyboard/event", func(event) {
|
|
|
|
var key = event.getNode("key");
|
2008-09-29 13:30:42 +00:00
|
|
|
if (!event.getNode("pressed").getValue()) {
|
2008-09-30 20:41:06 +00:00
|
|
|
if (cmd == "" and key.getValue() == `;`) # FIXME hack around kbd bug
|
2008-09-29 22:39:45 +00:00
|
|
|
key.setValue(`:`);
|
2008-09-29 13:30:42 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-09-28 20:28:28 +00:00
|
|
|
if (handle_key(key.getValue()))
|
|
|
|
key.setValue(-1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var stop = func {
|
|
|
|
removelistener(listener);
|
2008-09-29 10:06:16 +00:00
|
|
|
listener = nil;
|
2008-10-07 10:44:45 +00:00
|
|
|
dialog.del();
|
2008-09-28 20:28:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var handle_key = func(key) {
|
|
|
|
var mode = 0;
|
|
|
|
if (key == 27) {
|
|
|
|
stop();
|
|
|
|
return 1;
|
|
|
|
} elsif (key == 8) {
|
|
|
|
cmd = substr(cmd, 0, size(cmd) - 1);
|
2008-10-07 10:44:45 +00:00
|
|
|
} elsif (key == `\t`) {
|
2008-10-02 00:59:52 +00:00
|
|
|
menu = !menu;
|
2008-09-28 20:28:28 +00:00
|
|
|
} elsif (key == `\n` or key == `\r`) {
|
|
|
|
mode = 2;
|
2008-09-29 10:06:16 +00:00
|
|
|
} elsif (contains(translate, key)) {
|
|
|
|
cmd ~= translate[key];
|
2008-09-28 20:28:28 +00:00
|
|
|
} elsif (!string.isprint(key)) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
cmd ~= chr(key);
|
|
|
|
}
|
|
|
|
|
2008-10-03 09:47:40 +00:00
|
|
|
var options = [];
|
2008-09-28 20:28:28 +00:00
|
|
|
var bindings = [];
|
2008-10-02 00:59:52 +00:00
|
|
|
var desc = __multikey._ = nil;
|
|
|
|
if ((var node = find_entry(cmd, data, __multikey.arg = [])) != nil) {
|
|
|
|
desc = node.getNode("desc", 1).getValue() or "";
|
|
|
|
desc = call(sprintf, [desc] ~ __multikey.arg);
|
|
|
|
bindings = node.getChildren("binding");
|
|
|
|
if (node.getNode("no-exit") != nil) {
|
|
|
|
cmd = substr(cmd, 0, size(cmd) - 1);
|
|
|
|
mode = 1;
|
|
|
|
} elsif (node.getNode("exit") != nil) {
|
|
|
|
mode = 2;
|
2008-09-28 20:28:28 +00:00
|
|
|
}
|
2008-10-07 10:44:45 +00:00
|
|
|
if (menu)
|
|
|
|
foreach (var c; node.getChildren("key"))
|
|
|
|
if (size(c.getChildren("binding")) or size(c.getChildren("key")))
|
|
|
|
append(options, c);
|
2008-09-28 20:28:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mode and size(bindings)) {
|
|
|
|
foreach (var b; bindings)
|
|
|
|
props.runBinding(b, "__multikey");
|
|
|
|
if (mode == 2)
|
|
|
|
stop();
|
|
|
|
}
|
|
|
|
if (mode < 2)
|
2008-10-07 10:44:45 +00:00
|
|
|
dialog.update(cmd, __multikey._ or desc, options);
|
2008-09-28 20:28:28 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-02 00:59:52 +00:00
|
|
|
var Dialog = {
|
|
|
|
new : func {
|
|
|
|
var m = { parents: [Dialog] };
|
|
|
|
m.name = "multikey";
|
|
|
|
m.prop = props.Node.new({ "dialog-name": m.name });
|
|
|
|
m.isopen = 0;
|
2008-10-02 11:37:17 +00:00
|
|
|
m.firstrun = 1;
|
2008-10-02 00:59:52 +00:00
|
|
|
return m;
|
|
|
|
},
|
|
|
|
del : func {
|
|
|
|
me.isopen and fgcommand("dialog-close", me.prop);
|
|
|
|
me.isopen = 0;
|
|
|
|
},
|
|
|
|
update : func(cmd, title, options) {
|
|
|
|
var dlg = gui.Widget.new();
|
|
|
|
dlg.set("name", me.name);
|
|
|
|
dlg.set("y", -80);
|
|
|
|
dlg.set("layout", "vbox");
|
2008-10-05 16:13:13 +00:00
|
|
|
dlg.set("default-padding", 2);
|
|
|
|
|
|
|
|
# title/description
|
2008-10-02 00:59:52 +00:00
|
|
|
var t = dlg.addChild("text");
|
2008-10-05 16:13:13 +00:00
|
|
|
if (!size(cmd)) {
|
|
|
|
t.set("label", " Command Mode ");
|
|
|
|
t.setColor(1, 0.7, 0);
|
|
|
|
} elsif (title) {
|
|
|
|
t.set("label", " " ~ title ~ " ");
|
|
|
|
t.setColor(0.7, 1, 0.7);
|
|
|
|
} else {
|
|
|
|
t.set("label", " <unknown> ");
|
|
|
|
t.setColor(1, 0.4, 0.4);
|
|
|
|
}
|
|
|
|
|
|
|
|
# typed command
|
2008-10-02 11:37:17 +00:00
|
|
|
var t = dlg.addChild("text");
|
|
|
|
if (me.firstrun) {
|
|
|
|
me.firstrun = 0;
|
2008-10-05 16:13:13 +00:00
|
|
|
cmd = " Use <Tab> to toggle options! ";
|
2008-10-02 11:37:17 +00:00
|
|
|
t.setColor(0.5, 0.5, 0.5);
|
|
|
|
}
|
|
|
|
t.set("label", cmd);
|
2008-10-02 00:59:52 +00:00
|
|
|
|
2008-10-05 16:13:13 +00:00
|
|
|
# option menu
|
2008-10-07 10:44:45 +00:00
|
|
|
if (var numopt = size(options)) {
|
2008-10-02 00:59:52 +00:00
|
|
|
dlg.addChild("hrule");
|
|
|
|
var g = dlg.addChild("group");
|
|
|
|
g.set("layout", "table");
|
2008-10-02 11:37:17 +00:00
|
|
|
g.set("default-padding", 2);
|
2008-10-08 21:59:31 +00:00
|
|
|
var numrows = numopt / (1 + (numopt > 15) + (numopt > 30));
|
2008-10-02 00:59:52 +00:00
|
|
|
forindex (var i; options) {
|
2008-10-08 21:59:31 +00:00
|
|
|
var col = 3 * int(i / numrows);
|
|
|
|
var row = math.mod(i, numrows);
|
2008-10-07 10:44:45 +00:00
|
|
|
|
|
|
|
var desc = (options[i].getNode("desc", 1).getValue() or "") ~ " ";
|
|
|
|
var name = " " ~ options[i].getNode("name", 1).getValue();
|
2008-10-04 22:43:24 +00:00
|
|
|
name = string.replace(name, "%%", "%");
|
2008-10-07 10:44:45 +00:00
|
|
|
|
|
|
|
var o = g.addChild("text");
|
|
|
|
o.set("label", name);
|
|
|
|
o.set("row", row);
|
|
|
|
o.set("col", col);
|
|
|
|
var o = g.addChild("text");
|
|
|
|
o.set("label", " ... ");
|
|
|
|
o.set("row", row);
|
|
|
|
o.set("col", col + 1);
|
|
|
|
var o = g.addChild("text");
|
|
|
|
o.set("label", desc);
|
|
|
|
o.set("row", row);
|
|
|
|
o.set("col", col + 2);
|
|
|
|
o.set("halign", "left");
|
2008-10-02 00:59:52 +00:00
|
|
|
}
|
|
|
|
}
|
2008-10-05 16:13:13 +00:00
|
|
|
me.del();
|
2008-10-02 00:59:52 +00:00
|
|
|
fgcommand("dialog-new", dlg.prop());
|
|
|
|
fgcommand("dialog-show", me.prop);
|
|
|
|
me.isopen = 1;
|
|
|
|
},
|
|
|
|
};
|
2008-09-28 20:28:28 +00:00
|
|
|
|
|
|
|
|
2008-10-01 16:30:25 +00:00
|
|
|
var help = func {
|
2008-10-02 10:43:17 +00:00
|
|
|
var colorize = func(str) {
|
|
|
|
var s = "";
|
|
|
|
for (var i = 0; i < size(str); i += 1) {
|
|
|
|
var c = str[i];
|
|
|
|
if (c == `<` or c == `>` or c == `^` or c == `_`) {
|
2008-10-03 15:20:12 +00:00
|
|
|
s ~= string.color("35", chr(c));
|
2008-10-02 10:43:17 +00:00
|
|
|
} elsif (c == `%`) {
|
|
|
|
if ((i += 1) < size(str) and str[i] == `%`) {
|
|
|
|
s ~= '%';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var f = '%';
|
|
|
|
for (; i < size(str) and (c = str[i]) != nil and string.isdigit(c); i += 1)
|
|
|
|
f ~= chr(c);
|
|
|
|
if (c == `d`)
|
2008-10-03 15:20:12 +00:00
|
|
|
s ~= string.color("31", f ~ 'd');
|
2008-10-02 10:43:17 +00:00
|
|
|
elsif (c == `u`)
|
2008-10-03 15:20:12 +00:00
|
|
|
s ~= string.color("32", f ~ 'u');
|
2008-10-02 10:43:17 +00:00
|
|
|
elsif (c == `f`)
|
2008-10-03 15:20:12 +00:00
|
|
|
s ~= string.color("36", f ~ 'f');
|
2008-10-02 10:43:17 +00:00
|
|
|
elsif (c == `s`)
|
2008-10-03 15:20:12 +00:00
|
|
|
s ~= string.color("34", f ~ 's');
|
2008-10-02 10:43:17 +00:00
|
|
|
} else {
|
|
|
|
s ~= chr(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
var list = [];
|
|
|
|
var read_list = func(data) {
|
|
|
|
foreach (var c; data.children)
|
|
|
|
read_list(c);
|
|
|
|
if (size(data.format))
|
|
|
|
append(list, [data.format, data.node]);
|
|
|
|
}
|
|
|
|
read_list(data);
|
|
|
|
|
2008-10-01 16:30:25 +00:00
|
|
|
var (curr, title) = (0, "");
|
2008-10-02 10:43:17 +00:00
|
|
|
foreach (var k; sort(list, func(a, b) cmp(a[0], b[0]))) {
|
2008-10-01 16:30:25 +00:00
|
|
|
var bndg = k[1].getChildren("binding");
|
|
|
|
var desc = k[1].getNode("desc", 1).getValue() or "??";
|
|
|
|
if (size(k[0]) == 1 or k[0][0] == `%`)
|
|
|
|
title = desc;
|
2008-10-04 22:43:24 +00:00
|
|
|
if (!size(bndg) or size(bndg) == 1 and bndg[0].getNode("command", 1).getValue() == "null")
|
2008-10-01 16:30:25 +00:00
|
|
|
continue;
|
|
|
|
if (string.isalnum(k[0][0]) and k[0][0] != curr) {
|
|
|
|
curr = k[0][0];
|
2008-10-03 09:47:40 +00:00
|
|
|
var line = "---------------------------------------------------";
|
2008-10-03 15:20:12 +00:00
|
|
|
print(string.color("33", sprintf("\n-- %s %s", title, substr(line, size(title) + 2))));
|
2008-10-01 16:30:25 +00:00
|
|
|
}
|
2008-10-03 15:20:12 +00:00
|
|
|
if (k[1].getNode("no-exit") != nil)
|
2008-10-03 20:46:08 +00:00
|
|
|
desc ~= string.color("32", " +");
|
2008-10-03 15:20:12 +00:00
|
|
|
elsif (k[1].getNode("exit") != nil)
|
2008-10-03 20:46:08 +00:00
|
|
|
desc ~= string.color("31", " $");
|
2008-10-02 10:43:17 +00:00
|
|
|
printf("%s\t%s", colorize(k[0]), desc);
|
2008-10-01 16:30:25 +00:00
|
|
|
}
|
2008-10-03 15:20:12 +00:00
|
|
|
print(string.color("33", "\n-- Legend -------------------------------------------"));
|
2008-10-02 10:43:17 +00:00
|
|
|
printf("\t%s ... unsigned number", colorize("%u"));
|
|
|
|
printf("\t%s ... signed number", colorize("%d"));
|
|
|
|
printf("\t%s ... floating point number", colorize("%f"));
|
|
|
|
printf("\t%s ... string", colorize("%s"));
|
2008-10-02 15:19:47 +00:00
|
|
|
printf("\t%s ... < or cursor left", colorize("<"));
|
|
|
|
printf("\t%s ... > or cursor right", colorize(">"));
|
|
|
|
printf("\t%s ... ^ or cursor up", colorize("^"));
|
|
|
|
printf("\t%s ... _ or cursor down", colorize("_"));
|
2008-10-03 15:20:12 +00:00
|
|
|
printf("\t%s ... repeatable action", string.color("32", "+"));
|
|
|
|
printf("\t%s ... immediate action", string.color("31", "$"));
|
2008-10-01 16:30:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-02 00:59:52 +00:00
|
|
|
var find_entry = func(str, data, result) {
|
|
|
|
foreach (var c; data.children)
|
|
|
|
if ((var n = find_entry(str, c, result)) != nil)
|
|
|
|
return n;
|
|
|
|
if (string.scanf(str, data.format, var res = [])) {
|
|
|
|
foreach (var r; res)
|
|
|
|
append(result, r);
|
|
|
|
return data.node;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-09-28 20:28:28 +00:00
|
|
|
var init = func {
|
2008-09-29 22:39:45 +00:00
|
|
|
globals["__multikey"] = { _: };
|
2008-09-28 20:28:28 +00:00
|
|
|
var tree = props.globals.getNode("/input/keyboard/multikey", 1);
|
|
|
|
|
|
|
|
foreach (var n; tree.getChildren("nasal")) {
|
|
|
|
n.getNode("module", 1).setValue("__multikey");
|
|
|
|
fgcommand("nasal", n);
|
|
|
|
}
|
|
|
|
|
2008-10-02 00:59:52 +00:00
|
|
|
var scan = func(tree, format = "") {
|
|
|
|
var d = [];
|
2008-10-02 11:37:17 +00:00
|
|
|
foreach (var key; tree.getChildren("key"))
|
|
|
|
foreach (var name; key.getChildren("name"))
|
2008-10-02 10:43:17 +00:00
|
|
|
if ((var n = name.getValue()) != nil)
|
|
|
|
append(d, { format: format ~ n, node: key,
|
|
|
|
children: scan(key, format ~ n) });
|
2008-10-02 00:59:52 +00:00
|
|
|
return d;
|
2008-09-28 20:28:28 +00:00
|
|
|
}
|
|
|
|
|
2008-10-02 00:59:52 +00:00
|
|
|
data = { format: "", node: tree, children: scan(tree) };
|
2008-09-28 20:28:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_setlistener("/sim/signals/nasal-dir-initialized", init);
|
|
|
|
|
2008-09-29 22:39:45 +00:00
|
|
|
|