1
0
Fork 0
fgdata/Nasal/screen.nas
mfranz f827653668 - create new Nasal module string.nas with simple shell style pattern matching
algorithm (needed for the file selector and useful for other purposes, like
  assembling lists of livery or screenshot files etc.)
- io.nas: move fixpath to string.nas (it's not only useful for file paths
  but can also be used for property paths)
- screen.nas: move trim to string.nas (used by screen.nas and nasal-console.xml)
- gui.nas: add pattern matching to FileSelctor
- ufo.nas: use patterm matching (only *.ac and *.xml files shall be listed)
2007-06-22 14:13:30 +00:00

287 lines
7.7 KiB
Text

# screen log window
# =================
#
#
# simple use:
#
# foo = screen.window.new()
# foo.write("message in the middle of the screen");
#
#
# advanced use:
#
# bar = screen.window.new(nil, -100, 3, 10);
# bar.fg = [1, 1, 1, 1]; # choose white color
# bar.align = "left";
#
# bar.write("first line");
# bar.write("second line (red)", 1, 0, 0);
#
#
#
# arguments:
# x ... x coordinate
# y ... y coordinate
# positive coords position relative to the left/lower corner,
# negative coords from the right/upper corner, nil centers
# maxlines ... max number of displayed lines; if more are pushed into the
# screen, then the ones on top fall off
# autoscroll ... seconds that each line should be shown; can be less if
# a message falls off; if 0 then don't scroll at all
#
##
# convert string for output; replaces tabs by spaces, and skips
# delimitiers and the voice part in "{text|voice}" constructions
#
var sanitize = func(s) {
var r = "";
var skip = 0;
for (var i = 0; i < size(s); i += 1) {
var c = s[i];
if (c == `\t`) {
r ~= ' ';
} elsif (c == `{`) {
#
} elsif (c == `|`) {
skip = 1;
} elsif (c == `}`) {
skip = 0;
} elsif (!skip) {
r ~= chr(c);
}
}
return r;
}
var dialog_id = 0;
var theme_font = nil;
var window = {
new : func(x = nil, y = nil, maxlines = 10, autoscroll = 10) {
var m = { parents : [window] };
#
# "public"
m.x = x;
m.y = y;
m.maxlines = maxlines;
m.autoscroll = autoscroll; # display time in seconds
m.sticky = 0; # reopens on old place
m.font = nil;
m.bg = [0, 0, 0, 0]; # background color
m.fg = [0.9, 0.4, 0.2, 1]; # default foreground color
m.align = "center"; # "left", "right", "center"
#
# "private"
m.name = "__screen_window_" ~ (dialog_id += 1) ~ "__";
m.lines = [];
m.skiptimer = 0;
m.dialog = nil;
m.namenode = props.Node.new({ "dialog-name" : m.name });
setlistener("/sim/startup/xsize", func { m._redraw_() });
setlistener("/sim/startup/ysize", func { m._redraw_() });
return m;
},
write : func(msg, r = nil, g = nil, b = nil, a = nil) {
if (me.namenode == nil) { return }
if (r == nil) { r = me.fg[0] }
if (g == nil) { g = me.fg[1] }
if (b == nil) { b = me.fg[2] }
if (a == nil) { a = me.fg[3] }
foreach (var line; split("\n", string.trim(msg))) {
line = sanitize(string.trim(line));
append(me.lines, [line, r, g, b, a]);
if (size(me.lines) > me.maxlines) {
me.lines = subvec(me.lines, 1);
if (me.autoscroll) {
me.skiptimer += 1;
}
}
if (me.autoscroll) {
settimer(func { me._timeout_() }, me.autoscroll, 1);
}
}
me.show();
},
show : func {
if (me.dialog != nil) {
me.close();
}
me.dialog = gui.Widget.new();
me.dialog.set("name", me.name);
if (me.x != nil) { me.dialog.set("x", me.x) }
if (me.y != nil) { me.dialog.set("y", me.y) }
me.dialog.set("layout", "vbox");
me.dialog.set("default-padding", 2);
if (me.font != nil) {
me.dialog.setFont(me.font);
} elsif (theme_font != nil) {
me.dialog.setFont(theme_font);
}
me.dialog.setColor(me.bg[0], me.bg[1], me.bg[2], me.bg[3]);
foreach (var line; me.lines) {
var w = me.dialog.addChild("text");
w.set("halign", me.align);
w.set("label", line[0]);
w.setColor(line[1], line[2], line[3], line[4]);
}
fgcommand("dialog-new", me.dialog.prop());
fgcommand("dialog-show", me.namenode);
},
close : func {
fgcommand("dialog-close", me.namenode);
if (me.dialog != nil and me.sticky) {
me.x = me.dialog.prop().getNode("lastx").getValue();
me.y = me.dialog.prop().getNode("lasty").getValue();
}
},
_timeout_ : func {
if (me.skiptimer > 0) {
me.skiptimer -= 1;
return;
}
if (size(me.lines) > 1) {
me.lines = subvec(me.lines, 1);
me.show();
} else {
me.close();
dialog = nil;
me.lines = [];
}
},
_redraw_ : func {
if (me.dialog != nil) {
me.close();
me.show();
}
},
};
var log = nil;
_setlistener("/sim/signals/nasal-dir-initialized", func {
setlistener("/sim/gui/current-style", func {
var theme = getprop("/sim/gui/current-style");
theme_font = getprop("/sim/gui/style[" ~ theme ~ "]/fonts/message-display/name");
}, 1);
log = window.new(nil, -30, 10, 10);
log.sticky = 0; # don't turn on; makes scrolling up messages jump left and right
var b = "/sim/screen/";
setlistener(b ~ "black", func { log.write(cmdarg().getValue(), 0, 0, 0) });
setlistener(b ~ "white", func { log.write(cmdarg().getValue(), 1, 1, 1) });
setlistener(b ~ "red", func { log.write(cmdarg().getValue(), 0.8, 0, 0) });
setlistener(b ~ "green", func { log.write(cmdarg().getValue(), 0, 0.6, 0) });
setlistener(b ~ "blue", func { log.write(cmdarg().getValue(), 0, 0, 0.8) });
setlistener(b ~ "yellow", func { log.write(cmdarg().getValue(), 0.8, 0.8, 0) });
setlistener(b ~ "magenta", func { log.write(cmdarg().getValue(), 0.7, 0, 0.7) });
setlistener(b ~ "cyan", func { log.write(cmdarg().getValue(), 0, 0.6, 0.6) });
});
##############################################################################
# functions that make use of the window class (and don't belong anywhere else)
##############################################################################
var msg_repeat = func {
if (getprop("/sim/tutorials/running")) {
var last = getprop("/sim/tutorials/last-message");
if (last == nil) {
return;
}
setprop("/sim/messages/pilot", "Say again ...");
settimer(func { setprop("/sim/messages/copilot", last) }, 1.5);
} else {
var last = atc.getValue();
if (last == nil) {
return;
}
setprop("/sim/messages/pilot", "This is " ~ callsign.getValue() ~ ". Say again, over.");
settimer(func {
atc.setValue(atclast.getValue());
}, 6);
}
}
var atc = nil;
var callsign = nil;
var atclast = nil;
var listener = {};
_setlistener("/sim/signals/nasal-dir-initialized", func {
# set /sim/screen/nomap=true to prevent default message mapping
var nomap = getprop("/sim/screen/nomap");
if (nomap != nil and nomap) {
return;
}
callsign = props.globals.getNode("/sim/user/callsign", 1);
atc = props.globals.getNode("/sim/messages/atc", 1);
atclast = props.globals.getNode("/sim/messages/atc-last", 1);
atclast.setValue("");
# map ATC messages to the screen log and to the voice subsystem
var map = func(type, msg, r, g, b) {
setprop("/sim/sound/voices/" ~ type, msg);
screen.log.write(msg, r, g, b);
printlog("info", "{", type, "} ", msg);
# save last ATC message for user callsign, unless this was already
# a repetition; insert "I say again" appropriately
if (type == "atc") {
var cs = callsign.getValue();
if (find(", I say again: ", atc.getValue()) < 0
and (var pos = find(cs, msg)) >= 0) {
var m = substr(msg, 0, pos + size(cs));
msg = substr(msg, pos + size(cs));
if ((pos = find("Tower, ", msg)) >= 0) {
m ~= substr(msg, 0, pos + 7);
msg = substr(msg, pos + 7);
} else {
m ~= ", ";
}
m ~= "I say again: " ~ msg;
atclast.setValue(m);
printlog("debug", "ATC_LAST_MESSAGE: ", m);
}
}
}
var m = "/sim/messages/";
listener["atc"] = setlistener(m ~ "atc",
func { map("atc", cmdarg().getValue(), 0.7, 1.0, 0.7) });
listener["approach"] = setlistener(m ~ "approach",
func { map("approach", cmdarg().getValue(), 0.7, 1.0, 0.7) });
listener["ground"] = setlistener(m ~ "ground",
func { map("ground", cmdarg().getValue(), 0.7, 1.0, 0.7) });
listener["pilot"] = setlistener(m ~ "pilot",
func { map("pilot", cmdarg().getValue(), 1.0, 0.8, 0.0) });
listener["copilot"] = setlistener(m ~ "copilot",
func { map("copilot", cmdarg().getValue(), 1.0, 1.0, 1.0) });
listener["ai-plane"] = setlistener(m ~ "ai-plane",
func { map("ai-plane", cmdarg().getValue(), 0.9, 0.4, 0.2) });
});