7 # foo = screen.window.new()
8 # foo.write("message in the middle of the screen");
13 # bar = screen.window.new(nil, -100, 3, 10);
14 # bar.fg = [1, 1, 1, 1]; # choose white color
17 # bar.write("first line");
18 # bar.write("second line (red)", 1, 0, 0);
25 # positive coords position relative to the left/lower corner,
26 # negative coords from the right/upper corner, nil centers
27 # maxlines ... max number of displayed lines; if more are pushed into the
28 # screen, then the ones on top fall off
29 # autoscroll ... seconds that each line should be shown; can be less if
30 # a message falls off; if 0 then don't scroll at all
35 # convert string for output; replaces tabs by spaces, and skips
36 # delimiters and the voice part in "{text|voice}" constructions
38 var sanitize = func(s) {
41 for (var i = 0; i < size(s); i += 1) {
64 new : func(x = nil, y = nil, maxlines = 10, autoscroll = 10) {
65 var m = { parents : [window] };
70 m.maxlines = maxlines;
71 m.autoscroll = autoscroll; # display time in seconds
72 m.sticky = 0; # reopens on old place
74 m.bg = [0, 0, 0, 0]; # background color
75 m.fg = [0.9, 0.4, 0.2, 1]; # default foreground color
76 m.align = "center"; # "left", "right", "center"
79 m.name = "__screen_window_" ~ (dialog_id += 1) ~ "__";
83 m.namenode = props.Node.new({ "dialog-name" : m.name });
84 setlistener("/sim/startup/xsize", func { m._redraw_() });
85 setlistener("/sim/startup/ysize", func { m._redraw_() });
89 write : func(msg, r = nil, g = nil, b = nil, a = nil) {
90 if (me.namenode == nil) { return }
91 if (r == nil) { r = me.fg[0] }
92 if (g == nil) { g = me.fg[1] }
93 if (b == nil) { b = me.fg[2] }
94 if (a == nil) { a = me.fg[3] }
95 foreach (var line; split("\n", string.trim(msg))) {
96 line = sanitize(string.trim(line));
97 append(me.lines, [line, r, g, b, a]);
98 if (size(me.lines) > me.maxlines) {
99 me.lines = subvec(me.lines, 1);
105 settimer(func { me._timeout_() }, me.autoscroll, 1);
112 if (me.dialog != nil) {
116 me.dialog = gui.Widget.new();
117 me.dialog.set("name", me.name);
118 if (me.x != nil) { me.dialog.set("x", me.x) }
119 if (me.y != nil) { me.dialog.set("y", me.y) }
120 me.dialog.set("layout", "vbox");
121 me.dialog.set("default-padding", 2);
122 if (me.font != nil) {
123 me.dialog.setFont(me.font);
124 } elsif (theme_font != nil) {
125 me.dialog.setFont(theme_font);
127 me.dialog.setColor(me.bg[0], me.bg[1], me.bg[2], me.bg[3]);
129 foreach (var line; me.lines) {
130 var w = me.dialog.addChild("text");
131 w.set("halign", me.align);
132 w.set("label", line[0]);
133 w.setColor(line[1], line[2], line[3], line[4]);
136 fgcommand("dialog-new", me.dialog.prop());
137 fgcommand("dialog-show", me.namenode);
141 fgcommand("dialog-close", me.namenode);
142 if (me.dialog != nil and me.sticky) {
143 me.x = me.dialog.prop().getNode("lastx").getValue();
144 me.y = me.dialog.prop().getNode("lasty").getValue();
149 if (me.skiptimer > 0) {
153 if (size(me.lines) > 1) {
154 me.lines = subvec(me.lines, 1);
164 if (me.dialog != nil) {
175 _setlistener("/sim/signals/nasal-dir-initialized", func {
176 setlistener("/sim/gui/current-style", func {
177 var theme = getprop("/sim/gui/current-style");
178 theme_font = getprop("/sim/gui/style[" ~ theme ~ "]/fonts/message-display/name");
181 log = window.new(nil, -30, 10, 10);
182 log.sticky = 0; # don't turn on; makes scrolling up messages jump left and right
184 var b = "/sim/screen/";
185 setlistener(b ~ "black", func { log.write(cmdarg().getValue(), 0, 0, 0) });
186 setlistener(b ~ "white", func { log.write(cmdarg().getValue(), 1, 1, 1) });
187 setlistener(b ~ "red", func { log.write(cmdarg().getValue(), 0.8, 0, 0) });
188 setlistener(b ~ "green", func { log.write(cmdarg().getValue(), 0, 0.6, 0) });
189 setlistener(b ~ "blue", func { log.write(cmdarg().getValue(), 0, 0, 0.8) });
190 setlistener(b ~ "yellow", func { log.write(cmdarg().getValue(), 0.8, 0.8, 0) });
191 setlistener(b ~ "magenta", func { log.write(cmdarg().getValue(), 0.7, 0, 0.7) });
192 setlistener(b ~ "cyan", func { log.write(cmdarg().getValue(), 0, 0.6, 0.6) });
199 ##############################################################################
200 # functions that make use of the window class (and don't belong anywhere else)
201 ##############################################################################
204 var msg_repeat = func {
205 if (getprop("/sim/tutorials/running")) {
206 var last = getprop("/sim/tutorials/last-message");
210 setprop("/sim/messages/pilot", "Say again ...");
211 settimer(func { setprop("/sim/messages/copilot", last) }, 1.5);
214 var last = atc.getValue();
218 setprop("/sim/messages/pilot", "This is " ~ callsign.getValue() ~ ". Say again, over.");
220 atc.setValue(atclast.getValue());
231 _setlistener("/sim/signals/nasal-dir-initialized", func {
232 # set /sim/screen/nomap=true to prevent default message mapping
233 var nomap = getprop("/sim/screen/nomap");
234 if (nomap != nil and nomap) {
238 callsign = props.globals.getNode("/sim/user/callsign", 1);
239 atc = props.globals.getNode("/sim/messages/atc", 1);
240 atclast = props.globals.getNode("/sim/messages/atc-last", 1);
241 atclast.setValue("");
243 # map ATC messages to the screen log and to the voice subsystem
244 var map = func(type, msg, r, g, b) {
245 setprop("/sim/sound/voices/" ~ type, msg);
246 screen.log.write(msg, r, g, b);
247 printlog("info", "{", type, "} ", msg);
249 # save last ATC message for user callsign, unless this was already
250 # a repetition; insert "I say again" appropriately
252 var cs = callsign.getValue();
253 if (find(", I say again: ", atc.getValue()) < 0
254 and (var pos = find(cs, msg)) >= 0) {
255 var m = substr(msg, 0, pos + size(cs));
256 msg = substr(msg, pos + size(cs));
258 if ((pos = find("Tower, ", msg)) >= 0) {
259 m ~= substr(msg, 0, pos + 7);
260 msg = substr(msg, pos + 7);
264 m ~= "I say again: " ~ msg;
266 printlog("debug", "ATC_LAST_MESSAGE: ", m);
271 var m = "/sim/messages/";
272 listener["atc"] = setlistener(m ~ "atc",
273 func { map("atc", cmdarg().getValue(), 0.7, 1.0, 0.7) });
274 listener["approach"] = setlistener(m ~ "approach",
275 func { map("approach", cmdarg().getValue(), 0.7, 1.0, 0.7) });
276 listener["ground"] = setlistener(m ~ "ground",
277 func { map("ground", cmdarg().getValue(), 0.7, 1.0, 0.7) });
279 listener["pilot"] = setlistener(m ~ "pilot",
280 func { map("pilot", cmdarg().getValue(), 1.0, 0.8, 0.0) });
281 listener["copilot"] = setlistener(m ~ "copilot",
282 func { map("copilot", cmdarg().getValue(), 1.0, 1.0, 1.0) });
283 listener["ai-plane"] = setlistener(m ~ "ai-plane",
284 func { map("ai-plane", cmdarg().getValue(), 0.9, 0.4, 0.2) });