08fd851489
Change fonts; make use of the new feature to allow fonts to be specified using property from dialog xml. Ensure that all fonts are defined within the style.
428 lines
12 KiB
XML
428 lines
12 KiB
XML
<?xml version="1.0"?>
|
|
|
|
<PropertyList>
|
|
<name>chat-menu</name>
|
|
<x>10</x>
|
|
<y>-30</y>
|
|
<layout>vbox</layout>
|
|
<draggable>false</draggable>
|
|
<default-padding>1</default-padding>
|
|
<font>
|
|
<property>sim/gui/selected-style/fonts/gui-small</property>
|
|
</font>
|
|
<color>
|
|
<red>0</red>
|
|
<green>0</green>
|
|
<blue>0</blue>
|
|
<alpha>0</alpha>
|
|
</color>
|
|
<text>
|
|
<label>0. Back</label>
|
|
<halign>left</halign>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<label>1. Edit</label>
|
|
<halign>left</halign>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[0]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[1]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[2]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[3]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[4]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[5]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[6]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
<text>
|
|
<halign>left</halign>
|
|
<property>/sim/multiplay/chat-menu/entry[7]</property>
|
|
<live>true</live>
|
|
<color>
|
|
<red>0.9</red>
|
|
<green>0.2</green>
|
|
<blue>0.2</blue>
|
|
<alpha>1</alpha>
|
|
</color>
|
|
</text>
|
|
|
|
<nasal>
|
|
<open>
|
|
<![CDATA[
|
|
var text = "";
|
|
var topNode = "/sim/multiplay/chat-menu/config";
|
|
|
|
setprop("/sim/multiplay/chat-menu/entry", "1");
|
|
setprop("/sim/multiplay/chat-menu/entry[1]", "2");
|
|
setprop("/sim/multiplay/chat-menu/entry[2]", "3");
|
|
setprop("/sim/multiplay/chat-menu/entry[3]", "4");
|
|
setprop("/sim/multiplay/chat-menu/entry[4]", "5");
|
|
setprop("/sim/multiplay/chat-menu/entry[5]", "6");
|
|
setprop("/sim/multiplay/chat-menu/entry[6]", "7");
|
|
setprop("/sim/multiplay/chat-menu/entry[7]", "8");
|
|
setprop("/sim/multiplay/chat-menu/entry[8]", "9");
|
|
setprop("/sim/multiplay/chat-menu/entry[9]", "0");
|
|
|
|
# Get the conversation tree.
|
|
var entryNodes = props.globals.getNode("/sim/multiplay/chat-menu").getChildren("entry");
|
|
var pos = props.globals.getNode(topNode);
|
|
|
|
# Get various substitution values.
|
|
var callsign = getprop("/sim/multiplay/callsign");
|
|
|
|
# Simple rounding of altitude to 100 ft.
|
|
var altitude = int(getprop("/position/altitude-ft")/100) * 100;
|
|
|
|
var type = getprop("/sim/description");
|
|
if (type != "")
|
|
{
|
|
# Get the first word of the description, which will (hopefully) be
|
|
# something appropriate, such as "Cessna", "Boeing".
|
|
type = split(" ", type)[0];
|
|
}
|
|
|
|
# Get the nearest airport.
|
|
var airport = airportinfo();
|
|
|
|
# Determine the active runway. We have two ways to do this:
|
|
# - If the aircraft is on the ground (or very close to it), we'll try to determine
|
|
# the runway it is closest to, and facing the correct direction for.
|
|
# - If the aircraft is in the air, we'll work out the active runway based on the wind.
|
|
|
|
var active_runway = "";
|
|
var on_ground = (getprop("/position/altitude-agl-ft") < 100);
|
|
|
|
if (on_ground)
|
|
{
|
|
# To find out the closest runway to the aircrafts position, we'll look at the heading
|
|
# required to go from the aircraft's current position to the center of each runway.
|
|
# The closer this is to the runways real heading, the more likely this is the runway
|
|
# we're on. Note that we can't rely on /sim/atc/runway, as this is only set on
|
|
# initialization.
|
|
var max = 360;
|
|
var loc = geo.aircraft_position();
|
|
|
|
foreach (var r; keys(airport.runways)) {
|
|
var curr = airport.runways[r];
|
|
var p = geo.Coord.new();
|
|
p.set_latlon(curr.lat, curr.lon, airport.elevation);
|
|
|
|
var course = loc.course_to(p) - curr.heading;
|
|
while (course >= 180) course -= 360;
|
|
while (course < -180) course += 360;
|
|
|
|
var deviation = math.abs(course);
|
|
|
|
if (deviation < max)
|
|
{
|
|
active_runway = r;
|
|
max = deviation;
|
|
}
|
|
}
|
|
|
|
# It may be the case that we're taxiing down the runway, past the mid-point.
|
|
# If the aircraft is facing the "wrong" way for this runway, then take the
|
|
# reciprocal runway.
|
|
var heading = getprop("/orientation/heading-magnetic-deg");
|
|
var deviation = heading - airport.runways[active_runway].heading;
|
|
|
|
while (deviation >= 180) deviation -= 360;
|
|
while (deviation < -180) deviation += 360;
|
|
deviation = math.abs(deviation);
|
|
|
|
if (deviation > 90)
|
|
{
|
|
var number = math.mod(num(substr(active_runway, 0, 2)) + 18, 36);
|
|
var side = substr(active_runway, 2, 1);
|
|
active_runway = sprintf("%02d%s", number, side == "R" ? "L" : side == "L" ? "R" : side);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var wind_speed = getprop("/environment/wind-speed-kt");
|
|
var wind_from = wind_speed ? getprop("/environment/wind-from-heading-deg") : 270;
|
|
var max = -1;
|
|
|
|
foreach (var r; keys(airport.runways)) {
|
|
var curr = airport.runways[r];
|
|
|
|
var wind = wind_from - curr.heading;
|
|
while (wind >= 180) wind -= 360;
|
|
while (wind < -180) wind += 360;
|
|
|
|
var deviation = math.abs(wind) + 1e-20;
|
|
var v = (0.01 * curr.length + 0.01 * curr.width) / deviation;
|
|
|
|
if (v > max) {
|
|
max = v;
|
|
active_runway = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Find our distance and cardinal direction to the airport.
|
|
var directions = split(",", "North,North East,East,South East,South,South West,West,North West");
|
|
var loc = geo.aircraft_position();
|
|
var airportloc = geo.Coord.new();
|
|
airportloc.set_latlon(airport.lat, airport.lon, airport.elevation);
|
|
|
|
# We want the course _from_ the airport to the aircraft - for reporting ".. approaching from the SW"
|
|
var airport_course = airportloc.course_to(loc);
|
|
var airport_distance = int((airportloc.distance_to(loc) / 1609) + 0.5);
|
|
|
|
# Get an index into our array of directions.
|
|
var dir = int(math.mod((airport_course + 22.5), 360) / 45);
|
|
var airport_direction = directions[dir];
|
|
|
|
# Now launch the keyboard listener.
|
|
var kbdevent = setlistener("/devices/status/keyboard/event", func (event) {
|
|
|
|
# Only check the key when pressed.
|
|
if (!event.getNode("pressed").getValue())
|
|
return;
|
|
|
|
var key = event.getNode("key");
|
|
|
|
if (handle_key(key.getValue()))
|
|
key.setValue(-1); # drop key event
|
|
});
|
|
|
|
var handle_key = func (key) {
|
|
# We only handle keys 0-9 and Esc
|
|
|
|
if (key == 27)
|
|
{
|
|
# escape -> cancel
|
|
removelistener(kbdevent);
|
|
gui.popdown();
|
|
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
|
|
return 1;
|
|
}
|
|
|
|
if ((key < `0`) or (key > `9`))
|
|
{
|
|
# pass the event back.
|
|
return 0;
|
|
}
|
|
|
|
if (key == `0`)
|
|
{
|
|
# Go back one level.
|
|
text = "";
|
|
|
|
if (pos.getName() != "config")
|
|
{
|
|
# Build up the chat string again.
|
|
pos = pos.getParent();
|
|
var p = pos;
|
|
|
|
while ((p.getName() != "config") and
|
|
(p.getChild("name").getValue() != nil))
|
|
{
|
|
var t = string.trim(p.getChild("name").getValue());
|
|
|
|
# Entries that begin with "[" are silent.
|
|
if (t[0] != `[`)
|
|
{
|
|
text = t ~ " " ~ text;
|
|
}
|
|
|
|
p = p.getParent();
|
|
}
|
|
}
|
|
|
|
updateDialog();
|
|
}
|
|
|
|
if (key == `1`)
|
|
{
|
|
# Go to edit mode using the inline editor.
|
|
removelistener(kbdevent);
|
|
gui.popdown();
|
|
multiplayer.compose_message(text);
|
|
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
|
|
}
|
|
|
|
if ((key > `1`) and (key <= `9`))
|
|
{
|
|
# Select the appropriate new node and update.
|
|
# The index starts from position 2.
|
|
var i = key - `2`;
|
|
|
|
if (i > (size(pos.getChildren("menu")) -1))
|
|
{
|
|
# Drop out if the user has entered a too large value.
|
|
return 0;
|
|
}
|
|
|
|
var t = entryNodes[i].getValue();
|
|
|
|
t = string.trim(substr(t, 3, size(t) -3));
|
|
|
|
# Entries that begin with "[" are silent.
|
|
if (t[0] != `[`)
|
|
{
|
|
text = text ~ " " ~ t;
|
|
}
|
|
|
|
pos = pos.getChildren("menu")[i];
|
|
|
|
if (size(pos.getChildren("menu")) == 0)
|
|
{
|
|
# We've come to the end of the tree - send the message and close
|
|
setprop("/sim/multiplay/chat", text);
|
|
removelistener(kbdevent);
|
|
gui.popdown();
|
|
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
|
|
}
|
|
else
|
|
{
|
|
# We've got more tree to traverse.
|
|
updateDialog();
|
|
}
|
|
}
|
|
|
|
# If we got here, we consumed the event
|
|
return 1;
|
|
}
|
|
|
|
# Substitute simple values into the string.
|
|
# The values we handle are
|
|
#
|
|
# % - Type (first word of /sim/description)
|
|
# # - callsign
|
|
# $ - altitude
|
|
# * - airport ID.
|
|
# & - course from airport to aircraft - "North West"
|
|
# ! - distance from aircraft to airport in miles.
|
|
#
|
|
var subvals = func (str) {
|
|
var t = "";
|
|
|
|
for (var p = 0; p < size(str); p += 1)
|
|
{
|
|
if (str[p] == `!`) t ~= airport_direction;
|
|
elsif (str[p] == `^`) t ~= airport_distance;
|
|
elsif (str[p] == `(`) t ~= active_runway;
|
|
elsif (str[p] == `*`) t ~= airport.name;
|
|
elsif (str[p] == `%`) t ~= type;
|
|
elsif (str[p] == `#`) t ~= callsign;
|
|
elsif (str[p] == `$`) t ~= altitude;
|
|
else t ~= chr(str[p]);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
var updateDialog = func {
|
|
|
|
var children = pos.getChildren("menu");
|
|
var i = 0;
|
|
foreach(var c; children)
|
|
{
|
|
var p = i + 2;
|
|
var txt = p ~ ': ' ~ subvals(c.getChild("name").getValue());
|
|
|
|
if (i < 9)
|
|
{
|
|
entryNodes[i].setValue(txt);
|
|
}
|
|
i = i + 1;
|
|
}
|
|
|
|
# Set the rest of the dialog to blank.
|
|
while (i <= 9)
|
|
{
|
|
entryNodes[i].setValue("");
|
|
i = i + 1;
|
|
}
|
|
|
|
# Write the popup.
|
|
gui.popupTip(text, 1000000);
|
|
}
|
|
|
|
# Start by updating the dialog.
|
|
updateDialog();
|
|
]]>
|
|
</open>
|
|
</nasal>
|
|
</PropertyList>
|